1. الفكرة الرئيسية للّعبة
في هذا الدرس التطبيقي المدروس حول الترميز، ستتعرّف على كيفية التحكّم في شمعة عاكسة لإضاءة LED في PLAYBULB باستخدام JavaScript فقط، وذلك بفضل Web Bluetooth API. بالإضافة إلى ذلك، ستستمتع أيضًا بميزات JavaScript ES2015، مثل الفئات والدوال السهمية والخريطة والوعود.
المعلومات التي ستطّلع عليها
- كيفية التفاعل مع جهاز قريب يتضمّن بلوتوث في JavaScript
- كيفية استخدام فئات ES2015، ودوال الأسهم، والخرائط، والوعود
المتطلبات
- فهم أساسي لتطوير البرامج على الويب
- معلومات أساسية حول البلوتوث منخفض الطاقة (BLE) والملف الشخصي للسمة العامة (GATT)
- محرِّر نصوص من اختيارك
- جهاز Mac أو Chromebook أو جهاز Android M يتضمّن تطبيق متصفّح Chrome وكابل USB مصغر إلى USB
2. اللعب أولاً
ننصحك بالاطّلاع على الإصدار النهائي من التطبيق الذي تريد إنشائه من خلال الرابط https://googlecodelabs.github.io/candle-bluetooth واللعب باستخدام جهاز PLAYBULB Candle الذي يعمل بتقنية البلوتوث تحت تصرفك قبل الانتقال إلى هذا الدرس التطبيقي حول الترميز.
يمكنك أيضًا متابعتي أثناء تغيير الألوان على الرابط https://www.youtube.com/watch?v=fBCPA9gIxlU
3- الإعداد
تنزيل نموذج الرمز
يمكنك الحصول على نموذج الرمز لهذا الرمز عن طريق تنزيل الرمز المضغوط من هنا:
أو من خلال استنساخ مستودع git هذا:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
إذا نزّلت المصدر كملف zip، سيؤدي فك ضغطه إلى الحصول على المجلد الجذر candle-bluetooth-master
.
تثبيت خادم الويب وإثبات ملكيته
على الرغم من أنّه يمكنك استخدام خادم الويب الخاص بك بحرية، صُمِّم هذا الدرس التطبيقي ليعمل بشكل جيد مع خادم الويب Chrome. إذا لم يكن هذا التطبيق مثبّتًا، يمكنك تثبيته من "سوق Chrome الإلكتروني".
بعد تثبيت تطبيق خادم الويب لمتصفح Chrome، انقر على اختصار التطبيقات في شريط الإشارات المرجعية:
في النافذة التالية، انقر فوق أيقونة خادم الويب:
سيظهر لك مربع الحوار هذا بعد ذلك، والذي يتيح لك ضبط خادم الويب المحلي:
انقر على الزر اختيار مجلد، واختَر جذر المستودع المنسوخ (أو الذي تم إخراجه من الأرشيف). سيمكّنك هذا من عرض عملك قيد التقدم من خلال عنوان URL المظلل في مربع حوار خادم الويب (في قسم عناوين URL لخادم الويب).
ضمن "الخيارات"، ضع علامة في المربّع بجانب "عرض index.html تلقائيًا"، كما هو موضّح أدناه:
انتقِل الآن إلى موقعك الإلكتروني من خلال متصفّح الويب (بالنقر على عنوان URL الخاص بخادم الويب) لتظهر لك صفحة على النحو التالي:
إذا أردت معرفة كيف سيبدو هذا التطبيق على هاتف Android، يجب تفعيل تصحيح الأخطاء عن بُعد على Android وإعداد إعادة توجيه المنفذ (رقم المنفذ هو 8887 تلقائيًا). وبعد ذلك، يمكنك ببساطة فتح علامة تبويب جديدة في Chrome للوصول إلى http://localhost:8887 على هاتف Android.
التالي
في الوقت الحالي، لا يقدّم تطبيق الويب هذا الكثير من الإجراءات. لنبدأ في إضافة دعم البلوتوث.
4. استكشِف الشمعة
سنبدأ بكتابة مكتبة تستخدم فئة JavaScript ES2015 لجهاز PLAYBULB Candle بلوتوث.
حافظ على هدوئك. لا تقدِّم بنية الفئة نموذجًا جديدًا للاكتساب الموجَّه إلى العناصر في JavaScript. إنه يوفر ببساطة بناء جملة أوضح جدًا لإنشاء الكائنات والتعامل مع التوريث، كما يمكنك قراءة أدناه.
أولاً، لنحدِّد فئة PlaybulbCandle
في playbulbCandle.js
وننشئ مثيل playbulbCandle
سيكون متاحًا في ملف app.js
لاحقًا.
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
لطلب الوصول إلى جهاز قريب يتضمّن بلوتوث، علينا الاتصال بـ 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();
})();
كميزة أمان، يجب طلب رصد الأجهزة القريبة التي تتضمّن بلوتوث من خلال "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
" الذي تم تسجيله.
قد تظهر لك رسالة خطأ عند إيقاف البلوتوث و/أو إيقاف جهاز بلوتوث PLAYBULB Candle. وفي هذه الحالة، يُرجى تفعيله والمتابعة من جديد.
مكافأة إلزامية
لا أعرف شيئًا عنك، لكنني أرى عددًا كبيرًا جدًا من function() {}
في هذا الرمز. لنبدِّل إلى () => {}
JavaScript ES2015 Arrow Functions بدلاً من ذلك. إنّها موفرات مطلقة للحياة: من قبيل حب الوظائف المجهولة التي لا غنى لها عن الربط.
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 عن بُعد في البلوتوث والذي يحتفظ بخدمة البلوتوث وتعريفات الخصائص من خلال الاتصال بـ 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 الخاص بجهاز PLAYBULB Candle Bluetooth. نريد الآن الحصول على خدمة 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، ثم انقر على "اتصال" في الصفحة، ومن المفترض أن يظهر اسم الجهاز أسفل منتقي الألوان.
قراءة مستوى البطارية
تتوفر أيضًا ميزة بلوتوث عادية لمستوى البطارية في جهاز PLAYBULB Candle Bluetooth الذي يحتوي على مستوى بطارية الجهاز. ويعني هذا أنّه يمكننا استخدام الأسماء العادية، مثل battery_service
للمعرّف الفريد العالمي (UUID) لخدمة Bluetooth GATT وbattery_level
للمعرّف الفريد العالمي (UUID) لخاصية 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
لأنّه لا يتم الإعلان عنه من خلال جهاز 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- تغيير اللون
يمكن تغيير اللون بسهولة مثل كتابة مجموعة محددة من الأوامر إلى خاصية البلوتوث (0xFFFC
) في خدمة GATT الأساسية المعلن عنها باسم 0xFF02
. على سبيل المثال، تحويل شمعة PLAYBULB إلى اللون الأحمر سيؤدي إلى كتابة مصفوفة من الأعداد الصحيحة غير الموقَّعة المكوّنة من 8 بت والتي تساوي [0x00, 255, 0, 0]
، حيث يشير 0x00
إلى تشبّع اللون الأبيض وتمثّل 255, 0, 0
القيم الحمراء والأخضر والأزرق على التوالي .
سنستخدم characteristic.writeValue
لكتابة بعض البيانات إلى خاصية البلوتوث في الطريقة العامة الجديدة 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
عندما تظهر الرسالة "لا يوجد تأثير" تحديد زر الاختيار. سبق أن تم ضبط متغيّرات اللون 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 حسبما تريد.
تأثيرات شموع Moar
إذا كنت قد أضاءت شمعة من قبل، يعني ذلك أنّ الضوء ليس ثابتًا. لحسن الحظ، هناك خاصية بلوتوث أخرى (0xFFFB
) في خدمة GATT الأساسية المُعلَن عنها على أنّها 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. خطوات إضافية
ها نحن ذا! قد تعتقد أن الانتهاء قد اقترب ولكن التطبيق لم ينته بعد. لنرَ ما إذا كنت قد فهمت بالفعل ما قمت بنسخه ولصقه في هذا الدرس التطبيقي حول الترميز. في ما يلي الإجراءات التي تريد أن تفعلها بنفسك الآن لجعل هذا التطبيق مميزًا.
إضافة تأثيرات مفقودة
في ما يلي بيانات التأثيرات المفقودة:
- النبض:
[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]
.
كتابة اسم الجهاز
هذه الخطوة سهلة! يمكن كتابة اسم جهاز مخصص بسهولة مثل كتابة خاصية اسم جهاز بلوتوث السابقة. أنصح باستخدام طريقة encode
TextEncoder
للحصول على جهاز Uint8Array
يحتوي على اسم الجهاز.
بعد ذلك، سأضيف "إدخال" eventListener
إلى document.querySelector('#deviceName')
والاتصال بـ playbulbCandle.setDeviceName
لتبسيط الأمر.
أسمّي اسمي PLAY💡 CANDLE.
8. هذا كل شيء!
الدروس المستفادة
- كيفية التفاعل مع جهاز قريب يتضمّن بلوتوث في JavaScript
- كيفية استخدام فئات ES2015، ودوال الأسهم، والخرائط، والوعود
الخطوات التالية
- تعرَّف على مزيد من المعلومات حول Web Bluetooth API.
- تصفَّح نماذج Web Bluetooth والعروض التوضيحية الرسميَين.
- يمكنك مشاهدة القط الغاضب الذي يطير