Conexión a dispositivos HID poco comunes

La API de WebHID permite que los sitios web accedan a teclados auxiliares alternativos y controles de juegos exóticos.

François Beaufort
François Beaufort

Existe una gran variedad de dispositivos de interfaz humana (HID), como teclados o controles de juegos exóticos, que son demasiado nuevos, demasiado antiguos o demasiado poco comunes accesibles a través de sistemas controladores de dispositivos. La API de WebHID resuelve esto proporcionando un de implementar la lógica específica del dispositivo en JavaScript.

Casos de uso sugeridos

Un dispositivo HID toma entradas de personas o les proporciona salidas. Ejemplos de dispositivos incluyen teclados, dispositivos apuntadores (mouse, pantallas táctiles, etc.) y controles de juegos. El protocolo HID permite acceder a estos dispositivos en computadoras de escritorio computadoras con controladores de sistema operativo. La plataforma web admite dispositivos HID basándose en estos controladores.

La imposibilidad de acceder a dispositivos HID poco comunes es particularmente problemática cuando incluye teclados auxiliares alternativos (p.ej., Elgato Stream Deck, Jabra auriculares, teclas X) y un control de juegos exótico. Controles de juegos diseñados para computadoras a menudo usan HID para entradas y salidas del control de mando (botones, joysticks, gatillos, etc.). (LED, ruido). Por desgracia, las entradas y salidas del control de mando no funcionan correctamente. los navegadores web y estandarizados suelen requerir una lógica personalizada para dispositivos específicos. Esto es insostenible y genera un mal apoyo para la cola de personas mayores dispositivos poco comunes. También provoca que el navegador dependa de algunas particularidades en el comportamiento de dispositivos específicos.

Terminología

HID consta de dos conceptos fundamentales: informes y descriptores de informes. Los informes son los datos que se intercambian entre un dispositivo y un cliente de software. El descriptor de informes describe el formato y el significado de los datos que el dispositivo admite.

Un dispositivo de interfaz humana (HID) es un tipo de dispositivo que recibe entradas o genera resultados a las personas. También se refiere al protocolo HID, un estándar para y bidireccional entre un host y un dispositivo que está diseñado para simplificar el procedimiento de instalación. El protocolo HID se desarrolló originalmente. para dispositivos USB, pero desde entonces se ha implementado sobre muchos otros protocolos incluido Bluetooth.

Las aplicaciones y los dispositivos HID intercambian datos binarios mediante tres tipos de informes:

Tipo de informe Descripción
Informe de entrada Datos que se envían del dispositivo a la aplicación (p. ej., cuando se presiona un botón)
Informe de resultados Datos que se envían de la aplicación al dispositivo (p. ej., una solicitud para encender la retroiluminación del teclado)
Informe de funciones Datos que se pueden enviar en cualquier dirección. El formato es específico del dispositivo.

Un descriptor de informes describe el formato binario de los informes que admite la dispositivo. Su estructura es jerárquica y puede agrupar informes como datos colecciones dentro de la colección de nivel superior. El formato del descriptor es según la especificación HID.

El uso de HID es un valor numérico que hace referencia a una entrada o salida estandarizadas. Los valores de uso permiten que un dispositivo describa el uso previsto del dispositivo y la de cada campo en sus informes. Por ejemplo, se define uno para el lado izquierdo botón de un mouse. Los usos también se organizan en páginas de uso, que proporcionan una indicación de la categoría de alto nivel del dispositivo o el informe.

Usa la API de WebHID

Detección de funciones

Para comprobar si la API de WebHID es compatible, usa lo siguiente:

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

Cómo abrir una conexión HID

La API de WebHID es asíncrona por diseño para evitar que la IU del sitio web el bloqueo cuando se espera una entrada. Esto es importante porque los datos HID pueden recibirse en cualquier momento y requiere una forma de escucharlos.

Para abrir una conexión HID, primero accede a un objeto HIDDevice. Para ello, puedes puedes pedirle al usuario que seleccione un dispositivo llamando navigator.hid.requestDevice() o elige uno de navigator.hid.getDevices() Esta acción muestra una lista de los dispositivos a los que tiene acceso el sitio web anteriormente.

La función navigator.hid.requestDevice() toma un objeto obligatorio que define filtros. Se usan para hacer coincidir cualquier dispositivo conectado a un proveedor USB identificador (vendorId), un identificador de producto USB (productId) y una página de uso valor (usagePage) y un valor de uso (usage). Puedes obtenerlas de la Repositorio de ID de USB y documento de tablas de uso de HID.

