Yaygın olmayan HID cihazlarına bağlanılıyor

WebHID API, web sitelerinin alternatif yardımcı klavyelere ve sıra dışı oyun kumandaları erişmesine olanak tanır.

François Beaufort
François Beaufort

Alternatif klavyeler veya egzotik gamepad'ler gibi çok yeni, çok eski veya sistemlerin cihaz sürücüleri tarafından erişilemeyecek kadar yaygın olmayan çok sayıda insan arayüz cihazı (HID) vardır. WebHID API, JavaScript'te cihaza özel mantığı uygulamanın bir yolunu sağlayarak bu sorunu çözer.

Önerilen kullanım alanları

HID cihazlar, kullanıcılardan giriş alır veya kullanıcılara çıkış sağlar. Klavye, işaretleme cihazları (fare, dokunmatik ekran vb.) ve oyun kumandası bu cihazlara örnek olarak verilebilir. HID protokolü, işletim sistemi sürücülerini kullanarak masaüstü bilgisayarlarda bu cihazlara erişmeyi mümkün kılar. Web platformu, bu sürücüleri kullanarak HID cihazlarını destekler.

Yaygın olmayan HID cihazlara erişememek, özellikle alternatif yardımcı klavyeler (ör. Elgato Stream Deck, Jabra kulaklıklar, X-keys) ve sıra dışı gamepad desteği söz konusu olduğunda can sıkıcı olabilir. Masaüstü için tasarlanmış oyun kumandaları genellikle oyun kumandası girişleri (düğmeler, kontrol çubukları, tetikler) ve çıkışları (LED'ler, titreşim) için HID kullanır. Maalesef gamepad giriş ve çıkışları iyi standartlaştırılmamıştır ve web tarayıcıları genellikle belirli cihazlar için özel mantık gerektirir. Bu durum sürdürülebilir değildir ve eski ve yaygın olmayan cihazların uzun kuyrukları için kötü bir destek sunar. Ayrıca, tarayıcının belirli cihazların davranışındaki tuhaflıklara bağlı olmasına neden olur.

Terminoloji

HID iki temel kavramdan oluşur: raporlar ve rapor tanımlayıcıları. Raporlar, bir cihaz ile yazılım istemcisi arasında paylaşılan verilerdir. Rapor tanımlayıcısı, cihazın desteklediği verilerin biçimini ve anlamını açıklar.

HID (İnsan Arabirim Cihazı), kullanıcılardan giriş alan veya kullanıcılara çıkış sağlayan bir cihaz türüdür. Ayrıca, ana makine ile cihaz arasında kurulum işlemini basitleştirmek için tasarlanmış iki yönlü iletişim standardı olan HID protokolünü de ifade eder. HID protokolü başlangıçta USB cihazlar için geliştirilmiş olsa da o zamandan beri Bluetooth da dahil olmak üzere birçok başka protokolde uygulanmıştır.

Uygulamalar ve HID cihazlar, üç rapor türü aracılığıyla ikili veri alışverişinde bulunur:

Rapor türü Açıklama
Giriş raporu Cihazdan uygulamaya gönderilen veriler (ör. bir düğmeye basılması)
Çıkış raporu Uygulamadan cihaza gönderilen veriler (ör. klavye arka ışığını açma isteği).
Özellik raporu Her iki yönde de gönderilebilecek veriler. Biçim cihaza özeldir.

Rapor tanımlayıcısı, cihaz tarafından desteklenen raporların ikili biçimini tanımlar. Hiyerarşik bir yapıya sahiptir ve raporları üst düzey koleksiyon içinde ayrı koleksiyonlar olarak gruplandırabilir. Tanımlayıcının biçimi HID spesifikasyonu tarafından tanımlanır.

HID kullanımı, standartlaştırılmış bir giriş veya çıkışı belirten sayısal bir değerdir. Kullanım değerleri, cihazın raporlarında cihazın amaçlanan kullanımını ve her alanın amacını tanımlamasına olanak tanır. Örneğin, farenin sol düğmesi için bir tane tanımlanır. Kullanımlar, cihazın veya raporun üst düzey kategorisini gösteren kullanım sayfalarına da ayrılır.

WebHID API'yi kullanma

Özellik algılama

WebHID API'nin desteklenip desteklenmediğini kontrol etmek için:

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

HID bağlantısı açma

WebHID API, web sitesi kullanıcı arayüzünün giriş beklerken engellenmesini önlemek için tasarlandığından asenkrondur. HID verileri herhangi bir zamanda alınabileceğinden, bu verileri dinlemek için bir yönteme ihtiyaç vardır.

HID bağlantısını açmak için önce bir HIDDevice nesnesine erişin. Bunun için navigator.hid.requestDevice() işlevini çağırarak kullanıcıdan bir cihaz seçmesini isteyebilir veya web sitesinin daha önce erişmesine izin verilen cihazların listesini döndüren navigator.hid.getDevices() işlevinden birini seçebilirsiniz.

navigator.hid.requestDevice() işlevi, filtreleri tanımlayan zorunlu bir nesne alır. Bunlar, bağlı herhangi bir cihazı USB tedarikçi tanımlayıcısı (vendorId), USB ürün tanımlayıcısı (productId), kullanım sayfası değeri (usagePage) ve kullanım değeri (usage) ile eşleştirmek için kullanılır. Bu değerleri USB kimlik deposundan ve HID kullanım tabloları belgesinden edinebilirsiniz.

Bu işlev tarafından döndürülen birden fazla HIDDevice nesnesi, aynı fiziksel cihazdaki birden fazla HID arayüzünü temsil eder.

// 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();
Bir web sitesindeki HID cihaz isteğinin ekran görüntüsü.
Nintendo Switch Joy-Con seçmek için kullanıcı istemi.

Örneğin, düzgün çalışmadığı bilinen bazı cihazları tarayıcı seçiciden hariç tutmak için navigator.hid.requestDevice() içinde isteğe bağlı exclusionFilters anahtarını da kullanabilirsiniz.

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

HIDDevice nesnesi, cihaz tanımlama için USB tedarikçi ve ürün tanımlayıcılarını içerir. collections özelliği, cihazın rapor biçimlerinin hiyerarşik bir açıklamasıyla başlatılır.

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
}

