الاتصال بأجهزة HID غير مألوفة

تسمح واجهة برمجة التطبيقات WebHID API للمواقع الإلكترونية بالوصول إلى لوحات المفاتيح الإضافية البديلة وأجهزة التحكّم في الألعاب الغريبة.

François Beaufort
François Beaufort

هناك عدد كبير من أجهزة الواجهة البشرية (HID)، مثل لوحات المفاتيح البديلة أو وحدات التحكّم في الألعاب الغريبة، التي تكون جديدة جدًا أو قديمة جدًا أو غير شائعة جدًا بحيث لا يمكن لبرامج تشغيل الأجهزة في الأنظمة الاستفادة منها. وتحلّ WebHID API هذه المشكلة من خلال توفير طريقة لتنفيذ منطق خاص بالجهاز في JavaScript.

حالات الاستخدام المقترَحة

يتلقّى جهاز HID إدخالات من المستخدمين أو يقدّم لهم مخرجات. تشمل أمثلة الأجهزة لوحات المفاتيح وأجهزة التأشير (مثل الماوس والشاشات التي تعمل باللمس وما إلى ذلك) وأجهزة التحكّم في الألعاب. ويتيح بروتوكول HID إمكانية الوصول إلى هذه الأجهزة على أجهزة كمبيوتر سطح المكتب باستخدام برامج تشغيل نظام التشغيل. تتوافق منصة الويب مع أجهزة HID من خلال الاعتماد على برامج التشغيل هذه.

وتُعدّ عدم القدرة على الوصول إلى أجهزة الواجهة البشرية (HID) غير مألوفة أمرًا مؤلمًا بشكل خاص عندما يتعلق الأمر بلوحات المفاتيح الإضافية البديلة (مثل Elgato Stream Deck وسماعات Jabra ومفاتيح X) وإمكانية استخدام أجهزة غير تقليدية في الألعاب. إنّ أجهزة التحكّم في الألعاب المصمّمة لأجهزة الكمبيوتر المكتبي تستخدم غالبًا بروتوكول HID لإدخالات أجهزة التحكّم في الألعاب (الأزرار وعصا التحكم وأدوات التنشيط) ومخرجاتها (مؤشرات LED والاهتزاز). لا تكون مدخلات ومخرجات أجهزة التحكّم في الألعاب متّسقة بشكلٍ جيد، وغالبًا ما تتطلّب متصفّحات الويب منطقًا مخصّصًا لأجهزة معيّنة. وهذا النهج غير مستدام ويؤدي إلى توفّر دعم ضعيف للأجهزة القديمة والغير الشائعة. ويؤدي ذلك أيضًا إلى اعتماد المتصفّح على سلوكيات غريبة في الأجهزة المحدّدة.

المصطلحات

يتألف HID من مفهومَين أساسيَين: التقارير ووصاف التقارير. التقارير هي البيانات التي يتم تبادلها بين جهاز وعميل برنامج. يصف وصف التقرير تنسيق البيانات التي يقبلها الجهاز ومعناها.

HID (جهاز الواجهة البشرية) هو نوع من الأجهزة التي تستخرج المدخلات من المستخدم أو يوفر إخراجه. يشير أيضًا إلى بروتوكول HID، وهو معيار للاتصال ثنائي الاتجاه بين المضيف والجهاز المصمم لتبسيط إجراءات التثبيت. تم تطوير بروتوكول HID في الأصل لأجهزة USB، ولكن تم تنفيذه منذ ذلك الحين على العديد من البروتوكولات الأخرى، بما في ذلك البلوتوث.

تتبادل التطبيقات وأجهزة 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();
لقطة شاشة لطلب جهاز HID على موقع إلكتروني
طلب من المستخدم لاختيار جهاز Nintendo Switch Joy-Con

يمكنك أيضًا استخدام المفتاح الاختياري 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).

صورة لجهاز Nintendo Switch باللونَين الأحمر والأزرق
أجهزة Nintendo Switch Joy-Con

