Connexion à des périphériques HID peu courants

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

François Beaufort
François Beaufort

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

Cas d'utilisation suggérés

Un appareil HID reçoit des entrées ou fournit des sorties à des humains. Exemples d'appareils comme les claviers, les dispositifs de pointage (souris, écrans tactiles, etc.) et les manettes de jeu. Le protocole HID permet d'accéder à ces appareils depuis un ordinateur. ordinateurs à l’aide de pilotes de système d’exploitation. La plate-forme Web est compatible avec les appareils HID en s'appuyant sur ces pilotes.

L'impossibilité d'accéder à des appareils HID peu courants est particulièrement pénible mais plutôt d'autres claviers auxiliaires (par exemple, Elgato Stream Deck et Jabra). des casques audio et des touches X) et la compatibilité avec des manettes de jeu exotiques. Manettes de jeu conçues pour ordinateur utilisent souvent HID pour les entrées et sorties de la manette de jeu (boutons, joysticks, déclencheurs) (LED, bruits). Malheureusement, les entrées et sorties de la manette ne sont pas bien standardisés et les navigateurs web nécessitent souvent une logique personnalisée pour des appareils spécifiques. Cette situation n'est pas durable et entraîne une mauvaise prise en charge de la longue traîne des anciennes et appareils peu courants. Cela oblige également le navigateur à dépendre de particularités dans le comportement d'appareils spécifiques.

Terminologie

La fonctionnalité HID repose sur deux concepts fondamentaux: les rapports et les descripteurs de rapport. Les rapports désignent les données échangées entre un appareil et un client logiciel. Le descripteur du rapport décrit le format et la signification des données que l'appareil compatibles.

Un HID (Human Interface Device) est un type de dispositif qui reçoit des entrées de ou fournit des résultats à des humains. Il fait également référence au protocole HID, une norme une communication bidirectionnelle entre un hôte et un appareil conçue pour pour simplifier la procédure d'installation. À l'origine, le protocole HID a été développé pour les périphériques USB, mais a depuis été implémentée sur de nombreux autres protocoles, y compris le Bluetooth.

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