HIDDevice cihazları varsayılan olarak "kapalı" durumda iade edilir ve veri gönderilmeden veya alınmadan önce open() çağrılarak açılmalıdır.

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

Giriş raporları alma

HID bağlantısı kurulduktan sonra cihazdaki "inputreport" etkinliklerini dinleyerek gelen giriş raporlarını işleyebilirsiniz. Bu etkinlikler, HID verilerini DataView nesnesi (data), ait olduğu HID cihazı (device) ve giriş raporuyla ilişkili 8 bitlik rapor kimliği (reportId) olarak içerir.

Kırmızı ve mavi Nintendo Switch fotoğrafı.
Nintendo Switch Joy-Con cihazları.

Önceki örneğe devam ediyoruz. Aşağıdaki kodda, kullanıcının Joy-Con sağ cihazda hangi düğmeye bastığını nasıl algılayacağınız gösterilmektedir. Böylece, bunu evde deneyebilirsiniz.

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

Çıkış raporlarını gönderme

Bir HID cihazına çıkış raporu göndermek için çıkış raporuyla ilişkili 8 bitlik rapor kimliğini (reportId) ve baytları device.sendReport()'e BufferSource (data) olarak iletin. Döndürülen söz, rapor gönderildikten sonra çözülür. HID cihazı rapor kimlikleri kullanmıyorsa reportId değerini 0 olarak ayarlayın.

Aşağıdaki örnek, Joy-Con cihazı için geçerlidir ve çıkış raporlarıyla nasıl titretileceğini gösterir.

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

