تسمح واجهة برمجة التطبيقات WebHID API للمواقع الإلكترونية بالوصول إلى لوحات مفاتيح مساعدة بديلة وأجهزة تحكّم غريبة في الألعاب.
تاريخ النشر: 15 سبتمبر 2020
هناك العديد من أجهزة الواجهة البشرية (HID)، مثل لوحات المفاتيح البديلة أو أذرع التحكّم في الألعاب غير الشائعة، التي تكون إما جديدة جدًا أو قديمة جدًا أو غير شائعة جدًا بحيث لا يمكن الوصول إليها من خلال برامج تشغيل الأجهزة في الأنظمة. تحلّ WebHID API هذه المشكلة من خلال توفير طريقة لتنفيذ منطق خاص بالجهاز في JavaScript.
حالات الاستخدام المقترَحة
يتلقّى جهاز HID الإدخالات من البشر أو يقدّم لهم المخرجات. تشمل أمثلة الأجهزة لوحات المفاتيح وأجهزة التأشير (الماوس وشاشات اللمس وما إلى ذلك) وأذرع التحكّم في الألعاب. يتيح بروتوكول HID إمكانية الوصول إلى هذه الأجهزة على أجهزة الكمبيوتر المكتبي باستخدام برامج تشغيل نظام التشغيل. تتيح منصة الويب استخدام أجهزة HID من خلال الاعتماد على برامج التشغيل هذه.
ويصبح عدم إمكانية الوصول إلى أجهزة HID غير الشائعة مشكلة كبيرة عند استخدام لوحات مفاتيح مساعدة بديلة (مثل Elgato Stream Deck وسماعات رأس Jabra وX-keys) وعند استخدام أجهزة تحكّم في الألعاب غير الشائعة. تستخدم أجهزة التحكّم في الألعاب المصمّمة لأجهزة الكمبيوتر المكتبي بروتوكول HID غالبًا لإدخال البيانات (الأزرار وعصا التحكّم وأزرار التشغيل) وإخراجها (مؤشرات LED والاهتزاز).
لسوء الحظ، لا تتوفّر معايير موحّدة لإدخال البيانات وإخراجها في وحدات التحكّم في الألعاب، وغالبًا ما تتطلّب متصفّحات الويب منطقًا مخصّصًا لأجهزة معيّنة. وهذا غير مستدام ويؤدي إلى ضعف الدعم للأجهزة القديمة وغير الشائعة. ويؤدي ذلك أيضًا إلى اعتماد المتصفّح على سلوكيات غريبة في أجهزة معيّنة.
المصطلحات
يمكن لجهاز الواجهة البشرية (HID) تلقّي إدخالات أو تقديم مخرجات للمستخدمين. هناك بروتوكول HID، وهو معيار للاتصال الثنائي الاتجاه بين مضيف وجهاز مصمّم لتبسيط إجراءات التثبيت.
يتألف HID من مفهومَين أساسيَين: التقارير وأوصاف التقارير. التقارير هي البيانات التي يتم تبادلها بين جهاز وبرنامج عميل. يصف واصف التقرير تنسيق البيانات التي يتوافق معها الجهاز ومعناها.
تتبادل التطبيقات وأجهزة HID البيانات الثنائية من خلال ثلاثة أنواع من التقارير:
| نوع التقرير | الوصف |
|---|---|
| تقرير الإدخال | البيانات التي يتم إرسالها من الجهاز إلى التطبيق (مثل الضغط على زر) |
| تقرير الناتج | البيانات التي يتم إرسالها من التطبيق إلى الجهاز (مثل طلب تشغيل الإضاءة الخلفية للوحة المفاتيح) |
| تقرير الميزة | البيانات التي يمكن إرسالها في أيّ من الاتجاهين ويكون التنسيق خاصًا بالجهاز. |
يصف واصف التقرير التنسيق الثنائي للتقارير التي يتيحها الجهاز. بنيته هرمية ويمكنه تجميع التقارير معًا كمجموعات مميزة ضمن المجموعة ذات المستوى الأعلى. يتم تحديد تنسيق الواصف من خلال مواصفات HID.
استخدام HID هو قيمة رقمية تشير إلى إدخال أو إخراج موحّد. تتيح قيم الاستخدام للجهاز وصف الاستخدام المقصود للجهاز والغرض من كل حقل في تقاريره. على سبيل المثال، يتم تحديد أحدها لزر الماوس الأيسر. يتم أيضًا تنظيم الاستخدامات في صفحات الاستخدام، والتي تقدّم إشارة إلى الفئة العالية المستوى للجهاز أو التقرير.
استخدام WebHID API
للتحقّق من توفّر واجهة برمجة التطبيقات WebHID API، استخدِم ما يلي:
if ("hid" in navigator) {
// The WebHID API is supported.
}
فتح اتصال بجهاز HID
تم تصميم واجهة برمجة التطبيقات WebHID API بشكل غير متزامن لمنع حظر واجهة مستخدم الموقع الإلكتروني عند انتظار الإدخال. وهذا مهم لأنّه يمكن تلقّي بيانات HID في أي وقت، ما يتطلّب طريقة للاستماع إليها.
لفتح اتصال HID، عليك أولاً الوصول إلى عنصر HIDDevice. لإجراء ذلك، يمكنك إما أن تطلب من المستخدم اختيار جهاز من خلال استدعاء navigator.hid.requestDevice()، أو اختيار جهاز من navigator.hid.getDevices() الذي يعرض قائمة بالأجهزة التي تم منح الموقع الإلكتروني إذن الوصول إليها سابقًا.
تأخذ الدالة navigator.hid.requestDevice() عنصرًا إلزاميًا يحدّد الفلاتر. تُستخدَم هذه المعرّفات لمطابقة أي جهاز متصل بمعرّف مورّد USB (vendorId) ومعرّف منتج USB (productId) وقيمة صفحة الاستخدام (usagePage) وقيمة الاستخدام (usage). ويمكنك الحصول عليها من مستودع معرّفات USB ومستند جداول استخدام HID.
تمثّل عناصر HIDDevice المتعددة التي تعرضها هذه الدالة واجهات HID متعددة على الجهاز المادي نفسه.
// Filter on devices with the Nintendo Switch Joy-Con USB Vendor/Product IDs.
const filters = [
{
vendorId: 0x057e, // Nintendo Co., Ltd
productId: 0x2006 // Joy-Con Left
},
{
vendorId: 0x057e, // Nintendo Co., Ltd
productId: 0x2007 // Joy-Con Right
}
];
// Prompt user to select a Joy-Con device.
const [device] = await navigator.hid.requestDevice({ filters });
// Get all devices the user has previously granted the website access to.
const devices = await navigator.hid.getDevices();
يمكنك أيضًا استخدام المفتاح الاختياري exclusionFilters في navigator.hid.requestDevice() لاستبعاد بعض الأجهزة من أداة اختيار المتصفّح التي يُعرف أنّها لا تعمل بشكل صحيح.
// Request access to a device with vendor ID 0xABCD. The device must also have
// a collection with usage page Consumer (0x000C) and usage ID Consumer
// Control (0x0001). The device with product ID 0x1234 is malfunctioning.
const [device] = await navigator.hid.requestDevice({
filters: [{ vendorId: 0xabcd, usagePage: 0x000c, usage: 0x0001 }],
exclusionFilters: [{ vendorId: 0xabcd, productId: 0x1234 }],
});
يحتوي العنصر HIDDevice على معرّفات مورّد USB ومعرّفات المنتج لتحديد الجهاز. يتم ضبط قيمة السمة collections باستخدام وصف هرمي لتنسيقات التقارير الخاصة بالجهاز.
for (let collection of device.collections) {
// An HID collection includes usage, usage page, reports, and subcollections.
console.log(`Usage: ${collection.usage}`);
console.log(`Usage page: ${collection.usagePage}`);
for (let inputReport of collection.inputReports) {
console.log(`Input report: ${inputReport.reportId}`);
// Loop through inputReport.items
}
for (let outputReport of collection.outputReports) {
console.log(`Output report: ${outputReport.reportId}`);
// Loop through outputReport.items
}
for (let featureReport of collection.featureReports) {
console.log(`Feature report: ${featureReport.reportId}`);
// Loop through featureReport.items
}
// Loop through subcollections with collection.children
}
يتم إرجاع أجهزة HIDDevice في حالة "مغلقة" تلقائيًا، ويجب فتحها من خلال الاتصال بـ open() قبل إرسال البيانات أو تلقّيها.
// Wait for the HID connection to open before sending/receiving data.
await device.open();
تلقّي تقارير الإدخال

