A API WebHID permite que os sites acessem teclados auxiliares alternativos e gamepads exóticos.
Há uma longa cauda de dispositivos de interface humana (HIDs), como teclados alternativos ou gamepads exóticos, que são muito novos, muito antigos ou muito incomuns para serem acessados pelos drivers de dispositivo dos sistemas. A API WebHID resolve esse problema fornecendo uma maneira de implementar a lógica específica do dispositivo no JavaScript.
Casos de uso sugeridos
Um dispositivo HID recebe entrada ou fornece saída para humanos. Exemplos de dispositivos incluem teclados, dispositivos apontadores (mouses, touchscreens etc.) e gamepads. O protocolo HID permite acessar esses dispositivos em computadores desktop usando drivers do sistema operacional. A plataforma da Web oferece suporte a dispositivos HID dependendo desses drivers.
A incapacidade de acessar dispositivos HID incomuns é particularmente dolorosa quando se trata de teclados auxiliares alternativos (por exemplo, Elgato Stream Deck, fones de ouvido Jabra, X-keys) e suporte a gamepads exóticos. Os gamepads projetados para computadores geralmente usam HID para entradas (botões, joysticks, gatilhos) e saídas (LEDs, vibração). Infelizmente, as entradas e saídas do gamepad não são bem padronizadas, e os navegadores da Web geralmente exigem uma lógica personalizada para dispositivos específicos. Isso é insustentável e resulta em um suporte ruim para a cauda longa de dispositivos mais antigos e incomuns. Isso também faz com que o navegador dependa de peculiaridades no comportamento de dispositivos específicos.
Terminologia
O HID consiste em dois conceitos fundamentais: relatórios e descritores de relatórios. Os relatórios são os dados trocados entre um dispositivo e um cliente de software. O descritor de relatório descreve o formato e o significado dos dados com suporte do dispositivo.
Um dispositivo de interface humana (HID) é um tipo de dispositivo que recebe entrada ou oferece saída para humanos. Ele também se refere ao protocolo HID, um padrão de comunicação bidirecional entre um host e um dispositivo projetado para simplificar o procedimento de instalação. O protocolo HID foi originalmente desenvolvido para dispositivos USB, mas desde então foi implementado em muitos outros protocolos, incluindo o Bluetooth.
Os aplicativos e dispositivos HID trocam dados binários por três tipos de relatório:
Tipo de relatório | Descrição |
---|---|
Relatório de entrada | Dados enviados do dispositivo para o app (por exemplo, um botão é pressionado) |
Relatório de saída | Dados enviados do aplicativo para o dispositivo (por exemplo, uma solicitação para ativar o teclado com luz de fundo). |
Relatório de recursos | Dados que podem ser enviados em qualquer direção. O formato é específico do dispositivo. |
Um descritor de relatório descreve o formato binário dos relatórios aceitos pelo dispositivo. A estrutura é hierárquica e pode agrupar relatórios como coleções distintas na coleção de nível superior. O formato do descritor é definido pela especificação HID.
Um uso de HID é um valor numérico que se refere a uma entrada ou saída padronizada. Os valores de uso permitem que um dispositivo descreva o uso pretendido e a finalidade de cada campo nos relatórios. Por exemplo, um é definido para o botão esquerdo do mouse. Os usos também são organizados em páginas de uso, que fornecem uma indicação da categoria de alto nível do dispositivo ou relatório.
Como usar a API WebHID
Detecção de recursos
Para verificar se a API WebHID tem suporte, use:
if ("hid" in navigator) {
// The WebHID API is supported.
}
Abrir uma conexão HID
A API WebHID é assíncrona por design para evitar que a interface do site seja bloqueada ao aguardar entrada. Isso é importante porque os dados HID podem ser recebidos a qualquer momento, exigindo uma maneira de ouvi-los.
Para abrir uma conexão HID, primeiro acesse um objeto HIDDevice
. Para isso, você pode
pedir que o usuário selecione um dispositivo chamando
navigator.hid.requestDevice()
ou escolher um em navigator.hid.getDevices()
, que retorna uma lista de dispositivos aos quais o site teve acesso
anteriormente.
A função navigator.hid.requestDevice()
usa um objeto obrigatório que
define filtros. Eles são usados para corresponder a qualquer dispositivo conectado com um identificador
de fornecedor USB (vendorId
), um identificador de produto USB (productId
), um valor de página de uso (usagePage
) e um valor de uso (usage
). Você pode acessar esses dados no repositório de IDs USB e no documento de tabelas de uso de HID.
Os vários objetos HIDDevice
retornados por essa função representam várias
interfaces HID no mesmo 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();
Também é possível usar a chave exclusionFilters
opcional em
navigator.hid.requestDevice()
para excluir alguns dispositivos do seletor de navegadores
que não estão funcionando corretamente, por exemplo.
// 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 }],
});
Um objeto HIDDevice
contém identificadores de fornecedor e de produto USB para identificação
do dispositivo. O atributo collections
é inicializado com uma descrição hierárquica
dos formatos de relatório do 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
}
Por padrão, os dispositivos HIDDevice
são retornados em um estado "fechado" e precisam ser
abertos chamando open()
antes que os dados possam ser enviados ou recebidos.
// Wait for the HID connection to open before sending/receiving data.
await device.open();
Receber relatórios de entrada
Depois que a conexão HID for estabelecida, você poderá processar relatórios de entrada
de entrada detectando os eventos "inputreport"
do dispositivo. Esses eventos
contêm os dados HID como um objeto DataView
(data
), o dispositivo HID a que ele pertence (device
) e o ID de relatório de 8 bits associado ao relatório de entrada
(reportId
).
Continuando com o exemplo anterior, o código abaixo mostra como detectar qual botão o usuário pressionou em um dispositivo Joy-Con direito para que você possa testá-lo em 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]}.`);
});
Enviar relatórios de saída
Para enviar um relatório de saída a um dispositivo HID, transmita o ID de relatório de 8 bits associado
ao relatório de saída (reportId
) e os bytes como um BufferSource
(data
) para
device.sendReport()
. A promessa retornada é resolvida quando o relatório é
enviado. Se o dispositivo HID não usar IDs de relatório, defina reportId
como 0.
O exemplo abaixo se aplica a um dispositivo Joy-Con e mostra como fazer com que ele vibre com relatórios de saída.
// 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));
Enviar e receber relatórios de recursos
Os relatórios de recursos são o único tipo de relatório de dados HID que pode ser transmitido em ambos os sentidos. Elas permitem que dispositivos e aplicativos HID troquem dados HID não padronizados. Ao contrário dos relatórios de entrada e saída, os relatórios de recursos não são recebidos ou enviados pelo aplicativo regularmente.
Para enviar um relatório de recurso a um dispositivo HID, transmita o ID de relatório de 8 bits associado
ao relatório de recurso (reportId
) e os bytes como um BufferSource
(data
) para
device.sendFeatureReport()
. A promessa retornada é resolvida quando o relatório é
enviado. Se o dispositivo HID não usar IDs de relatório, defina reportId
como 0.
O exemplo abaixo ilustra o uso de relatórios de recursos mostrando como solicitar, abrir e fazer piscar um dispositivo de luz de fundo de teclado da 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);
}
Para receber um relatório de recurso de um dispositivo HID, transmita o ID de relatório de 8 bits
associado ao relatório de recurso (reportId
) para
device.receiveFeatureReport()
. A promessa retornada é resolvida com um objeto
DataView
que contém o conteúdo do relatório de recurso. Se o dispositivo HID
não usar IDs de relatório, defina reportId
como 0.
// Request feature report.
const dataView = await device.receiveFeatureReport(/* reportId= */ 1);
// Read feature report contents with dataView.getInt8(), getUint8(), etc...
Ouvir a conexão e a desconexão
Quando o site recebe permissão para acessar um dispositivo HID, ele pode
receber ativamente eventos de conexão e desconexão ao ouvir eventos "connect"
e "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.
});
Revogar o acesso a um dispositivo HID
O site pode limpar as permissões para acessar um dispositivo HID que não é mais
interessante manter chamando forget()
na instância HIDDevice
. Por
exemplo, em um aplicativo educacional da Web usado em um computador compartilhado com muitos
dispositivos, um grande número de permissões geradas pelo usuário acumuladas cria uma experiência
de usuário ruim.
Chamar forget()
em uma única instância de HIDDevice
revoga o acesso a todas
as interfaces HID no mesmo dispositivo físico.
// Voluntarily revoke access to this HID device.
await device.forget();
Como forget()
está disponível no Chrome 100 ou versões mais recentes, verifique se esse recurso tem
suporte com o seguinte:
if ("hid" in navigator && "forget" in HIDDevice.prototype) {
// forget() is supported.
}
Dicas para desenvolvedores
É fácil depurar HID no Chrome com a página interna about://device-log
, em que você pode conferir todos os eventos relacionados a dispositivos HID e USB em um único lugar.
Confira o explorador HID para despejar informações do dispositivo HID em um formato legível. Ele mapeia os valores de uso para nomes de cada uso de HID.
Na maioria dos sistemas Linux, os dispositivos HID são mapeados com permissões de leitura somente
por padrão. Para permitir que o Chrome abra um dispositivo HID, adicione uma nova regra
udev. Crie um arquivo em /etc/udev/rules.d/50-yourdevicename.rules
com o
seguinte conteúdo:
KERNEL=="hidraw*", ATTRS{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
Na linha acima, [yourdevicevendor]
é 057e
se o dispositivo for um Nintendo Switch
Joy-Con, por exemplo. ATTRS{idProduct}
também pode ser adicionado para uma regra mais específica. Verifique se o user
é membro do grupo plugdev
. Depois, reconecte o dispositivo.
Suporte ao navegador
A API WebHID está disponível em todas as plataformas de computador (ChromeOS, Linux, macOS e Windows) no Chrome 89.
Demonstrações
Algumas demonstrações do WebHID estão listadas em web.dev/hid-examples. Confira!
Segurança e privacidade
Os autores da especificação projetaram e implementaram a API WebHID usando os princípios principais definidos em Como controlar o acesso a recursos poderosos da plataforma da Web, incluindo controle do usuário, transparência e ergonomia. A capacidade de usar essa API é principalmente restrita por um modelo de permissão que concede acesso a apenas um dispositivo HID por vez. Em resposta a um comando do usuário, ele precisa realizar etapas ativas para selecionar um dispositivo HID específico.
Para entender as compensações de segurança, consulte a seção Considerações de segurança e privacidade da especificação do WebHID.
Além disso, o Chrome inspeciona o uso de cada coleção de nível superior. Se uma coleção de nível superior tiver um uso protegido (por exemplo, teclado genérico, mouse), um site não poderá enviar e receber relatórios definidos nessa coleção. A lista completa de usos protegidos está disponível publicamente.
Dispositivos HID sensíveis à segurança, como dispositivos HID FIDO usados para autenticação mais forte, também são bloqueados no Chrome. Consulte os arquivos de lista de bloqueio do USB e lista de bloqueio do HID.
Feedback
A equipe do Chrome quer saber sua opinião e experiência com a API WebHID.
Conte sobre o design da API
Há algo na API que não funciona como esperado? Ou há métodos ou propriedades ausentes que você precisa implementar sua ideia?
Envie um problema de especificação no repositório do GitHub da API WebHID ou adicione suas ideias a um problema existente.
Informar um problema com a implementação
Você encontrou um bug na implementação do Chrome? Ou a implementação é diferente da especificação?
Confira Como registrar bugs do WebHID. Inclua o máximo de
detalhes possível, forneça instruções simples para reproduzir o bug e defina
Components como Blink>HID
. O Glitch é ótimo para
compartilhar reprosagens rápidas e fáceis.
Mostrar apoio
Você planeja usar a API WebHID? Seu apoio público ajuda a equipe do Chrome a priorizar recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.
Envie um tweet para @ChromiumDev usando a hashtag
#WebHID
e diga onde e como você está usando o recurso.
Links úteis
- Especificação
- Rastreamento de bugs
- Entrada do ChromeStatus.com
- Componente do Blink:
Blink>HID
Agradecimentos
Agradecemos a Matt Reynolds e Joe Medley pelas revisões deste artigo. Foto do Nintendo Switch vermelho e azul de Sara Kurfeß e foto de um computador laptop preto e prata de Athul Cyriac Ajay no Unsplash.