การเชื่อมต่อกับอุปกรณ์ HID ที่ไม่ปกติ

WebHID API ช่วยให้เว็บไซต์เข้าถึงแป้นพิมพ์เสริมสำรองและเกมแพดที่แปลกใหม่ได้

François Beaufort
François Beaufort

อุปกรณ์สำหรับมนุษย์ (HID) ที่มีอยู่หากันมาอย่างยาวนาน เช่น แป้นพิมพ์หรือเกมแพดที่แปลกใหม่ ซึ่งใหม่เกินไป เก่าเกินไป หรือแปลกผิดปกติ เข้าถึงได้จากระบบ ไดรเวอร์อุปกรณ์ WebHID API แก้ปัญหานี้โดยการระบุ วิธีนำตรรกะที่เจาะจงอุปกรณ์ไปใช้ใน JavaScript

Use Case ที่แนะนำ

อุปกรณ์ HID รับอินพุตหรือเอาต์พุตให้มนุษย์ ตัวอย่างอุปกรณ์ รวมถึงแป้นพิมพ์ อุปกรณ์ชี้ตำแหน่ง (เมาส์ หน้าจอสัมผัส ฯลฯ) และเกมแพด โปรโตคอล HID ช่วยให้เข้าถึงอุปกรณ์เหล่านี้บนเดสก์ท็อปได้ คอมพิวเตอร์ที่ใช้ไดรเวอร์ระบบปฏิบัติการ แพลตฟอร์มเว็บรองรับอุปกรณ์ HID โดยอาศัยตัวขับเคลื่อนเหล่านี้

การไม่สามารถเข้าถึงอุปกรณ์ HID ที่ผิดปกตินั้นเป็นเรื่องที่เจ็บปวดมาก สำหรับแป้นพิมพ์เสริมทางเลือก (เช่น Elgato Stream Deck, Jabra ชุดหูฟัง ปุ่ม X) และการสนับสนุนเกมแพดที่แปลกใหม่ เกมแพดที่ออกแบบมาเพื่อเดสก์ท็อป มักใช้ HID สำหรับอินพุตเกมแพด (ปุ่ม จอยสติ๊ก ทริกเกอร์) และเอาต์พุต (ไฟ LED สีรุ้ง) ขออภัย อินพุตและเอาต์พุตของเกมแพดไม่ดี และเว็บเบราว์เซอร์มาตรฐานมักต้องใช้ตรรกะที่กำหนดเองสำหรับอุปกรณ์บางประเภท สิ่งนี้ไม่ยั่งยืน ส่งผลให้เกิดการสนับสนุนที่ไม่ดีสำหรับผู้สูงอายุและ อุปกรณ์ที่ผิดปกติ และยังทำให้เบราว์เซอร์ต้องพึ่งพฤติกรรมที่ไม่ปกติ ของอุปกรณ์ที่เฉพาะเจาะจง

คำศัพท์

HID ประกอบด้วยแนวคิดพื้นฐาน 2 ประการ ได้แก่ รายงานและข้อบ่งชี้รายงาน รายงานคือข้อมูลที่รับส่งกันระหว่างอุปกรณ์กับซอฟต์แวร์ไคลเอ็นต์ ข้อบ่งชี้รายงานจะอธิบายรูปแบบและความหมายของข้อมูลที่อุปกรณ์ รองรับ

HID (Human Interface Device) เป็นประเภทของอุปกรณ์ที่รับอินพุตหรือ ให้ผลผลิตแก่มนุษย์ นอกจากนี้ยังหมายถึงโปรโตคอล HID ซึ่งเป็นมาตรฐานสำหรับ การสื่อสารแบบ 2 ทิศทางระหว่างโฮสต์และอุปกรณ์ที่ออกแบบมาเพื่อ ลดความซับซ้อนของขั้นตอนการติดตั้ง โปรโตคอล HID พัฒนาขึ้นครั้งแรก สำหรับอุปกรณ์ USB แต่หลังจากนั้น ก็ถูกนำมาใช้ผ่านโปรโตคอลอื่นๆ อีกมากมาย รวมถึงบลูทูธ

แอปพลิเคชันและอุปกรณ์ HID จะแลกเปลี่ยนข้อมูลไบนารีผ่านรายงาน 3 ประเภทดังนี้

ประเภทรายงาน คำอธิบาย
รายงานอินพุต ข้อมูลที่ส่งจากอุปกรณ์ไปยังแอปพลิเคชัน (เช่น เมื่อมีการกดปุ่ม)
รายงานเอาต์พุต ข้อมูลที่ส่งจากแอปพลิเคชันไปยังอุปกรณ์ (เช่น คำขอเปิดไฟแบ็กไลต์ของแป้นพิมพ์)
รายงานฟีเจอร์ ข้อมูลที่อาจส่งไปในทิศทางใดทิศทางหนึ่ง รูปแบบนี้จะเจาะจงอุปกรณ์