بعد إنشاء اتصال HID، يمكنك التعامل مع تقارير الإدخال الواردة من خلال الاستماع إلى أحداث "inputreport" من الجهاز. تحتوي هذه الأحداث على بيانات HID كعنصر DataView (data) وجهاز HID الذي تنتمي إليه (device) ومعرّف التقرير المكوّن من 8 بت المرتبط بتقرير الإدخال (reportId).
بالاستناد إلى المثال السابق، يساعدك هذا الرمز في رصد الزر الذي ضغط عليه المستخدم على جهاز Joy-Con Right، ما يتيح لك تجربة ذلك في المنزل.
device.addEventListener("inputreport", event => {
const { data, device, reportId } = event;
// Handle only the Joy-Con Right device and a specific report ID.
if (device.productId !== 0x2007 && reportId !== 0x3f) return;
const value = data.getUint8(0);
if (value === 0) return;
const someButtons = { 1: "A", 2: "X", 4: "B", 8: "Y" };
console.log(`User pressed button ${someButtons[value]}.`);
});
يمكنك الرجوع إلى العرض التوضيحي على CodePen.
إرسال تقارير النتائج
لإرسال تقرير إخراج إلى جهاز HID، مرِّر معرّف التقرير المكوّن من 8 بت المرتبط بتقرير الإخراج (reportId) والبايتات كـ BufferSource (data) إلى device.sendReport(). يتم حلّ الوعد الذي تم إرجاعه بعد إرسال التقرير. إذا كان جهاز HID لا يستخدم أرقام تعريف التقارير، اضبط reportId على 0.
ينطبق المثال التالي على جهاز Joy-Con ويوضّح لك كيفية إصدار صوت اهتزاز باستخدام تقارير الإخراج.
// First, send a command to enable vibration.
// Magical bytes come from https://github.com/mzyy94/joycon-toolweb
const enableVibrationData = [1, 0, 1, 64, 64, 0, 1, 64, 64, 0x48, 0x01];
await device.sendReport(0x01, new Uint8Array(enableVibrationData));
// Then, send a command to make the Joy-Con device rumble.
// Actual bytes are available in the sample.
const rumbleData = [ /* ... */ ];
await device.sendReport(0x10, new Uint8Array(rumbleData));
يمكنك الرجوع إلى العرض التوضيحي على CodePen.
إرسال تقارير عن الميزات وتلقّيها