Type de rapport Description
Rapport d'entrée Données envoyées de l'appareil à l'application (par exemple, sur un bouton).
Rapport sur les résultats Données envoyées de l'application à l'appareil (demande d'activation du rétroéclairage du clavier, par exemple)
Rapport sur les fonctionnalités Données pouvant être envoyées dans les deux sens. Le format est spécifique à chaque appareil.

Un descripteur de rapport décrit le format binaire des rapports compatibles avec appareil. Sa structure est hiérarchique et permet de regrouper les rapports pour en créer 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 faisant référence à une entrée ou à une sortie standardisées. Les valeurs d'utilisation permettent à un appareil de décrire l'usage auquel il est destiné et les l'objectif de chaque champ dans ses rapports. Par exemple, l'un est défini pour la partie gauche bouton d'une souris. Les utilisations sont également organisées en pages d'utilisation, qui fournissent indique la catégorie générale de l'appareil ou du rapport.

Utiliser l'API WebHID

Détection de caractéristiques

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

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

Ouvrir une connexion HID

L'API WebHID est asynchrone par la conception pour empêcher l'interface utilisateur du site Web en attente d'une entrée. C'est important, car les données HID peuvent être reçues à tout moment, nécessitant un moyen de l'écouter.

Pour ouvrir une connexion HID, accédez d'abord à un objet HIDDevice. Pour cela, vous pouvez soit inviter l'utilisateur à sélectionner un appareil en appelant navigator.hid.requestDevice(), ou choisissez-en un dans navigator.hid.getDevices() qui renvoie une liste des appareils auxquels le site Web a accès précédemment.

La fonction navigator.hid.requestDevice() utilise un objet obligatoire qui définit des filtres. Ils sont utilisés pour correspondre à tout appareil connecté à un fournisseur USB un identifiant (vendorId), un code produit USB (productId), une page d'utilisation (usagePage) et une valeur d'utilisation (usage). Vous pouvez les obtenir dans la page Dépôt d'ID USB et le document sur les 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();
<ph type="x-smartling-placeholder">
</ph> Capture d&#39;écran d&#39;une invite concernant un appareil HID sur un site Web.
Invite de l'utilisateur pour sélectionner un Joy-Con pour la Nintendo Switch.

Vous pouvez également utiliser la clé exclusionFilters facultative dans navigator.hid.requestDevice() pour exclure certains appareils du sélecteur de navigateur qui sont connus pour dysfonctionnement, 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 des identifiants de fournisseur et de produit USB pour l'appareil. l'identification. Son attribut collections est initialisé avec une description des formats de rapport 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 dans un état "fermé" et doit être ouvert 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 d'entrée

Une fois la connexion HID établie, vous pouvez gérer l'entrée entrante. en écoutant les événements "inputreport" depuis l'appareil. Ces événements contiennent les données HID en tant qu'objet DataView (data), c'est-à-dire l'appareil HID auquel ils appartiennent. à (device) et l'ID de rapport 8 bits associé au rapport d'entrée (reportId).

<ph type="x-smartling-placeholder">
</ph> Photo Nintendo Switch rouge et bleue.
Appareils Joy-Con de la Nintendo Switch.

Reprenons l'exemple précédent. Le code ci-dessous montre comment détecter le bouton sur lequel l'utilisateur a appuyé sur un appareil Joy-Con de droite afin que vous puissiez si tout va bien, essayez à la maison.

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 les rapports de sortie

Pour envoyer un rapport de sortie à un appareil HID, transmettez l'ID de rapport 8 bits associé avec le rapport de sortie (reportId) et les octets sous forme de BufferSource (data) pour device.sendReport() La promesse renvoyée est résolue une fois que le rapport a été 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 avec les 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 fonctionnalités sont le seul type de rapports de données HID pouvant être transmis à la fois les instructions de navigation. Elles permettent aux applications et appareils HID d'échanger des données non standardisées données HID. Contrairement aux rapports d'entrée et de sortie, les rapports sur les fonctionnalités ne sont ni reçus, envoyées régulièrement par l'application.

<ph type="x-smartling-placeholder">
</ph> Photo d&#39;un ordinateur portable en noir et argent.
Clavier d'ordinateur portable
.

Pour envoyer un rapport de fonctionnalité à un appareil HID, transmettez l'ID de rapport 8 bits associé avec le rapport sur les fonctionnalités (reportId) et les octets sous forme de BufferSource (data) pour device.sendFeatureReport() La promesse renvoyée est résolue une fois que le rapport a 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 sur les fonctionnalités en vous montrant comment demander un dispositif de rétroéclairage à clavier Apple, ouvrez-le et faites-le 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) pour device.receiveFeatureReport() La promesse renvoyée se résout avec une Objet DataView incluant le contenu du rapport sur les fonctionnalités. Si le HID appareil n'utilise pas les 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 "connect" et "disconnect" événements.

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 à un appareil HID

Le site Web peut supprimer les autorisations d'accès à un appareil HID dont il n'est plus que vous souhaitez conserver en appelant forget() sur l'instance HIDDevice. Pour exemple, pour une application Web éducative utilisée sur un ordinateur partagé avec de nombreux les appareils mobiles, un grand nombre d'autorisations accumulées par l'utilisateur l'expérience utilisateur.

En appelant forget() sur une seule instance HIDDevice, vous révoquez l'accès à tous les interfaces HID sur le même appareil physique.

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

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

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

Conseils de développement

Déboguer facilement HID dans Chrome grâce à 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.

<ph type="x-smartling-placeholder">
</ph> Capture d&#39;écran de la page interne pour déboguer HID.
Page interne dans Chrome pour déboguer HID.

Consultez l'explorateur HID pour vider le cache de l'appareil HID. les informations dans un format lisible par l'humain. Elle fait correspondre les valeurs d'utilisation aux noms de chaque Utilisation de HID

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

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

Dans la ligne ci-dessus, [yourdevicevendor] est 057e si votre appareil est une Nintendo Switch Joy-Con, par exemple. Vous pouvez également ajouter ATTRS{idProduct} pour un d'une règle. Assurez-vous que votre user est un membre du groupe plugdev. Ensuite, reconnectez 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émos WebHID sont répertoriées sur web.dev/hid-examples. Allez voir !

Sécurité et confidentialité

Les auteurs des spécifications ont conçu et implémenté l'API WebHID à l'aide de l'API définis dans l'article Contrôler l'accès à des fonctionnalités de plate-forme Web performantes, y compris le contrôle de l'utilisateur, la transparence et l'ergonomie. La possibilité d'utiliser L'API est principalement contrôlée par un modèle d'autorisation qui n'accorde l'accès qu'à un seul accès HID périphérique à la fois. En réponse à l'invite de l'utilisateur, celui-ci doit prendre des mesures pour sélectionner un appareil HID particulier.

Pour comprendre les compromis en termes de sécurité, consultez le guide Considérations de la spécification WebHID.

De plus, Chrome inspecte l'utilisation de chaque collection de niveau supérieur et si un la collection de niveau supérieur a une utilisation protégée (par exemple, clavier générique, souris), puis un site Web ne pourra pas envoyer ni recevoir de rapports définis dans ce collection. La liste complète des utilisations protégées est accessible au public.

Notez que les périphériques HID sensibles à la sécurité (tels que les périphériques HID FIDO utilisés pour une authentification plus forte) sont également bloqués dans Chrome. Consultez la liste de blocage USB et Fichiers de liste de blocage HID

Commentaires

L'équipe Chrome aimerait connaître votre avis et votre expérience concernant la API WebHID.

Présentez-nous la conception de l'API

Y a-t-il un aspect de l'API qui ne fonctionne pas comme prévu ? Ou y a-t-il des méthodes ou des propriétés 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 faites part de 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 différent des spécifications ?

Pour en savoir plus, consultez Signaler des bugs WebHID. Assurez-vous d'inclure autant détaillez le plus possible, fournissez des instructions simples pour reproduire le bug Composants définis sur Blink>HID. Glitch fonctionne parfaitement pour partager des répétitions rapidement et facilement.

Montrez votre soutien

Prévoyez-vous d'utiliser l'API WebHID ? Votre assistance publique permet au Chrome l'équipe à hiérarchiser les fonctionnalités et montre aux autres fournisseurs de navigateurs à quel point il est essentiel les accompagner.

Envoyez un tweet à @ChromiumDev en utilisant le hashtag. #WebHID et n'hésitez pas à nous en faire part où et comment vous l'utilisez.

Liens utiles

Remerciements

Merci à Matt Reynolds et Joe Medley pour leurs commentaires sur cet article. Photo Nintendo Switch rouge et bleue de Sara Kurfeß, et ordinateur portable noir et argenté photo d'ordinateur par Athul Cyriac Ajay sur Unsplash.