Connexion à des périphériques HID peu courants

L'API WebHID permet aux sites Web d'accéder à d'autres claviers auxiliaires et manettes de jeu exotiques.

François Beaufort
François Beaufort

Il existe une longue traîne d'appareils à interface humaine (HID), tels que des claviers alternatifs ou des manettes de jeu exotiques, qui sont trop récents, trop anciens ou trop rares pour être accessibles par les pilotes de périphériques des systèmes. L'API WebHID résout ce problème en permettant d'implémenter une logique spécifique à l'appareil en JavaScript.

Cas d'utilisation suggérés

Un périphérique HID reçoit des entrées ou fournit une sortie aux humains. Les claviers, les dispositifs de pointage (souris, écrans tactiles, etc.) et les manettes de jeu sont des exemples de périphériques. Le protocole HID permet d'accéder à ces appareils sur des ordinateurs de bureau à l'aide de pilotes de système d'exploitation. La plate-forme Web prend en charge les périphériques HID en s'appuyant sur ces pilotes.

L'impossibilité d'accéder à des périphériques HID peu courants est particulièrement délicate lorsqu'il s'agit d'utiliser d'autres claviers auxiliaires (par exemple, Elgato Stream Deck, casques Jabra, X-keys) et la compatibilité avec les manettes de jeu exotiques. Les manettes de jeu conçues pour les ordinateurs de bureau utilisent souvent HID pour les entrées (boutons, joysticks, déclencheurs) et les sorties (LED, rumble). Malheureusement, les entrées et les sorties des manettes de jeu ne sont pas bien standardisées, et les navigateurs Web nécessitent souvent une logique personnalisée pour des appareils spécifiques. Cette approche n'est pas durable et se traduit par une compatibilité médiocre avec la longue traîne d'appareils plus anciens et peu courants. Il oblige également le navigateur à dépendre de particularités du comportement d'appareils spécifiques.

Terminologie

Les HID comprennent deux concepts fondamentaux: les rapports et les descripteurs de rapport. Les rapports sont les données qui sont échangées entre un appareil et un client logiciel. Le descripteur de rapport décrit le format et la signification des données compatibles avec l'appareil.

Un HID (Human Interface Device) est un type d'appareil qui reçoit des entrées ou fournit une sortie à des êtres humains. Il fait également référence au protocole HID, une norme de communication bidirectionnelle entre un hôte et un appareil conçue pour simplifier la procédure d'installation. Le protocole HID a été développé à l'origine pour les appareils USB, mais il a depuis été mis en œuvre sur de nombreux autres protocoles, y compris Bluetooth.

Les applications et les appareils HID échangent des données binaires par le biais de trois types de rapports:

Type de rapport Description
Rapport sur les données saisies Données envoyées de l'appareil à l'application (par exemple, lorsqu'un utilisateur appuie sur un bouton).
Rapport de sortie Données envoyées de l'application à l'appareil (par exemple, une demande d'activation du rétroéclairage du clavier).
Rapport sur les fonctionnalités Données pouvant être envoyées dans les deux sens. Le format est propre à chaque appareil.

Un descripteur de rapport décrit le format binaire des rapports pris en charge par l'appareil. Sa structure est hiérarchique et peut regrouper les rapports en tant que collections distinctes au sein de la collection de premier niveau. Le format du descripteur est défini par la spécification HID.

L'utilisation d'un HID est une valeur numérique qui fait référence à une entrée ou à une sortie standardisée. Les valeurs d'utilisation permettent à un appareil de décrire l'utilisation prévue de l'appareil et la fonction de chaque champ dans ses rapports. Par exemple, un pour le bouton gauche d'une souris. Les utilisations sont également organisées en pages d'utilisation, qui fournissent une indication de la catégorie générale de l'appareil ou du rapport.

Utiliser l'API WebHID

Détection de fonctionnalités

Pour vérifier si l'API WebHID est compatible, utilisez la commande suivante:

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

Ouvrir une connexion HID

L'API WebHID est asynchrone par nature pour éviter le blocage de l'interface utilisateur du site Web en cas d'attente d'une entrée. C'est important, car les données HID peuvent être reçues à tout moment, ce qui nécessite un moyen de les écouter.

Pour ouvrir une connexion HID, accédez d'abord à un objet HIDDevice. Pour ce faire, vous pouvez soit inviter l'utilisateur à sélectionner un appareil en appelant navigator.hid.requestDevice(), soit en choisir un dans navigator.hid.getDevices(), qui renvoie une liste des appareils auxquels le site Web a déjà été autorisé à accéder.

La fonction navigator.hid.requestDevice() utilise un objet obligatoire qui définit des filtres. Ces valeurs sont utilisées pour faire correspondre tout appareil connecté à un identifiant fournisseur USB (vendorId), à un identifiant produit USB (productId), à une valeur de page d'utilisation (usagePage) et à une valeur d'utilisation (usage). Vous pouvez les obtenir à partir du dépôt d'ID USB et du document des tableaux d'utilisation HID.

Les multiples objets HIDDevice renvoyés par cette fonction représentent plusieurs interfaces HID sur le même appareil physique.

// 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();
Capture d'écran d'une invite d'appareil HID sur un site Web.
Invite de l'utilisateur à sélectionner un Joy-Con Nintendo Switch.

Vous pouvez également utiliser la clé facultative exclusionFilters dans navigator.hid.requestDevice() pour exclure du sélecteur de navigateur certains appareils connus pour dysfonctionnements, par exemple.

// 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 objet HIDDevice contient les codes produit et de fournisseur USB pour identifier les appareils. Son attribut collections est initialisé avec une description hiérarchique des formats de rapports de l'appareil.

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
}

Par défaut, les appareils HIDDevice sont renvoyés à l'état "fermé" et doivent être ouverts en appelant open() avant que des données puissent être envoyées ou reçues.

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

Recevoir les rapports sur les contributions

Une fois la connexion HID établie, vous pouvez gérer les rapports d'entrée entrants en écoutant les événements "inputreport" depuis l'appareil. Ces événements contiennent les données HID sous la forme d'un objet DataView (data), de l'appareil HID auquel il appartient (device) et de l'ID de rapport 8 bits associé au rapport d'entrée (reportId).

Photo de Nintendo Switch rouge et bleue.
Appareils Nintendo Switch Joy-Con

Pour reprendre l'exemple précédent, le code ci-dessous vous montre comment détecter le bouton sur lequel l'utilisateur a appuyé sur un appareil Joy-Con Right pour pouvoir l'essayer chez vous.

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

Envoyer des rapports de sortie

Pour envoyer un rapport de sortie à un appareil HID, transmettez l'ID de rapport de 8 bits associé au rapport de sortie (reportId) et les octets en tant que BufferSource (data) à device.sendReport(). La promesse renvoyée est résolue une fois le rapport envoyé. Si l'appareil HID n'utilise pas d'ID de rapport, définissez reportId sur 0.

L'exemple ci-dessous s'applique à un appareil Joy-Con et vous montre comment le transformer avec des rapports de sortie.

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

Envoyer et recevoir des rapports sur les fonctionnalités

Les rapports sur les caractéristiques sont le seul type de rapports de données HID pouvant être transmis dans les deux sens. Ils permettent aux appareils et aux applications HID d'échanger des données HID non standardisées. Contrairement aux rapports d'entrée et de sortie, les rapports sur les fonctionnalités ne sont ni reçus ni envoyés régulièrement par l'application.

Photo d'ordinateur portable en noir et argent.
Clavier d'ordinateur portable

Pour envoyer un rapport sur les fonctionnalités à un appareil HID, transmettez l'ID de rapport 8 bits associé au rapport sur les fonctionnalités (reportId) et les octets en tant que BufferSource (data) à device.sendFeatureReport(). La promesse renvoyée est résolue une fois le rapport envoyé. Si l'appareil HID n'utilise pas d'ID de rapport, définissez reportId sur 0.

L'exemple ci-dessous illustre l'utilisation des rapports de fonctionnalités en vous montrant comment demander un appareil de rétroéclairage du clavier Apple, l'ouvrir et le faire clignoter.

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

Pour recevoir un rapport sur les fonctionnalités d'un appareil HID, transmettez l'ID de rapport 8 bits associé au rapport sur les fonctionnalités (reportId) à device.receiveFeatureReport(). La promesse renvoyée est résolue avec un objet DataView qui contient le contenu du rapport sur les fonctionnalités. Si l'appareil HID n'utilise pas d'ID de rapport, définissez reportId sur 0.

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

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

Écouter la connexion et la déconnexion

Lorsque le site Web a été autorisé à accéder à un appareil HID, il peut recevoir activement les événements de connexion et de déconnexion en écoutant les événements "connect" et "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.
});

Révoquer l'accès d'un appareil HID

Le site Web peut nettoyer les autorisations d'accès à un appareil HID qu'il ne souhaite plus conserver en appelant forget() sur l'instance HIDDevice. Par exemple, pour une application Web éducative utilisée sur un ordinateur partagé avec de nombreux appareils, l'accumulation d'autorisations générées par l'utilisateur nuit à l'expérience utilisateur.