ข้อบ่งชี้รายงานอธิบายรูปแบบไบนารีของรายงานที่ฟิลด์ อุปกรณ์ โครงสร้างเป็นลำดับชั้นและสามารถจัดกลุ่มรายงานเข้าด้วยกันแยกกันได้ ภายในคอลเล็กชันระดับบนสุด รูปแบบของข้อบ่งชี้คือ ตามข้อกำหนดของ HID

การใช้งาน HID คือค่าตัวเลขที่อ้างอิงอินพุตหรือเอาต์พุตมาตรฐาน ค่าการใช้งานช่วยให้อุปกรณ์อธิบายการใช้งานตามจุดประสงค์ของอุปกรณ์และ ของแต่ละช่องในรายงาน เช่น หมายเลขหนึ่งกำหนดไว้สำหรับทางซ้าย ปุ่มเมาส์ การใช้งานยังมีการจัดไว้ในหน้าการใช้งาน ซึ่งให้ ถึงหมวดหมู่ระดับสูงของอุปกรณ์หรือรายงาน

การใช้ WebHID API

การตรวจหาฟีเจอร์

หากต้องการตรวจสอบว่าระบบรองรับ WebHID API หรือไม่ ให้ใช้รายการต่อไปนี้

if ("hid" in navigator) {
  // The WebHID API is supported.
}

เปิดการเชื่อมต่อ HID

WebHID API ออกแบบมาให้ทำงานแบบไม่พร้อมกันเพื่อป้องกันไม่ให้ UI ของเว็บไซต์ บล็อกขณะรออินพุต ซึ่งมีความสำคัญเพราะสามารถรับข้อมูล HID ได้ ในเวลาใดก็ได้ และจำเป็นต้องมีวิธีฟัง วิดีโอ

หากต้องการเปิดการเชื่อมต่อ HID ก่อนอื่นให้เข้าถึงออบเจ็กต์ HIDDevice สำหรับขั้นตอนนี้ คุณสามารถ แจ้งให้ผู้ใช้เลือกอุปกรณ์ด้วยการโทร navigator.hid.requestDevice() หรือเลือกจาก navigator.hid.getDevices() ซึ่งจะแสดงรายการอุปกรณ์ที่ได้รับสิทธิ์เข้าถึงเว็บไซต์ ก่อนหน้านี้

ฟังก์ชัน navigator.hid.requestDevice() รับออบเจ็กต์ที่จำเป็นซึ่ง กำหนดตัวกรอง รหัสผ่านเหล่านี้ใช้เพื่อจับคู่อุปกรณ์ที่เชื่อมต่อกับผู้ให้บริการ USB ตัวระบุ (vendorId), ตัวระบุผลิตภัณฑ์ USB (productId) หน้าการใช้งาน (usagePage) และค่าการใช้งาน (usage) โดยคุณสามารถดึงข้อมูลเหล่านั้นได้จาก ที่เก็บ USB ID และเอกสารตารางการใช้งาน 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 ในเว็บไซต์
ข้อความแจ้งผู้ใช้ให้เลือก Joy-Con ของ Nintendo Switch

คุณยังใช้คีย์ 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 สีแดงและน้ำเงิน
อุปกรณ์ Nintendo Switch Joy-Con

