WebHID API ช่วยให้เว็บไซต์เข้าถึงแป้นพิมพ์เสริมและเกมแพดแปลกๆ ได้
มีอุปกรณ์ที่โต้ตอบกับมนุษย์โดยตรง (HID) จำนวนมาก เช่น แป้นพิมพ์ทางเลือกหรือเกมแพดแปลกๆ ที่ใหม่เกินไป เก่าเกินไป หรือหายากเกินไปที่จะเข้าถึงได้จากโปรแกรมควบคุมอุปกรณ์ของระบบ WebHID API ช่วยแก้ปัญหานี้ด้วยวิธีใช้ตรรกะที่เจาะจงอุปกรณ์ใน JavaScript
กรณีการใช้งานที่แนะนํา
อุปกรณ์ HID จะรับข้อมูลจากหรือแสดงผลลัพธ์ต่อมนุษย์ ตัวอย่างอุปกรณ์ ได้แก่ แป้นพิมพ์ อุปกรณ์ชี้ตำแหน่ง (เมาส์ ทัชสกรีน ฯลฯ) และเกมแพด โปรโตคอล HID ช่วยให้เข้าถึงอุปกรณ์เหล่านี้ในคอมพิวเตอร์เดสก์ท็อปได้โดยใช้ไดรเวอร์ระบบปฏิบัติการ แพลตฟอร์มเว็บรองรับอุปกรณ์ HID โดยอาศัยไดรเวอร์เหล่านี้
การที่เข้าถึงอุปกรณ์ HID ที่ไม่ค่อยพบได้ทั่วไปไม่ได้นั้นเป็นเรื่องที่น่าปวดหัวอย่างยิ่งเมื่อพูดถึงแป้นพิมพ์เสริมทางเลือก (เช่น Elgato Stream Deck, ชุดหูฟัง Jabra, X-keys) และการสนับสนุนเกมแพดแปลกๆ เกมแพดที่ออกแบบมาสำหรับเดสก์ท็อปมักใช้ HID สำหรับอินพุต (ปุ่ม จอยสติ๊ก ไกด์) และเอาต์พุต (LED การสั่น) ของเกมแพด ขออภัย อินพุตและเอาต์พุตของเกมแพดไม่ได้มาตรฐานมากนัก และเว็บเบราว์เซอร์มักต้องใช้ตรรกะที่กำหนดเองสำหรับอุปกรณ์บางรุ่น ซึ่งไม่ยั่งยืนและส่งผลให้การสนับสนุนอุปกรณ์รุ่นเก่าและอุปกรณ์ที่ไม่ค่อยมีคนใช้มีคุณภาพต่ำ และยังทําให้เบราว์เซอร์ต้องอาศัยลักษณะการทำงานที่แปลกประหลาดของอุปกรณ์บางรุ่นด้วย
คำศัพท์
HID ประกอบด้วยแนวคิดพื้นฐาน 2 อย่าง ได้แก่ รายงานและตัวระบุรายงาน รายงานคือข้อมูลที่แลกเปลี่ยนระหว่างอุปกรณ์กับไคลเอ็นต์ซอฟต์แวร์ ตัวระบุรายงานจะอธิบายรูปแบบและความหมายของข้อมูลที่อุปกรณ์รองรับ
HID (อุปกรณ์ที่โต้ตอบกับมนุษย์โดยตรง) คืออุปกรณ์ประเภทหนึ่งที่รับข้อมูลจากหรือแสดงผลลัพธ์ต่อมนุษย์ และยังหมายถึงโปรโตคอล HID ซึ่งเป็นมาตรฐานการสื่อสารแบบ 2 ทิศทางระหว่างโฮสต์กับอุปกรณ์ที่ออกแบบมาเพื่อลดความซับซ้อนของกระบวนการติดตั้ง โปรโตคอล HID พัฒนาขึ้นเพื่ออุปกรณ์ USB แต่ต่อมามีการใช้งานในโปรโตคอลอื่นๆ อีกมากมาย รวมถึงบลูทูธ
แอปพลิเคชันและอุปกรณ์ HID จะแลกเปลี่ยนข้อมูลไบนารีผ่านรายงาน 3 ประเภท ได้แก่
ประเภทรายงาน | คำอธิบาย |
---|---|
รายงานอินพุต | ข้อมูลที่ส่งจากอุปกรณ์ไปยังแอปพลิเคชัน (เช่น การกดปุ่ม) |
รายงานเอาต์พุต | ข้อมูลที่ส่งจากแอปพลิเคชันไปยังอุปกรณ์ (เช่น คำขอเปิดไฟแบ็กไลต์ของแป้นพิมพ์) |
รายงานฟีเจอร์ | ข้อมูลที่อาจส่งในทิศทางใดก็ได้ รูปแบบจะแตกต่างกันไปตามอุปกรณ์ |
ตัวบ่งชี้รายงานจะอธิบายรูปแบบไบนารีของรายงานที่อุปกรณ์รองรับ โครงสร้างของรายงานเป็นแบบลําดับชั้นและสามารถจัดกลุ่มรายงานเป็นคอลเล็กชันที่แยกต่างหากภายในคอลเล็กชันระดับบนสุด รูปแบบของตัวบ่งชี้จะกำหนดโดยข้อกำหนด HID
การใช้งาน HID คือค่าตัวเลขที่อ้างอิงอินพุตหรือเอาต์พุตมาตรฐาน ค่าการใช้งานช่วยให้อุปกรณ์อธิบายวัตถุประสงค์ในการใช้งานอุปกรณ์และวัตถุประสงค์ของช่องแต่ละช่องในรายงานได้ เช่น กำหนด 1 รายการสำหรับปุ่มซ้ายของเมาส์ นอกจากนี้ ระบบยังจัดระเบียบการใช้งานเป็นหน้าการใช้งาน ซึ่งระบุหมวดหมู่ระดับสูงของอุปกรณ์หรือรายงาน
การใช้ 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 และเอกสารตารางการใช้งาน 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 ขวาเพื่อให้คุณลองทำที่บ้านได้
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()
Promise ที่แสดงผลจะยุติเมื่อส่งรายงานแล้ว หากอุปกรณ์ 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 เพื่อถ่ายโอนข้อมูลอุปกรณ์ HID ในรูปแบบที่มนุษย์อ่านได้ โดยจะจับคู่จากค่าการใช้งานกับชื่อสําหรับการใช้งาน HID แต่ละรายการ
ในระบบ Linux ส่วนใหญ่ ระบบจะแมปอุปกรณ์ HID ด้วยสิทธิ์อ่านอย่างเดียวโดยค่าเริ่มต้น หากต้องการอนุญาตให้ Chrome เปิดอุปกรณ์ HID คุณจะต้องเพิ่ม udev
rule ใหม่ สร้างไฟล์ที่ /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 ได้ครั้งละ 1 เครื่องเท่านั้น ผู้ใช้ต้องดำเนินการตามขั้นตอนเพื่อเลือกอุปกรณ์ HID ที่ต้องการเพื่อตอบสนองต่อข้อความแจ้ง
หากต้องการทำความเข้าใจข้อเสียด้านความปลอดภัย โปรดดูส่วนข้อควรพิจารณาด้านความปลอดภัยและความเป็นส่วนตัวของข้อกำหนด WebHID
นอกจากนี้ Chrome จะตรวจสอบการใช้งานของคอลเล็กชันระดับบนสุดแต่ละรายการ และหากคอลเล็กชันระดับบนสุดมีการใช้งานที่ได้รับการปกป้อง (เช่น แป้นพิมพ์ เมาส์ทั่วไป) เว็บไซต์จะไม่สามารถส่งและรับรายงานที่กําหนดไว้ในคอลเล็กชันนั้นได้ รายการการใช้งานที่ได้รับการคุ้มครองทั้งหมดเผยแพร่ต่อสาธารณะ
โปรดทราบว่า Chrome จะบล็อกอุปกรณ์ HID ที่มีความละเอียดอ่อนด้านความปลอดภัย (เช่น อุปกรณ์ FIDO HID ที่ใช้สำหรับการตรวจสอบสิทธิ์ที่รัดกุมยิ่งขึ้น) ด้วย ดูไฟล์รายการที่บล็อก USB และรายการที่บล็อก HID
ความคิดเห็น
ทีม Chrome อยากทราบความคิดเห็นและประสบการณ์ของคุณเกี่ยวกับ WebHID API
บอกเราเกี่ยวกับการออกแบบ API
มีสิ่งใดเกี่ยวกับ API ที่ไม่ทำงานตามที่คาดไว้ไหม หรือมีเมธอดหรือพร็อพเพอร์ตี้ที่ขาดหายไปซึ่งคุณต้องนำไปใช้กับไอเดียของคุณ
แจ้งปัญหาเกี่ยวกับข้อกำหนดใน ที่เก็บ GitHub ของ WebHID API หรือแสดงความคิดเห็นเกี่ยวกับปัญหาที่มีอยู่
รายงานปัญหาเกี่ยวกับการติดตั้งใช้งาน
หากพบข้อบกพร่องในการใช้งาน Chrome หรือการใช้งานแตกต่างจากข้อกําหนดหรือไม่
ดูวิธีรายงานข้อบกพร่องของ WebHID โปรดระบุรายละเอียดให้มากที่สุด ระบุวิธีการง่ายๆ ในการจำลองข้อบกพร่อง และตั้งค่าคอมโพเนนต์เป็น Blink>HID
Glitch เหมาะอย่างยิ่งสำหรับการแชร์การจำลองข้อบกพร่องที่รวดเร็วและง่ายดาย
แสดงการสนับสนุน
คุณกำลังวางแผนที่จะใช้ WebHID API ใช่ไหม การสนับสนุนแบบสาธารณะของคุณจะช่วยให้ทีม Chrome จัดลำดับความสำคัญของฟีเจอร์ต่างๆ และแสดงให้เห็นว่าการสนับสนุนฟีเจอร์เหล่านี้สำคัญเพียงใดต่อผู้ให้บริการเบราว์เซอร์รายอื่นๆ
ส่งทวีตถึง @ChromiumDev โดยใช้แฮชแท็ก #WebHID
และแจ้งให้เราทราบถึงตำแหน่งและวิธีใช้
ลิงก์ที่มีประโยชน์
- ข้อกำหนด
- ข้อบกพร่องการติดตาม
- รายการ ChromeStatus.com
- คอมโพเนนต์ Blink:
Blink>HID
ขอขอบคุณ
ขอขอบคุณ Matt Reynolds และ Joe Medley ที่ตรวจสอบบทความนี้ รูปภาพ Nintendo Switch สีแดงและน้ำเงินโดย Sara Kurfeß และรูปภาพแล็ปท็อปและคอมพิวเตอร์สีดำและสีเงินโดย Athul Cyriac Ajay ใน Unsplash