Nawiązywanie połączeń z rzadkimi urządzeniami HID

Interfejs WebHID API umożliwia witrynom dostęp do alternatywnych klawiatur pomocniczych i egzotycznych padów do gier.

François Beaufort
François Beaufort

W obrębie długiego ogona są układy HID, takie jak klawiatur lub padów do gier, które są zbyt nowe, zbyt stare lub zbyt rzadko używane które są dostępne dla systemów sterowników urządzeń. Interfejs WebHID API rozwiązuje ten problem, udostępniając za pomocą JavaScriptu.

Sugerowane zastosowania

Urządzenie HID pobiera dane wejściowe ludziom lub przekazuje je ludziom. Przykłady urządzeń obejmują klawiatury, urządzenia wskazujące (myszy, ekrany dotykowe itp.) i pady do gier. Protokół HID umożliwia dostęp do tych urządzeń na komputerach. komputery ze sterownikami systemu operacyjnego. Platforma internetowa obsługuje urządzenia HID dzięki tym sterownikom.

Brak dostępu do rzadko używanych urządzeń HID jest szczególnie uciążliwy, gdy dotyczy alternatywnych klawiatur pomocniczych (np. Elgato Stream Deck, Jabra zestawy słuchawkowe, klawisze X) i egzotyczne pady do gier. Pady do gier zaprojektowane z myślą o komputerach często używają HID jako wejścia i wyjścia pada do gier (przyciski, joysticki, spusty) (diody LED, wibracje). Wejścia i wyjścia pada do gier nie działają poprawnie ustandaryzowane, a przeglądarki często wymagają niestandardowej logiki dla określonych urządzeń. Takie działanie jest nieskuteczne i skutkuje słabym wsparciem dla „długich ogonów” starszych i starszych na rzadko spotykane urządzenia. Sprawia też, że przeglądarka jest zależna od osobliwości w działaniu określonych urządzeń.

Terminologia

HID składa się z 2 podstawowych pojęć: raportów i deskryptorów raportów. Raporty to dane wymieniane między urządzeniem a klientem oprogramowania. Deskryptor raportu opisuje format i znaczenie danych, obsługuje.

HID (Human Interface Device) to typ urządzenia, które pobiera dane z dostarcza dane wyjściowe użytkownikom. Odnosi się też do protokołu HID, który jest standardem dla dwukierunkowa komunikacja między hostem a urządzeniem, która uprościć procedurę instalacji. Protokół HID został pierwotnie stworzony urządzeń USB, ale obecnie jest zaimplementowane za pomocą wielu innych protokołów w tym Bluetooth.

Aplikacje i urządzenia HID wymieniają dane binarne za pomocą 3 typów raportów:

Typ raportu Opis
Raport wejściowy Dane wysyłane z urządzenia do aplikacji (np. gdy naciśniesz przycisk).
Raport wyjściowy Dane wysyłane z aplikacji na urządzenie (np. żądanie włączenia podświetlenia klawiatury).
Raport o funkcjach Dane, które mogą być wysyłane w dowolnym kierunku. Format zależy od urządzenia.

Deskryptor raportu opisuje binarny format raportów obsługiwanych przez urządzenia. Ma ona hierarchiczną strukturę i może grupować raporty jako osobne w kolekcji najwyższego poziomu. Format deskryptora to zdefiniowane w specyfikacji HID.

Użycie HID to wartość liczbowa odnosząca się do ustandaryzowanych danych wejściowych lub wyjściowych. Wartości użycia pozwalają urządzeniu opisać jego zamierzone zastosowanie oraz przez poszczególne pola w raportach. Na przykład 1 jest zdefiniowany dla lewej strony przycisk myszy. Informacje o wykorzystaniu są też uporządkowane na stronach wykorzystania, które zawierają ogólna kategoria urządzenia lub raportu.

Korzystanie z interfejsu WebHID API

Wykrywanie cech

Aby sprawdzić, czy interfejs WebHID API jest obsługiwany, użyj:

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

Otwórz połączenie HID

Interfejs WebHID API jest z założenia asynchroniczny i uniemożliwia na czas oczekiwania na dane wejściowe. To ważne, ponieważ można odbierać dane HID w dowolnym momencie, wymagając podania sposobu, w jaki ją wysłuchać.

Aby otworzyć połączenie HID, najpierw uzyskaj dostęp do obiektu HIDDevice. Aby to zrobić, możesz poprosić użytkownika o wybranie urządzenia przez połączenie navigator.hid.requestDevice() lub wybierz coś z witryny navigator.hid.getDevices() które zwraca listę urządzeń, do których strona ma dostęp wcześniej.

Funkcja navigator.hid.requestDevice() przyjmuje obowiązkowy obiekt, który definiuje filtry. Służą one do dopasowywania dowolnego urządzenia podłączonego do dostawcy USB. identyfikator (vendorId), identyfikator produktu USB (productId), strona użycia wartość (usagePage) oraz wartość wykorzystania (usage). Znajdziesz je w Repozytorium identyfikatorów USB i dokumentacja tabel użytkowania HID.