Los distintos objetos HIDDevice que muestra esta función representan varios Interfaces HID en el mismo dispositivo físico.

// 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();
Captura de pantalla de un mensaje de dispositivo HID en un sitio web.
Mensaje para que el usuario seleccione un Joy-Con de Nintendo Switch.

También puedes usar la clave exclusionFilters opcional en navigator.hid.requestDevice() para excluir algunos dispositivos del selector del navegador que se sabe que funcionan mal.

// 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 }],
});

Un objeto HIDDevice contiene identificadores de productos y proveedores USB para dispositivos y la identificación de riesgos. Su atributo collections se inicializa con un código jerárquico descripción de los formatos de informe del dispositivo.

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
}

Los dispositivos HIDDevice se devuelven de forma predeterminada en un y debe ser abierto llamando a open() antes de que los datos se puedan enviar o recibir.

// Wait for the HID connection to open before sending/receiving data.
await device.open();

Cómo recibir informes de entrada

Una vez que se haya establecido la conexión HID, podrás controlar las entradas entrantes. informes escuchando los eventos "inputreport" del dispositivo. Esos eventos contener los datos HID como un objeto DataView (data), el dispositivo HID al que pertenece a (device) y el ID del informe de 8 bits asociado con el informe de entrada reportId).

Foto roja y azul del interruptor de Nintendo.
Dispositivos Nintendo Switch Joy-Con

Siguiendo con el ejemplo anterior, el siguiente código te muestra cómo detectar qué botón presionó el usuario en un dispositivo Joy-Con Right para que puedas con suerte en casa.

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

Cómo enviar informes de resultados

Para enviar un informe de salida a un dispositivo HID, pasa el ID del informe de 8 bits asociado. con el informe de resultados (reportId) y bytes como BufferSource (data) a device.sendReport() La promesa que se muestra se resuelve una vez que se haya procesado el informe enviados. Si el dispositivo HID no usa IDs de informes, establece reportId en 0.

El siguiente ejemplo se aplica a un dispositivo Joy-Con y te muestra cómo crear uno los informes de salida.

// 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));

Envía y recibe informes de funciones

Los informes de funciones son el único tipo de informes de datos HID que pueden aparecer tanto en cómo llegar a un lugar. Permiten que los dispositivos y las aplicaciones HID intercambien información Datos HID. A diferencia de los informes de entrada y salida, los informes de atributos no se reciben que envía la aplicación con regularidad.

Foto de una laptop en negro y plateada.
Teclado de la laptop

Para enviar un informe de funciones a un dispositivo HID, pasa el ID del informe de 8 bits asociado. con el informe de funciones (reportId) y bytes como BufferSource (data) a device.sendFeatureReport() La promesa que se devuelve se resuelve una vez que el informe que se envió. Si el dispositivo HID no usa IDs de informes, establece reportId en 0.

El siguiente ejemplo ilustra el uso de los informes de funciones y te muestra cómo solicita un dispositivo retroiluminado con teclado Apple, ábrelo y haz que parpadee.

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);
}

Para recibir un informe de funciones de un dispositivo HID, pasa el ID del informe de 8 bits. asociado con el informe de funciones (reportId) a device.receiveFeatureReport() La promesa que se muestra se resuelve con un Es el objeto DataView que incluye el contenido del informe de funciones. Si el termostato HID el dispositivo no usa los IDs de los informes, establece reportId en 0.

// Request feature report.
const dataView = await device.receiveFeatureReport(/* reportId= */ 1);

// Read feature report contents with dataView.getInt8(), getUint8(), etc...

Escuchar conexión y desconexión

Cuando el sitio web obtiene permiso para acceder a un dispositivo HID, puede recibir activamente eventos de conexión y desconexión escuchando a "connect" y "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.
});

Cómo revocar el acceso a un dispositivo HID

El sitio web puede limpiar los permisos para acceder a un dispositivo HID que ya no es interesado en retener llamando a forget() en la instancia HIDDevice. Para ejemplo, para una aplicación web educativa que se utiliza en una computadora compartida con muchos una gran cantidad de permisos acumulados generados por el usuario crea la experiencia del usuario.

Si llamas a forget() en una sola instancia de HIDDevice, se revocará el acceso a todas las interfaces HID en el mismo dispositivo físico.