استمرارًا للمثال السابق، يوضح لك الرمز أدناه كيفية اكتشاف الزر الذي ضغط عليه المستخدم على جهاز Joy-ConRight بحيث يمكنك تجربته في المنزل.

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]}.`);
});

إرسال تقارير النتائج

لإرسال تقرير إخراج إلى جهاز واجهة بشرية (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 below.
const rumbleData = [ /* ... */ ];
await device.sendReport(0x10, new Uint8Array(rumbleData));

إرسال تقارير الميزات واستلامها

تقارير العناصر هي النوع الوحيد من تقارير بيانات 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);
}

لتلقّي تقرير ميزات من جهاز 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
صفحة داخلية في Chrome لتصحيح أخطاء HID

يمكنك الاطّلاع على 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 API على جميع الأنظمة الأساسية لأجهزة كمبيوتر سطح المكتب (ChromeOS وLinux وmacOS وWindows) في Chrome 89.

إصدارات تجريبية

يمكنك الاطّلاع على بعض العروض التوضيحية لواجهة WebHID على الرابط web.dev/hid-examples. ألقِ نظرة.

الأمان والخصوصية

لقد صمم مؤلفو المواصفات واجهة برمجة التطبيقات WebHID API ونفّذوها باستخدام مبادئ أساسية تم تحديدها في مقالة التحكّم في الوصول إلى ميزات منصة الويب الفعّالة، بما في ذلك التحكّم الذي يمارسه المستخدم والشفافية وسهولة الاستخدام. إنّ إمكانية استخدام واجهة برمجة التطبيقات هذه تعتمد بشكل أساسي على نموذج إذن يمنح إمكانية الوصول إلى جهاز واجهة بشرية واحد فقط في كل مرّة. استجابةً لمطالبة المستخدم، على المستخدم اتخاذ خطوات نشطة لاختيار جهاز واجهة بشرية معين.

لفهم المفاضلات الأمنية، اطّلِع على قسم الخصوصية والأمان الاعتبارات في مواصفات WebHID.

بالإضافة إلى ذلك، يتحقّق Chrome من استخدام كل مجموعة من المستوى الأعلى، وإذا كانت مجموعة من المستوى الأعلى لها استخدام محمي (مثل لوحة مفاتيح عامة أو فأرة)، لن يتمكّن الموقع الإلكتروني من إرسال أي تقارير محدّدة في تلك المجموعة أو تلقّيها. إنّ القائمة الكاملة للاستخدامات المحمية متاحة للجميع.

يُرجى العِلم أنّه يتم أيضًا حظر أجهزة HID الحساسة للأمان (مثل أجهزة HID التي تستخدم بروتوكول FIDO للمصادقة بقوة أكبر) في Chrome. اطّلِع على ملفَي القائمة المحظورة لأجهزة USB والقائمة المحظورة لأجهزة HID.

ملاحظات

يسرّ فريق Chrome معرفة رأيك وتجاربك بشأن واجهة برمجة التطبيقات WebHID API.

أخبِرنا عن تصميم واجهة برمجة التطبيقات.

هل هناك أي مشكلة في واجهة برمجة التطبيقات لا تعمل كما هو متوقع؟ هل هناك methods أو properties مفقودة تحتاجها لتنفيذ فكرتك؟

يمكنك الإبلاغ عن مشكلة في المواصفات على مستودع GitHub الخاص بواجهة برمجة التطبيقات WebHID API أو إضافة ملاحظاتك إلى مشكلة حالية.

الإبلاغ عن مشكلة في التنفيذ

هل واجهت مشكلة في التنفيذ في Chrome؟ أم أن التنفيذ يختلف عن المواصفات؟

يمكنك الاطّلاع على كيفية الإبلاغ عن أخطاء WebHID. احرص على تضمين أكبر قدر ممكن من التفاصيل، وتقديم تعليمات بسيطة لإعادة إنتاج الخلل، وضبط قيمة المكوّنات على Blink>HID. يعمل الخطأ بشكلٍ رائع لمشاركة عمليات إعادة الإنتاج السريعة والسهلة.

إظهار الدعم

هل تخطّط لاستخدام واجهة برمجة التطبيقات WebHID API؟ يساعد دعمك العلني فريق Chrome في تحديد أولويات الميزات وإظهار مدى أهمية توفيرها لموفّري المتصفّحات الآخرين.

أرسِل تغريدة إلى ‎@ChromiumDev باستخدام الهاشتاغ #WebHID وأخبِرنا بمكان وطريقة استخدامك له.

روابط مفيدة

الشكر والتقدير

نشكر مات رينولدز وجو ميديل على مراجعتهما لهذه المقالة. صورة لجهاز Nintendo Switch باللونَين الأحمر والأزرق تصوّرها سارة كورفيس، وصورة لجهاز كمبيوتر محمول باللونَين الأسود والفضي تصوّرها أتول كيرياك أجاي على Unsplash