تقارير الميزات هي النوع الوحيد من تقارير بيانات HID التي يمكن أن تنتقل في كلا الاتجاهين. تسمح هذه التقنية لأجهزة HID وتطبيقاتها بتبادل بيانات HID غير موحّدة. على عكس تقارير الإدخال والإخراج، لا تتلقّى التطبيقات تقارير الميزات أو ترسلها بشكل منتظم.
لإرسال تقرير ميزة إلى جهاز HID، مرِّر معرّف التقرير المكوّن من 8 بت والمرتبط بتقرير الميزة (reportId) والبايتات كـ BufferSource (data) إلى device.sendFeatureReport(). يتم حلّ الوعد الذي تم إرجاعه بعد إرسال التقرير. إذا كان جهاز HID لا يستخدم أرقام تعريف التقارير، اضبط reportId على 0.
يوضّح هذا المثال كيفية استخدام تقارير الميزات من خلال عرض كيفية طلب جهاز إضاءة خلفية للوحة مفاتيح Apple وفتحه وجعله يومض.
const waitFor = duration => new Promise(r => setTimeout(r, duration));
// Prompt user to select an Apple Keyboard Backlight device.
const [device] = await navigator.hid.requestDevice({
filters: [{ vendorId: 0x05ac, usage: 0x0f, usagePage: 0xff00 }]
});
// Wait for the HID connection to open.
await device.open();
// Blink!
const reportId = 1;
for (let i = 0; i < 10; i++) {
// Turn off
await device.sendFeatureReport(reportId, Uint32Array.from([0, 0]));
await waitFor(100);
// Turn on
await device.sendFeatureReport(reportId, Uint32Array.from([512, 0]));
await waitFor(100);
}
يمكنك الرجوع إلى العرض التوضيحي على CodePen.
لتلقّي تقرير ميزة من جهاز HID، مرِّر معرّف التقرير المكوّن من 8 بتات
المرتبط بتقرير الميزة (reportId) إلى
device.receiveFeatureReport(). يتم حلّ الوعد الذي تم إرجاعه باستخدام الكائن DataView الذي يحتوي على محتوى تقرير الميزة. إذا كان جهاز HID لا يستخدم أرقام تعريف التقارير، اضبط reportId على 0.
// Request feature report.
const dataView = await device.receiveFeatureReport(/* reportId= */ 1);
// Read feature report contents with dataView.getInt8(), getUint8(), etc...
الاستماع إلى صوتي الربط وإلغاء الربط
عند منح الموقع الإلكتروني إذنًا بالوصول إلى جهاز HID، يمكنه تلقّي أحداث الاتصال والانقطاع بشكل نشط من خلال الاستماع إلى حدثَي "connect" و"disconnect".
navigator.hid.addEventListener("connect", event => {
// Automatically open event.device or warn user a device is available.
});
navigator.hid.addEventListener("disconnect", event => {
// Remove |event.device| from the UI.
});
إبطال إذن الوصول إلى جهاز HID
يمكن للموقع الإلكتروني إزالة الأذونات بالوصول إلى جهاز HID لم يعُد مهتمًا بالاحتفاظ بها من خلال استدعاء forget() على مثيل HIDDevice. على سبيل المثال، في تطبيق ويب تعليمي يتم استخدامه على جهاز كمبيوتر مشترك مع العديد من الأجهزة، يؤدي العدد الكبير من الأذونات المتراكمة التي يمنحها المستخدم إلى تجربة مستخدم سيئة.
سيؤدي استدعاء forget() على مثيل HIDDevice واحد إلى إبطال إذن الوصول إلى جميع واجهات HID على الجهاز الفعلي نفسه.
// Voluntarily revoke access to this HID device.
await device.forget();
بما أنّ forget() متاحة في الإصدار 100 من Chrome أو الإصدارات الأحدث، تحقَّق مما إذا كانت هذه الميزة متوافقة مع ما يلي:
if ("hid" in navigator && "forget" in HIDDevice.prototype) {
// forget() is supported.
}
نصائح للمطوّرين