Wiele obiektów HIDDevice zwróconych przez tę funkcję reprezentuje wiele obiektów z interfejsami HID na tym samym urządzeniu fizycznym.

// 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();
. Zrzut ekranu pokazujący prompt dla urządzenia HID na stronie internetowej.
Prośba użytkownika o wybranie konsoli Nintendo Switch Joy-Con.

Możesz też użyć opcjonalnego klucza exclusionFilters w navigator.hid.requestDevice(), aby wykluczyć niektóre urządzenia z selektora przeglądarki o których wiemy, że działają nieprawidłowo.

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

Obiekt HIDDevice zawiera identyfikatory dostawcy i produktu USB urządzenia danych identyfikacyjnych. Jego atrybut collections jest zainicjowany przy użyciu wartości hierarchicznej z opisem formatów raportów dostępnych na poszczególnych urządzeniach.

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
}

Urządzenia HIDDevice są domyślnie zwracane jako „zamknięte” i musi być należy otworzyć, wywołując metodę open(), zanim będzie można wysyłać lub odbierać dane.

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

Otrzymuj raporty wejściowe

Po ustanowieniu połączenia HID możesz obsługiwać przychodzące dane wejściowe. raportów, nasłuchując zdarzeń "inputreport" z urządzenia. Te wydarzenia zawierają dane HID w postaci obiektu DataView (data), czyli urządzenia HID, do którego należą do (device) oraz 8-bitowy identyfikator raportu powiązany z raportem z danymi wejściowymi (reportId).

Czerwono-niebieskie zdjęcie Nintendo Switch.
Urządzenia Nintendo Switch Joy-Con

Kontynuując poprzedni przykład, poniżej pokazujemy kod pokazujący, jak wykryć który przycisk został naciśnięty przez użytkownika na urządzeniu Joy-Con Right, aby można było spróbuję w domu.

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

Wysyłanie raportów wyjściowych

Aby wysłać raport wyjściowy do urządzenia HID, przekaż 8-bitowy identyfikator powiązany z z raportem wyjściowym (reportId) i bajtami w postaci BufferSource (data) do device.sendReport(). Zwrócona obietnica wygasa, gdy raport zostanie wysłano. Jeśli urządzenie HID nie używa identyfikatorów raportów, ustaw reportId na 0.

Poniższy przykład dotyczy urządzenia Joy-Con i pokazuje, jak je zrobić z raportami wyników.

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

Wysyłanie i odbieranie raportów dotyczących nowych funkcji

Raporty o funkcjach to jedyny typ raportów z danymi HID, które mogą być przesyłane wskazówek dojazdu. Umożliwiają one urządzeniom i aplikacjom HID wymianę nieustandaryzowanych Dane HID. W przeciwieństwie do raportów wejściowych i wyjściowych raporty dotyczące funkcji nie są przekazywane, regularnie wysyłane przez aplikację.

Czarno-srebrne zdjęcie na laptopie.
Klawiatura laptopa

Aby wysłać raport dotyczący funkcji na urządzenie HID, przekaż 8-bitowy identyfikator powiązany z raportem z raportem funkcji (reportId) i bajtami w postaci BufferSource (data) do device.sendFeatureReport(). Zwrócona obietnica wygasa, gdy raport wysłano. Jeśli urządzenie HID nie używa identyfikatorów raportów, ustaw reportId na 0.

Poniższy przykład pokazuje, jak korzystać z raportów funkcji. wyślij żądanie urządzenia z podświetleniem klawiatury Apple, otwórz je i spraw, żeby mrugnęło.

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

Aby otrzymać raport dotyczący funkcji z urządzenia HID, przekaż 8-bitowy identyfikator raportu powiązane z raportem funkcji (reportId) na device.receiveFeatureReport() Zwrócona obietnica rozwiązuje DataView – obiekt zawierający zawartość raportu dotyczącego cech. Jeśli HID urządzenie nie używa identyfikatorów raportów, ustaw reportId na 0.

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

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

Nasłuchuj połączenia i rozłączania

Jeśli strona ma dostęp do urządzenia HID, może aktywnie odbieraj zdarzenia połączenia i rozłączenia przez nasłuchiwanie "connect" i "disconnect" zdarzenia.

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

Unieważnij dostęp do urządzenia HID

Strona może wyczyścić uprawnienia dostępu do urządzenia HID, do którego już nie jest chcesz zachować zainteresowanie, wywołując funkcję forget() w wystąpieniu HIDDevice. Dla: przykładowa edukacyjna aplikacja internetowa używana na współdzielonym komputerze tych urządzeń, duża liczba zebranych uprawnień użytkowników powoduje, użytkowników.

Wywołanie forget() w pojedynczej instancji HIDDevice anuluje dostęp do wszystkich z interfejsami HID na tym samym urządzeniu.

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