Appeler forget() sur une seule instance HIDDevice révoque l'accès à toutes les interfaces HID d'un même appareil physique.

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

forget() étant disponible dans Chrome 100 ou version ultérieure, vérifiez si elle est compatible avec les éléments suivants:

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

Conseils pour les développeurs

Vous pouvez facilement déboguer les HID dans Chrome depuis la page interne about://device-log, où vous pouvez voir tous les événements liés aux périphériques HID et USB au même endroit.

Capture d&#39;écran de la page interne de débogage HID
Page interne de Chrome pour déboguer l'HID.

Consultez l'explorateur HID pour transférer les informations sur l'appareil HID dans un format lisible. Il met en correspondance les valeurs d'utilisation avec les noms pour chaque utilisation HID.

Sur la plupart des systèmes Linux, les périphériques HID sont mappés avec des autorisations en lecture seule par défaut. Pour autoriser Chrome à ouvrir un périphérique HID, vous devez ajouter une nouvelle règle udev. Créez un fichier à l'emplacement /etc/udev/rules.d/50-yourdevicename.rules avec le contenu suivant:

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

Dans la ligne ci-dessus, [yourdevicevendor] est 057e si votre appareil est un Joy-Con Nintendo Switch, par exemple. ATTRS{idProduct} peut également être ajouté pour une règle plus spécifique. Assurez-vous que votre user est un membre du groupe plugdev. Ensuite, il suffit de reconnecter votre appareil.

Prise en charge des navigateurs

L'API WebHID est disponible sur toutes les plates-formes de bureau (ChromeOS, Linux, macOS et Windows) dans Chrome 89.

Démonstrations

Certaines démonstrations de WebHID sont disponibles sur web.dev/hid-examples. Allez y jeter un œil !

Sécurité et confidentialité

Les auteurs des spécifications ont conçu et mis en œuvre l'API WebHID en suivant les principes fondamentaux définis dans Contrôler l'accès aux fonctionnalités puissantes de la plate-forme Web, y compris le contrôle utilisateur, la transparence et l'ergonomie. L'utilisation de cette API est principalement contrôlée par un modèle d'autorisation qui n'accorde l'accès qu'à un seul appareil HID à la fois. En réponse à une invite utilisateur, celui-ci doit prendre des mesures actives pour sélectionner un appareil HID spécifique.

Pour comprendre les compromis en matière de sécurité, consultez la section Considérations liées à la sécurité et à la confidentialité de la spécification WebHID.

De plus, Chrome inspecte l'utilisation de chaque collection de premier niveau. Si une collection de premier niveau présente une utilisation protégée (par exemple, un clavier générique ou une souris), un site Web ne pourra pas envoyer ni recevoir de rapports définis dans cette collection. La liste complète des utilisations protégées est accessible au public.

Notez que les appareils HID sensibles à la sécurité (tels que les appareils FIDO HID utilisés pour une authentification renforcée) sont également bloqués dans Chrome. Consultez les fichiers de la liste de blocage USB et de la liste de blocage HID.

Commentaires

Nous aimerions connaître votre avis et vos expériences concernant l'API WebHID.

Décrivez-nous la conception de l'API.

Y a-t-il quelque chose dans l'API qui ne fonctionne pas comme prévu ? Ou y a-t-il des méthodes ou des propriétés manquantes dont vous avez besoin pour mettre en œuvre votre idée ?

Signalez un problème de spécification dans le dépôt GitHub de l'API WebHID ou ajoutez vos commentaires à un problème existant.

Signaler un problème d'implémentation

Avez-vous détecté un bug dans l'implémentation de Chrome ? Ou l'implémentation est-elle différente des spécifications ?

Consultez Signaler des bugs WebHID. Veillez à inclure autant de détails que possible, à fournir des instructions simples pour reproduire le bug et à définir l'élément Components sur Blink>HID. Glitch est idéal pour partager des reproductions rapides et faciles.

Afficher le soutien

Comptez-vous utiliser l'API WebHID ? Votre assistance publique aide l'équipe Chrome à hiérarchiser les fonctionnalités et montre aux autres fournisseurs de navigateurs à quel point il est essentiel de les prendre en charge.

Envoyez un tweet à @ChromiumDev avec le hashtag #WebHID, et indiquez-nous où et comment vous l'utilisez.

Liens utiles

Remerciements

Merci à Matt Reynolds et Joe Medley d'avoir donné leur avis sur cet article. Photo de Nintendo Switch rouge et bleu de Sara Kurfeß, et photo d'ordinateur portable en noir et argent d'Athul Cyriac Ajay sur Unsplash.