// Voluntarily revoke access to this HID device.
await device.forget();

Dado que forget() está disponible en Chrome 100 o versiones posteriores, verifica si esta función está disponible compatibles con lo siguiente:

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

Sugerencias para desarrolladores

La depuración de HID en Chrome es sencilla gracias a la página interna about://device-log. donde puedes ver todos los eventos relacionados con dispositivos HID y USB en un solo lugar.

Captura de pantalla de la página interna para depurar HID.
Página interna en Chrome para depurar HID

Consulta el explorador HID para volcar los dispositivos HID. información en un formato legible por humanos. Asigna de los valores de uso a los nombres de cada uno Uso de HID.

En la mayoría de los sistemas Linux, los dispositivos HID se asignan con permisos de solo lectura por de forma predeterminada. Para permitir que Chrome abra un dispositivo HID, deberás agregar un nuevo udev estándar. Crea un archivo en /etc/udev/rules.d/50-yourdevicename.rules con el siguiente contenido:

KERNEL=="hidraw*", ATTRS{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"

En la línea anterior, [yourdevicevendor] es 057e si tu dispositivo es una Nintendo Switch Joy-Con, por ejemplo. También puedes agregar ATTRS{idProduct} para obtener . Asegúrate de que tu user sea miembro del grupo plugdev. Luego, solo tienes que vuelve a conectar el dispositivo.

Navegadores compatibles

La API de WebHID está disponible en todas las plataformas de escritorio (ChromeOS, Linux, macOS, y Windows) en Chrome 89.

Demostraciones

Puedes encontrar algunas demostraciones de WebHID en web.dev/hid-examples. ¡Échale un vistazo!

Seguridad y privacidad

Los autores de las especificaciones diseñaron e implementaron la API de WebHID con el SDK principal que se definen en Controla el acceso a las funciones potentes de la plataforma web, incluidos el control de usuario, la transparencia y la ergonomía. La capacidad de usar La API está restringida principalmente por un modelo de permisos que otorga acceso a un solo de a un dispositivo HID a la vez. En respuesta a la solicitud del usuario, este debe tomar pasos para seleccionar un dispositivo HID específico.

Para comprender las ventajas y desventajas de la seguridad, consulta el artículo Seguridad y privacidad Consideraciones de la especificación de WebHID.

Además, Chrome inspecciona el uso de cada colección de nivel superior y, si la colección de nivel superior tiene un uso protegido (p.ej., teclado genérico, mouse) y, luego, un sitio web no podrá enviar ni recibir los informes definidos en ese de elementos no utilizados. La lista completa de usos protegidos está disponible de forma pública.

Ten en cuenta que los dispositivos HID sensibles a la seguridad (como los dispositivos HID FIDO que se usan autenticación más sólida) también se bloquean en Chrome. Consulta la lista de entidades bloqueadas de USB y Archivos de la lista de entidades bloqueadas de HID

Comentarios

Al equipo de Chrome le gustaría conocer tus opiniones y experiencias con el API de WebHID

Cuéntanos sobre el diseño de la API

¿Existe algún aspecto de la API que no funcione según lo esperado? ¿O hay o propiedades faltantes que necesites para implementar tu idea?

Informa un problema de especificaciones en el repositorio de GitHub de la API de WebHID o agrega lo que piensas a un problema existente.

Informar un problema con la implementación

¿Encontraste un error en la implementación de Chrome? ¿O la implementación diferente de la especificación?

Consulta Cómo archivar errores de WebHID. Asegúrate de incluir tantos detalles como puedas, proporcionar instrucciones sencillas para reproducir el error y contar con Componentes establecidos en Blink>HID. Glitch funciona muy bien para para compartir repros rápidos y fáciles.

Demostrar apoyo

¿Planeas usar la API de WebHID? Tu apoyo público ayuda a Chrome prioriza funciones y muestra a otros proveedores de navegadores la importancia de respaldarlos.

Envía un tweet a @ChromiumDev con el hashtag #WebHID y avísanos dónde y cómo usarlos.

Vínculos útiles

Agradecimientos

Agradecemos a Matt Reynolds y Joe Medley por sus opiniones sobre este artículo. Foto de Nintendo Switch en rojo y azul de Sara Kurfeß, y una laptop en negro y plateado foto de computadora de Athul Cyriac Ajay en Unsplash.