Özellik raporları gönderme ve alma

Özellik raporları, her iki yönde de aktarılabilen tek HID veri raporu türüdür. HID cihazlarının ve uygulamalarının standartlaştırılmamış HID verilerini alışverişine olanak tanır. Giriş ve çıkış raporlarının aksine, özellik raporları uygulama tarafından düzenli olarak alınmaz veya gönderilmez.

Siyah ve gümüş renkli dizüstü bilgisayar fotoğrafı.
Dizüstü bilgisayar klavyesi

Bir HID cihazına özellik raporu göndermek için özellik raporuyla ilişkili 8 bitlik rapor kimliğini (reportId) ve baytları device.sendFeatureReport()'e BufferSource (data) olarak iletin. Döndürülen söz, rapor gönderildikten sonra çözülür. HID cihazı rapor kimlikleri kullanmıyorsa reportId değerini 0 olarak ayarlayın.

Aşağıdaki örnekte, bir Apple klavye arka ışık cihazını nasıl isteyeceğinizi, açacağınızı ve yanıp söndüreceğinizi göstererek özellik raporlarının kullanımı açıklanmaktadır.

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

HID cihazdan özellik raporu almak için özellik raporuyla ilişkili 8 bitlik rapor kimliğini (reportId) device.receiveFeatureReport()'e iletin. Döndürülen söz, özellik raporunun içeriğini içeren bir DataView nesnesi ile çözülür. HID cihazı rapor kimlikleri kullanmıyorsa reportId değerini 0 olarak ayarlayın.

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

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

Bağlantı ve bağlantı kesme seslerini dinleme

Web sitesine HID cihazına erişim izni verildiğinde, "connect" ve "disconnect" etkinliklerini dinleyerek bağlantı ve bağlantı kesme etkinliklerini etkin bir şekilde alabilir.

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

HID cihazına erişimi iptal etme

Web sitesi, HIDDevice örneğinde forget() çağrısını yaparak artık saklamak istemediği bir HID cihazına erişim izinlerini temizleyebilir. Örneğin, birçok cihazın bulunduğu paylaşılan bir bilgisayarda kullanılan eğitim amaçlı bir web uygulamasında, kullanıcı tarafından oluşturulan çok sayıda izin birikmesi kötü bir kullanıcı deneyimi oluşturur.

Tek bir HIDDevice örneğinde forget() çağrısı yapıldığında aynı fiziksel cihazdaki tüm HID arayüzlerine erişim iptal edilir.

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

forget(), Chrome 100 veya sonraki sürümlerde kullanılabildiğinden bu özelliğin aşağıdaki sürümlerde desteklenip desteklenmediğini kontrol edin:

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

Geliştirici İpuçları

Chrome'da HID ile ilgili hataları gidermek, HID ve USB cihazlarla ilgili tüm etkinlikleri tek bir yerde görebileceğiniz about://device-log dahili sayfasıyla kolaydır.

HID&#39;de hata ayıklama için dahili sayfanın ekran görüntüsü.
HID'de hata ayıklama yapmak için Chrome'daki dahili sayfa.

HID cihaz bilgilerini kullanıcı tarafından okunabilir bir biçime aktarmak için HID gezginine göz atın. Her HID kullanımı için kullanım değerlerini adlarla eşler.

Çoğu Linux sisteminde HID cihazları varsayılan olarak salt okuma izinleriyle eşlenir. Chrome'un HID cihazı açmasına izin vermek için yeni bir udev kuralı eklemeniz gerekir. /etc/udev/rules.d/50-yourdevicename.rules adresinde aşağıdaki içeriğe sahip bir dosya oluşturun:

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

Yukarıdaki satırda, cihazınız örneğin bir Nintendo Switch Joy-Con ise [yourdevicevendor], 057e olur. Daha spesifik bir kural için ATTRS{idProduct} da eklenebilir. user kullanıcınızın plugdev grubunun üyesi olduğundan emin olun. Ardından cihazınızı tekrar bağlayın.