จากตัวอย่างก่อนหน้านี้ โค้ดด้านล่างจะแสดงให้คุณเห็นวิธีตรวจหา ปุ่มที่ผู้ใช้กดบนอุปกรณ์ 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]}.`);
});

ส่งรายงานเอาต์พุต

หากต้องการส่งรายงานเอาต์พุตไปยังอุปกรณ์ 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() พร้อมใช้งานใน Chrome 100 ขึ้นไป โปรดตรวจสอบว่าฟีเจอร์นี้ ที่รองรับรายการต่อไปนี้

if ("hid" in navigator && "forget" in HIDDevice.prototype) {
  // forget() is supported.
}

เคล็ดลับสำหรับนักพัฒนาซอฟต์แวร์

การแก้ไขข้อบกพร่อง HID ใน Chrome เป็นเรื่องง่ายด้วยหน้าเว็บภายใน about://device-log ที่คุณสามารถดูเหตุการณ์เกี่ยวกับอุปกรณ์ HID และ USB ทั้งหมดได้ในที่เดียว

วันที่ ภาพหน้าจอของหน้าเว็บภายในเพื่อแก้ไขข้อบกพร่อง HID
หน้าภายในใน Chrome เพื่อแก้ไขข้อบกพร่อง HID

ดูโปรแกรมสำรวจ HID สำหรับการทิ้งอุปกรณ์ 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 โดยใช้ หลักการที่กำหนดไว้ในการควบคุมการเข้าถึงฟีเจอร์แพลตฟอร์มเว็บที่มีประสิทธิภาพ ซึ่งรวมถึงการควบคุมของผู้ใช้ ความโปร่งใส และการยศาสตร์ ความสามารถในการใช้ฟีเจอร์นี้ API มีการปิดกั้นโดยโมเดลสิทธิ์ที่ให้สิทธิ์เข้าถึงแก่โดเมนเดียวเท่านั้น ครั้งละ HID ในการตอบสนองต่อข้อความแจ้งผู้ใช้ ผู้ใช้ต้องมีสถานะใช้งาน ขั้นตอนในการเลือกอุปกรณ์ HID ที่เฉพาะเจาะจง

หากต้องการทำความเข้าใจเกี่ยวกับข้อดีและข้อเสียของความปลอดภัย โปรดดูที่ความปลอดภัยและความเป็นส่วนตัว ส่วนข้อควรพิจารณาของข้อกำหนด WebHID

นอกจากนี้ Chrome ยังตรวจสอบการใช้งานคอลเล็กชันระดับบนสุดแต่ละรายการ คอลเล็กชันระดับบนสุดมีการใช้งานที่มีการป้องกัน (เช่น แป้นพิมพ์ทั่วไป เมาส์) แล้ว เว็บไซต์จะไม่สามารถส่งและรับรายงานใดๆ ที่ระบุใน คอลเล็กชัน รายการการใช้งานที่มีการคุ้มครองทั้งหมดนี้จะพร้อมใช้งานแบบสาธารณะ

โปรดทราบว่าอุปกรณ์ HID ที่มีความละเอียดอ่อนต่อความปลอดภัย (เช่น อุปกรณ์ FIDO HID ที่ใช้สำหรับ การตรวจสอบสิทธิ์ที่รัดกุมยิ่งขึ้น) จะถูกบล็อกใน Chrome ด้วย โปรดดูรายการที่บล็อก USB และ ไฟล์รายการ HID ที่บล็อก

ความคิดเห็น

ทีม Chrome อยากทราบความคิดเห็นและประสบการณ์ของคุณเกี่ยวกับ WebHID API

บอกเราเกี่ยวกับการออกแบบ API

มีบางอย่างเกี่ยวกับ API ที่ไม่ทำงานตามที่คาดไว้หรือไม่ หรือมี วิธีการหรือคุณสมบัติที่จำเป็นไม่ครบที่คุณต้องการนำไอเดียไปปฏิบัติ

แจ้งปัญหาเกี่ยวกับที่เก็บ WebHID API GitHub หรือเพิ่มความเห็น กับปัญหาที่มีอยู่

รายงานปัญหาเกี่ยวกับการติดตั้งใช้งาน

คุณพบข้อบกพร่องในการติดตั้งใช้งาน Chrome ไหม หรือเป็นการติดตั้งใช้งาน แตกต่างจากข้อกำหนด

โปรดดูวิธีรายงานข้อบกพร่อง WebHID ตรวจสอบว่าได้ใส่ ให้ละเอียดที่สุด บอกวิธีการง่ายๆ ในการจำลองข้อผิดพลาดซ้ำ ตั้งค่าคอมโพเนนต์เป็น Blink>HID Glitch เหมาะสำหรับ แชร์ตัวอย่างผลงานที่ง่ายและรวดเร็ว

แสดงการสนับสนุน

คุณวางแผนที่จะใช้ WebHID API ไหม การสนับสนุนสาธารณะของคุณจะช่วยให้ Chrome ให้ทีมจัดลำดับความสำคัญของคุณลักษณะต่างๆ และแสดงให้ผู้ให้บริการเบราว์เซอร์รายอื่นเห็นว่ามันสำคัญมากแค่ไหน สนับสนุนพวกเขา

ส่งทวีตไปยัง @ChromiumDev โดยใช้แฮชแท็ก #WebHID และแจ้งให้เราทราบ คุณใช้งานที่ไหนและอย่างไร

ลิงก์ที่มีประโยชน์

กิตติกรรมประกาศ

ขอขอบคุณ Matt Reynolds และ Joe Medley สำหรับการรีวิวบทความนี้ รูปภาพ Nintendo Switch สีแดงและน้ำเงินโดย Sara Kurfeß และแล็ปท็อปสีดำและเงิน รูปภาพจากคอมพิวเตอร์โดย Athul Cyriac Ajay ใน Unsplash