Funkcja forget() jest dostępna w Chrome 100 i nowszych wersjach, więc sprawdź, czy jest ona obsługiwane przez:

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

Wskazówki dla programistów

Debugowanie HID w Chrome jest proste dzięki stronie wewnętrznej: about://device-log który umożliwia przeglądanie wszystkich zdarzeń związanych z urządzeniem HID i USB w jednym miejscu.

Zrzut ekranu strony wewnętrznej służącej do debugowania HID.
Wewnętrzna strona w Chrome służąca do debugowania HID.

W narzędziu HID Explorer możesz zainstalować zrzut danych z urządzenia HID w formacie zrozumiałym dla człowieka. Mapowanie od wartości użycia na nazwy Wykorzystanie HID.

W większości systemów Linux urządzenia HID są mapowane z uprawnieniami tylko do odczytu przez wartość domyślną. Aby zezwolić Chrome na otwieranie urządzenia HID, musisz dodać nowy udev . Utwórz plik pod adresem /etc/udev/rules.d/50-yourdevicename.rules z ta treść:

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

W wierszu powyżej [yourdevicevendor] to 057e, jeśli Twoje urządzenie to Nintendo Switch Joy-Con. Możesz też dodać ATTRS{idProduct}, aby dokładniej określić . Upewnij się, że user jest członkiem grupy plugdev. Potem wystarczy, Ponownie podłącz urządzenie.

Obsługa przeglądarek

Interfejs WebHID API jest dostępny na wszystkich platformach komputerowych (ChromeOS, Linux, macOS, i Windows) w Chrome 89.

Prezentacje

Niektóre wersje demonstracyjne WebHID znajdziesz na stronie web.dev/hid-examples. Zobacz!

Bezpieczeństwo i prywatność

Autorzy specyfikacji zaprojektowali i zaimplementowali interfejs WebHID API, wykorzystując podstawowe zasad zdefiniowanych w artykule Kontrolowanie dostępu do zaawansowanych funkcji platform internetowych, takich jak kontrola użytkownika, przejrzystość i ergonomia. Możliwość korzystania z tej karty Interfejs API jest chroniony głównie przez model uprawnień, który przyznaje dostęp tylko jedno urządzenie HID. W odpowiedzi na prośbę użytkownika musi on podjąć decyzję kroków, aby wybrać konkretne urządzenie HID.

Aby poznać zalety zabezpieczeń, zapoznaj się z artykułem na temat bezpieczeństwa i prywatności Uwagi w specyfikacji WebHID.

Oprócz tego Chrome sprawdza wykorzystanie każdej kolekcji najwyższego poziomu oraz czy kolekcji najwyższego poziomu mają chronione użycie (np. standardowa klawiatura, mysz), a następnie witryna nie będzie mogła wysyłać ani odbierać raportów zdefiniowanych w tej kolekcji. Pełna lista chronionych zastosowań jest dostępna publicznie.

Należy pamiętać, że urządzenia HID newralgiczne (takie jak urządzenia FIDO HID używane do silniejsze uwierzytelnianie) są też blokowane w Chrome. Zobacz listę zablokowanych USB oraz plików z listą zablokowanych HID.

Prześlij opinię

Zespół Chrome chętnie pozna Twoje przemyślenia i doświadczenia dotyczące Interfejs API WebHID.

Opowiedz nam o konstrukcji interfejsu API

Czy jest coś, co w interfejsie API nie działa zgodnie z oczekiwaniami? Czy są jeśli brakuje metod lub właściwości niezbędnych do realizacji pomysłu?

Zgłoś problem ze specyfikacją w repozytorium GitHub interfejsu WebHID API lub dodaj opinię do istniejącego problemu.

Zgłoś problem z implementacją

Czy wystąpił błąd z implementacją Chrome? Czy wdrożenie różni się od specyfikacji?

Zapoznaj się z artykułem Jak zgłaszać błędy WebHID. Podaj jak najwięcej jak najwięcej szczegółów, podać proste instrukcje dotyczące odtworzenia błędu oraz Ustawiono Komponenty na Blink>HID. Glitch działa świetnie: szybkie i łatwe udostępnianie.

Pokaż wsparcie

Czy zamierzasz korzystać z interfejsu WebHID API? Wasze publiczne wsparcie pomaga nam ulepszyć Chrome nadaje priorytet funkcjom i pokazuje innym dostawcom przeglądarek, jak ważne jest, ich wsparcie.

Wyślij tweeta na adres @ChromiumDev, używając hashtagu #WebHID i poinformuj nas o tym gdzie i jak z niej korzystasz.

Przydatne linki

Podziękowania

Dziękujemy Mattowi Reynoldsowi i Joe Medley za opinię o tym artykule. Czerwono-niebieskie zdjęcie Nintendo Switch, wykonawca: Sara Kurfeß, oraz czarno-srebrny laptop zdjęcie komputera: Athul Cyriac Ajay w Unsplash.