يمكنك تصحيح أخطاء HID في Chrome باستخدام الصفحة الداخلية about://device-log، حيث يمكنك الاطّلاع على جميع الأحداث ذات الصلة بأجهزة HID وUSB في مكان واحد.
يمكنك الاطّلاع على HID explorer لتفريغ معلومات جهاز HID بتنسيق يمكن قراءته. وهي تربط قيم الاستخدام بالأسماء لكل استخدام من استخدامات HID.
في معظم أنظمة Linux، يتم ربط أجهزة الواجهة البشرية (HID) بأذونات القراءة فقط تلقائيًا. للسماح لمتصفّح Chrome بفتح جهاز HID، عليك إضافة قاعدة udev
جديدة. أنشِئ ملفًا في /etc/udev/rules.d/50-yourdevicename.rules يتضمّن المحتوى التالي:
KERNEL=="hidraw*", ATTRS{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
في هذا الرمز، تكون قيمة [yourdevicevendor] هي 057e، مثلاً إذا كان جهازك هو
Nintendo Switch Joy-Con. يمكن إضافة ATTRS{idProduct} للحصول على قاعدة أكثر تحديدًا. تأكَّد من أنّ user هو عضو في مجموعة plugdev. بعد ذلك، ما عليك سوى إعادة توصيل جهازك.
العروض التوضيحية
يمكنك الاطّلاع على بعض العروض التوضيحية لواجهة برمجة التطبيقات WebHID على web.dev/hid-examples.
الأمان والخصوصية
صمّم مطوّرو المواصفات واجهة برمجة التطبيقات WebHID API ونفّذوها باستخدام المبادئ الأساسية المحدّدة في التحكّم في الوصول إلى الميزات الفعّالة لمنصة الويب، بما في ذلك تحكّم المستخدم والشفافية وبيئة العمل المريحة. تعتمد إمكانية استخدام واجهة برمجة التطبيقات هذه بشكل أساسي على نموذج أذونات يمنح إذن الوصول إلى جهاز HID واحد فقط في كل مرة. استجابةً لطلب من المستخدم، يجب أن يتّخذ المستخدم خطوات نشطة لاختيار جهاز واجهة بشرية (HID) معيّن.
للتعرّف على المفاضلات بين الأمان والخصوصية، يمكنك الاطّلاع على قسم اعتبارات الأمان والخصوصية في مواصفات WebHID.
بالإضافة إلى ذلك، يفحص Chrome استخدام كل مجموعة من المستوى الأعلى، وإذا كانت إحدى المجموعات من المستوى الأعلى تتضمّن استخدامًا محميًا (مثل لوحة المفاتيح العامة أو الماوس)، لن يتمكّن الموقع الإلكتروني من إرسال أي تقارير محدّدة في تلك المجموعة أو تلقّيها. يمكن الاطّلاع على القائمة الكاملة بحالات الاستخدام المحمية.
يُرجى العِلم أنّه يتم أيضًا حظر أجهزة HID الحساسة للأمان (مثل أجهزة FIDO HID المستخدَمة للمصادقة القوية) في Chrome. اطّلِع على ملفَي قائمة USB المحظورة وقائمة HID المحظورة.
الملاحظات
يسرّ فريق Chrome معرفة رأيك وتجربتك بشأن WebHID API.
أخبِرنا عن تصميم واجهة برمجة التطبيقات
هل هناك مشكلة في واجهة برمجة التطبيقات؟ أو هل هناك طرق أو سمات ناقصة تحتاج إلى تنفيذ فكرتك؟
يمكنك الإبلاغ عن مشكلة في المواصفات في مستودع WebHID API على GitHub أو إضافة أفكارك إلى مشكلة حالية.
الإبلاغ عن مشكلة في عملية التنفيذ
هل عثرت على خطأ في تنفيذ Chrome؟ أو هل يختلف التنفيذ عن المواصفات؟
اطّلِع على كيفية الإبلاغ عن أخطاء WebHID. تأكَّد من تضمين أكبر قدر ممكن من التفاصيل، وقدِّم تعليمات لإعادة إنتاج الخطأ، واضبط المكوّنات على Blink>HID.
روابط مفيدة
- المواصفات
- خطأ في التتبُّع
- إدخال ChromeStatus.com
- مكوّن Blink:
Blink>HID
الإقرارات
نشكر مات رينولدز وجو ميدلي على مراجعاتهما.