Tarayıcı desteği

WebHID API, Chrome 89'da tüm masaüstü platformlarında (ChromeOS, Linux, macOS ve Windows) kullanılabilir.

Demolar

Bazı WebHID demolar web.dev/hid-examples adresinde listelenmiştir. Göz atın.

Güvenlik ve gizlilik

Spesifikasyon yazarları, WebHID API'yi tasarlarken ve uygularken kullanıcı kontrolü, şeffaflık ve ergonomi gibi Güçlü Web Platformu Özelliklerine Erişimi Kontrol Etme başlıklı makalede tanımlanan temel ilkeleri temel almıştır. Bu API'nin kullanılabilmesi, temel olarak aynı anda yalnızca tek bir HID cihazına erişim izni veren bir izin modeliyle sınırlandırılmıştır. Kullanıcı istemlerine yanıt olarak kullanıcının belirli bir HID cihazı seçmek için etkin adımlar atması gerekir.

Güvenlikle ilgili avantajları ve dezavantajları anlamak için WebHID spesifikasyonunun Güvenlik ve Gizlilik Konusunda Dikkat Edilmesi Gerekenler bölümüne göz atın.

Bununla birlikte Chrome, her üst düzey koleksiyonun kullanımını inceler.Üst düzey bir koleksiyonun kullanımı korunuyorsa (ör. genel klavye, fare) web sitesi bu koleksiyonda tanımlanan raporları gönderip alamaz. Korunan kullanımların tam listesi herkese açıktır.

Güvenlik açısından hassas HID cihazların (ör. daha güçlü kimlik doğrulama için kullanılan FIDO HID cihazları) Chrome'da da engellendiğini unutmayın. USB engellenenler listesi ve HID engellenenler listesi dosyalarına bakın.

Geri bildirim

Chrome Ekibi, WebHID API ile ilgili düşüncelerinizi ve deneyimlerinizi öğrenmekten memnuniyet duyar.

API tasarımı hakkında bilgi verin

API ile ilgili beklendiği gibi çalışmayan bir şey var mı? Yoksa fikrinizi uygulamak için ihtiyaç duyduğunuz yöntemler veya özellikler eksik mi?

WebHID API GitHub deposunda spesifikasyon sorunu oluşturun veya mevcut bir soruna düşüncelerinizi ekleyin.

Uygulamayla ilgili sorunları bildirme

Chrome'un uygulamasında bir hata mı buldunuz? Yoksa uygulama, spesifikasyondan farklı mı?

WebHID hatalarını bildirme başlıklı makaleyi inceleyin. Olabildiğince fazla ayrıntı eklediğinizden, hatayı yeniden oluşturmayla ilgili basit talimatlar sağladığınızdan ve Bileşenler'in Blink>HID olarak ayarlandığından emin olun. Glitch, hızlı ve kolay yeniden oluşturma işlemlerini paylaşmak için mükemmel bir araçtır.

Destek gösterme

WebHID API'yi kullanmayı planlıyor musunuz? Herkese açık desteğiniz, Chrome ekibinin özelliklere öncelik vermesine yardımcı olur ve diğer tarayıcı tedarikçi firmalarına bu özellikleri desteklemenin ne kadar önemli olduğunu gösterir.

#WebHID hashtag'ini kullanarak @ChromiumDev hesabına tweet gönderin ve bu özelliği nerede ve nasıl kullandığınızı bize bildirin.

Faydalı bağlantılar

Teşekkür ederiz

Bu makaleyi inceleyen Matt Reynolds ve Joe Medley'e teşekkür ederiz. Sara Kurfeß'in kırmızı ve mavi Nintendo Switch fotoğrafı ve Athul Cyriac Ajay'ın Unsplash'taki siyah ve gümüş dizüstü bilgisayar fotoğrafı.