1. על מה מדובר?
בשיעור ה-Codelab המואר הזה תלמדו איך לשלוט בנר בלי להבות של PLAYBULB LED בעזרת JavaScript בלבד, בעזרת Web Bluetooth API. בדרך, אפשר גם לשחק עם תכונות של JavaScript ES2015 כמו כיתות, פונקציות חיצים, מפה והבטחות.
מה תלמדו
- איך לקיים אינטראקציה עם מכשיר Bluetooth בקרבת מקום ב-JavaScript
- איך משתמשים בכיתות ES2015, בפונקציות חיצים, במפה ובהבטחות
מה צריך להכין
- הבנה בסיסית של פיתוח אתרים
- ידע בסיסי ב-Bluetooth Low Energy (BLE) ובGeneral Attribute Profile (GATT)
- כלי לעריכת טקסט לבחירתכם
- מחשב Mac, מכשיר Chromebook או מכשיר Android M עם אפליקציית דפדפן Chrome וכבל USB מיקרו ל-USB.
2. הפעלה ראשונה
עליך לבדוק את הגרסה הסופית של האפליקציה שניהל עכשיו ליצור בכתובת https://googlecodelabs.github.io/candle-bluetooth ולשחק עם מכשיר PLAYBULB Candle Bluetooth שנמצא ברשותך לפני שממשיכים לתוך Codelab.
אפשר גם לצפות בי בזמן שינוי הצבעים בכתובת https://www.youtube.com/watch?v=fBCPA9gIxlU
3. להגדרה
להורדת הקוד לדוגמה
אפשר לקבל את הקוד לדוגמה של הקוד הזה על ידי הורדת המיקוד כאן:
או על ידי שכפול המאגר הזה של git:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
אם הורדתם את המקור כקובץ ZIP, פורקים אותו אמור ליצור תיקיית בסיס: candle-bluetooth-master
.
התקנה ואימות של שרת אינטרנט
אתם יכולים להשתמש בשרת האינטרנט משלכם, אבל ה-Codelab הזה מתוכנן לעבוד טוב עם שרת האינטרנט של Chrome. אם עדיין לא התקנתם את האפליקציה הזו, אתם יכולים להתקין אותה מחנות האינטרנט של Chrome.
לאחר התקנת האפליקציה 'שרת אינטרנט עבור Chrome', לוחצים על קיצור הדרך 'אפליקציות' בסרגל הסימניות:
בחלון שנפתח, לוחצים על הסמל של שרת האינטרנט:
בשלב הבא תראו את תיבת הדו-שיח הבאה, שבה תוכלו להגדיר את שרת האינטרנט המקומי:
לוחצים על הלחצן בחירת תיקייה ובוחרים את הרמה הבסיסית (root) של המאגר המשוכפל (או שאוחזר מהארכיון). כך תוכלו להציג את העבודה שביצעתם דרך כתובת ה-URL שמודגשת בתיבת הדו-שיח של שרת האינטרנט (בקטע כתובות URL של שרת האינטרנט).
בקטע 'אפשרויות', מסמנים את התיבה לצד 'הצגה אוטומטית של index.html', כפי שמוצג בהמשך:
עכשיו נכנסים לאתר בדפדפן האינטרנט (על ידי לחיצה על כתובת ה-URL המודגשת של שרת האינטרנט). אתם אמורים לראות דף שנראה כך:
כדי לראות איך האפליקציה הזו נראית בטלפון Android, צריך להפעיל ניפוי באגים מרחוק ב-Android ולהגדיר העברה ליציאה אחרת (כברירת מחדל, מספר היציאה הוא 8887). לאחר מכן, אפשר פשוט לפתוח כרטיסיית Chrome חדשה לכתובת http://localhost:8887 בטלפון Android.
הנושא הבא
בשלב זה אפליקציית האינטרנט הזו לא עושה הרבה. נתחיל להוסיף תמיכה ב-Bluetooth!
4. מגלים את הנר
נתחיל בכתיבת ספרייה שמשתמשת ב-JavaScript ES2015 Class במכשיר PLAYBULB Candle Bluetooth.
אל דאגה. תחביר המחלקה לא כולל מודל ירושה חדש מבוסס-אובייקטים ל-JavaScript. הוא פשוט מספק תחביר ברור יותר ליצירת אובייקטים ולהתמודדות עם ירושה, כפי שאפשר לקרוא בהמשך.
קודם נגדיר מחלקה PlaybulbCandle
ב-playbulbCandle.js
וניצור מכונה playbulbCandle
שתהיה זמינה בקובץ app.js
מאוחר יותר.
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
כדי לבקש גישה למכשיר Bluetooth בקרבת מקום, עלינו להתקשר למספר navigator.bluetooth.requestDevice
. מכיוון שמכשיר PLAYBULB Candle מפרסם באופן רציף (אם הוא עדיין לא הותאם), UUID של שירות Bluetooth GATT ידוע בצורתו הקצרה נקרא 0xFF02
, לכן אנחנו יכולים פשוט להגדיר קבוע ולהוסיף אותו לפרמטר של שירותי המסננים בשיטת connect
ציבורית חדשה מהמחלקה PlaybulbCandle
.
בנוסף, נעקוב אחרי האובייקט BluetoothDevice
באופן פנימי כדי שנוכל לגשת אליו מאוחר יותר במקרה הצורך. מכיוון ש-navigator.bluetooth.requestDevice
מחזיר JavaScript ES2015 Promise, נעשה זאת בשיטה then
.
playbulbCandle.js
(function() {
'use strict';
const CANDLE_SERVICE_UUID = 0xFF02;
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(function(device) {
this.device = device;
}.bind(this));
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
כאמצעי אבטחה, הקריאה של מכשירי Bluetooth בקרבת מקום עם navigator.bluetooth.requestDevice
חייבת להתבצע באמצעות תנועת משתמש, כמו מגע או לחיצת עכבר. לכן נקרא לשיטה connect
כשמשתמש ילחץ על 'קישור' הלחצן בקובץ app.js
:
app.js
document.querySelector('#connect').addEventListener('click', function(event) {
document.querySelector('#state').classList.add('connecting');
playbulbCandle.connect()
.then(function() {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
})
.catch(function(error) {
console.error('Argh!', error);
});
});
הפעלת האפליקציה
בשלב הזה, נכנסים לאתר בדפדפן האינטרנט (על ידי לחיצה על כתובת ה-URL של שרת האינטרנט המודגשת באפליקציה של שרת האינטרנט) או פשוט מרעננים את הדף הקיים. לוחצים על האפשרות הירוקה 'התחברות' לחצן, בוחרים את המכשיר בבורר ופותחים את קונסולת כלי הפיתוח המועדפת עליכם באמצעות מקשי הקיצור Ctrl + Shift + J ושימו לב שהאובייקט BluetoothDevice
נרשם ביומן.
אם ה-Bluetooth כבוי או אם מכשיר PLAYBULB Candle Bluetooth כבוי, תופיע הודעת שגיאה. במקרה כזה, צריך להפעיל אותו ולהמשיך שוב.
בונוס חובה
לא ידוע לי עליך, אבל אני כבר רואה יותר מדי function() {}
בקוד הזה. במקום זאת, נעבור ל-() => {}
JavaScript ES2015 פונקציות חיצים. הם מצילים לגמרי את החיים: כל היופי של פונקציות אנונימיות, בלי העצבות שבקישור.
playbulbCandle.js
(function() {
'use strict';
const CANDLE_SERVICE_UUID = 0xFF02;
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(device => {
this.device = device;
});
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
})
.catch(error => {
console.error('Argh!', error);
});
});
הנושא הבא
- אוקיי... אפשר לדבר עם הנר הזה או למה?
- בוודאי... דלגו לשלב הבא
שאלות נפוצות
5. רוצה לקרוא משהו?
אז מה עושים עכשיו, שהחזרתם BluetoothDevice
מההבטחה של navigator.bluetooth.requestDevice
? כדי להתחבר לשרת GATT המרוחק, שמכיל את שירות ה-Bluetooth ואת הגדרות המאפיינים, צריך להתקשר אל device.gatt.connect()
:
playbulbCandle.js
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(device => {
this.device = device;
return device.gatt.connect();
});
}
}
קריאת שם המכשיר
כאן אנחנו מחוברים לשרת GATT של מכשיר ה-Bluetooth PLAYBULB Candle. עכשיו אנחנו רוצים לקבל את שירות GATT הראשי (שפורסם בעבר בשם 0xFF02
) ולקרוא את מאפיין שם המכשיר (0xFFFF
) ששייך לשירות הזה. אפשר לעשות זאת בקלות על ידי הוספת שיטה חדשה getDeviceName
למחלקה PlaybulbCandle
ושימוש ב-device.gatt.getPrimaryService
וב-service.getCharacteristic
. השיטה characteristic.readValue
תחזיר בפועל את הערך DataView
, שאותו נפענח באמצעות TextDecoder
.
playbulbCandle.js
const CANDLE_DEVICE_NAME_UUID = 0xFFFF;
...
getDeviceName() {
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_DEVICE_NAME_UUID))
.then(characteristic => characteristic.readValue())
.then(data => {
let decoder = new TextDecoder('utf-8');
return decoder.decode(data);
});
}
כדי להוסיף את זה אל app.js
, פשוט מתקשרים אל playbulbCandle.getDeviceName
אחרי שנתחבר ונציג את שם המכשיר.
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
return playbulbCandle.getDeviceName().then(handleDeviceName);
})
.catch(error => {
console.error('Argh!', error);
});
});
function handleDeviceName(deviceName) {
document.querySelector('#deviceName').value = deviceName;
}
בשלב הזה, נכנסים לאתר בדפדפן האינטרנט (על ידי לחיצה על כתובת ה-URL של שרת האינטרנט המודגשת באפליקציה של שרת האינטרנט) או פשוט מרעננים את הדף הקיים. עליך לוודא שנר PLAYBULB פועל ואז ללחוץ על 'התחברות' בדף, ושם המכשיר אמור להופיע מתחת לבורר הצבעים.
קריאת נתוני הטעינה של הסוללה
קיים גם מאפיין Bluetooth סטנדרטי ברמת הסוללה שזמין במכשיר ה-Bluetooth עם נרתיק PLAYBULB שמכיל את רמת הטעינה של הסוללה. המשמעות היא שאנחנו יכולים להשתמש בשמות סטנדרטיים כמו battery_service
עבור ה-UUID של שירות Bluetooth GATT ו-battery_level
עבור ה-UUID של המאפיין Bluetooth GATT.
עכשיו נוסיף שיטת getBatteryLevel
חדשה למחלקה PlaybulbCandle
ונקרא את רמת הטעינה באחוזים.
playbulbCandle.js
getBatteryLevel() {
return this.device.gatt.getPrimaryService('battery_service')
.then(service => service.getCharacteristic('battery_level'))
.then(characteristic => characteristic.readValue())
.then(data => data.getUint8(0));
}
אנחנו צריכים גם לעדכן את אובייקט ה-JavaScript options
כך שיכלול את שירות הסוללה למפתח optionalServices
כי הוא לא מתפרסם על ידי מכשיר ה-Bluetooth של PLAYBULB Candle, אבל עדיין חובה לגשת אליו.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
כמו קודם, אפשר לחבר את זה אל app.js
על ידי קריאה למספר playbulbCandle.getBatteryLevel
אחרי שנקבל את שם המכשיר ונציג את רמת הטעינה של הסוללה.
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
return playbulbCandle.getDeviceName().then(handleDeviceName)
.then(() => playbulbCandle.getBatteryLevel().then(handleBatteryLevel));
})
.catch(error => {
console.error('Argh!', error);
});
});
function handleDeviceName(deviceName) {
document.querySelector('#deviceName').value = deviceName;
}
function handleBatteryLevel(batteryLevel) {
document.querySelector('#batteryLevel').textContent = batteryLevel + '%';
}
בשלב הזה, נכנסים לאתר דרך דפדפן האינטרנט (על ידי לחיצה על כתובת ה-URL של שרת האינטרנט המודגשת באפליקציית שרת האינטרנט) או פשוט מרעננים את הדף הקיים. לוחצים על 'התחברות'. הלחצן בדף, והשם של המכשיר ורמת הטעינה אמורים להופיע.
הנושא הבא
- איך אפשר לשנות את הצבע של הנורה? בגלל זה אני כאן!
- כל כך קרוב, מבטיח...
שאלות נפוצות
6. שינוי הצבע
שינוי הצבע פשוט כמו כתיבת קבוצה ספציפית של פקודות למאפיין Bluetooth (0xFFFC
) בשירות GATT הראשי שמפורסם בשם 0xFF02
. לדוגמה, כדי להפוך את PLAYBULB Candle לאדום, המשמעות היא שמערך מספרים שלמים לא חתומים בן 8 סיביות שווה ל-[0x00, 255, 0, 0]
כאשר 0x00
הוא הרוויה הלבנה ו-255, 0, 0
הם, בהתאמה, הערכים האדומים, הירוקים והכחולים .
נשתמש ב-characteristic.writeValue
כדי לכתוב נתונים מסוימים למאפיין ה-Bluetooth בשיטה הציבורית החדשה setColor
של המחלקה PlaybulbCandle
. בנוסף, נחזיר את הערכים האדומים, הירוקים והכחולים כשההבטחה תמומש, כדי שנוכל להשתמש בהם ב-app.js
מאוחר יותר:
playbulbCandle.js
const CANDLE_COLOR_UUID = 0xFFFC;
...
setColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_COLOR_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
נעדכן את הפונקציה changeColor
ב-app.js
כדי להפעיל playbulbCandle.setColor
כאשר "No Impact" (ללא אפקט) לחצן הבחירה מסומן. משתני הצבע הגלובליים r, g, b
כבר מוגדרים כשהמשתמש לוחץ על לוח הציור.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
if (effect === 'noEffect') {
playbulbCandle.setColor(r, g, b).then(onColorChanged);
}
}
בשלב הזה, נכנסים לאתר בדפדפן האינטרנט (על ידי לחיצה על כתובת ה-URL של שרת האינטרנט המודגשת באפליקציה של שרת האינטרנט) או פשוט מרעננים את הדף הקיים. לוחצים על 'התחברות'. בלחצן בדף ולוחצים על בוחר הצבעים כדי לשנות את הצבע של נר PLAYBULB כמה פעמים שרוצים.
אפקטים של נרות מוארים
אם כבר הדלקתם נר בעבר, סימן שהנורה לא סטטית. למרבה המזל, בשירות GATT הראשי יש מאפיין Bluetooth נוסף (0xFFFB
) שפורסם בתור 0xFF02
, שמאפשר למשתמש להגדיר אפקטים של נרות.
הגדרת 'אפקט לנר' למשל אפשר להשיג על ידי כתיבת [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]
. ואפשר גם להגדיר את 'אפקט ההבהוב' עם [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]
.
עכשיו נוסיף את השיטות setCandleEffectColor
ו-setFlashingColor
לכיתה PlaybulbCandle
.
playbulbCandle.js
const CANDLE_EFFECT_UUID = 0xFFFB;
...
setCandleEffectColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
setFlashingColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
ונעדכן את הפונקציה changeColor
ב-app.js
כדי לקרוא ל-playbulbCandle.setCandleEffectColor
כאשר 'אפקט הנר' לחצן הבחירה מסומן, והלחצן playbulbCandle.setFlashingColor
כאשר מופיע הכיתוב 'מהבהב' לחצן הבחירה מסומן. הפעם נשתמש בכתובת switch
אם זה בסדר מבחינתך.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
switch(effect) {
case 'noEffect':
playbulbCandle.setColor(r, g, b).then(onColorChanged);
break;
case 'candleEffect':
playbulbCandle.setCandleEffectColor(r, g, b).then(onColorChanged);
break;
case 'flashing':
playbulbCandle.setFlashingColor(r, g, b).then(onColorChanged);
break;
}
}
בשלב הזה, נכנסים לאתר בדפדפן האינטרנט (על ידי לחיצה על כתובת ה-URL של שרת האינטרנט המודגשת באפליקציה של שרת האינטרנט) או פשוט מרעננים את הדף הקיים. לוחצים על 'התחברות'. בדף ולשחק עם האפקטים של הנרות והמהבהב.
הנושא הבא
- זה הכול? 3 אפקטים של נרות חלשים? זו הסיבה שאני כאן?
- יש עוד, אבל הפעם אתה לבד.
7. עושים עוד מאמץ
אנחנו כאן! יכול להיות שתחשבו שזה הסוף, אבל האפליקציה עדיין לא הסתיימה. בואו נראה אם הבנתם בפועל את מה שהעתקתם והדבקתם במהלך ה-Codelab הזה. הנה מה שברצונך לעשות בעצמך כדי שהאפליקציה הזו תבלוט.
הוספת אפקטים חסרים
אלה הנתונים של האפקטים החסרים:
- דופק:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00]
(כדאי להתאים שם את הערכים שלr, g, b
) - קשת:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00]
(אנשים אפילפטיים יעדיפו להימנע מהבעיה הזו) - עמעום בצבעי הקשת:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
בעיקרון, מדובר בהוספת שיטות חדשות setPulseColor
, setRainbow
ו-setRainbowFade
לכיתה PlaybulbCandle
ולהתקשר אליהן ב-changeColor
.
איך פותרים את הבעיה 'ללא אפקט'
כפי שהבחנתם, "אין השפעה" האפשרות הזו לא מאפסת שום השפעה בזמן שהיא מתבצעת, הנתון הזה הוא קל אך עדיין. נפתור את זה. בשיטה setColor
, קודם בודקים אם מתבצע אפקט באמצעות משתנה מחלקה חדש _isEffectSet
, ואם true
, משביתים את האפקט לפני שמגדירים צבע חדש עם הנתונים הבאים: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00]
.
כתיבת שם המכשיר
זה קל! כדי לכתוב שם של מכשיר מותאם אישית, פשוט כותבים לפי מאפיין השם הקודם של מכשיר ה-Bluetooth. מומלץ להשתמש בשיטה TextEncoder
encode
כדי לקבל Uint8Array
שמכיל את שם המכשיר.
לאחר מכן, אוסיף "קלט" eventListener
אל document.querySelector('#deviceName')
ולהתקשר אל playbulbCandle.setDeviceName
כדי לשמור על פשטות.
קראתי לי באופן אישי PLAY המקושרים CANDLE!
8. זהו!
מה למדת
- איך לקיים אינטראקציה עם מכשיר Bluetooth בקרבת מקום ב-JavaScript
- איך משתמשים בכיתות ES2015, בפונקציות חיצים, במפה ובהבטחות
השלבים הבאים
- מידע נוסף על Web Bluetooth API
- אפשר לעיין בדוגמאות Bluetooth באינטרנט ובהדגמות הרשמיות
- לצפייה בחתול מרוב זועף