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

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

François Beaufort
François Beaufort

Alternatif klavyeler veya egzotik oyun kumandaları gibi çok sayıda yeni, çok eski veya sistemlerin cihaz sürücülerinin erişemeyeceği kadar nadir görülen insan arayüz cihazları (HID'ler) vardır. WebHID API, JavaScript'te cihaza özel mantığı uygulamak için bir yol sağlayarak bu sorunu çözer.

Önerilen kullanım alanları

HID cihazları, insanlardan giriş alır veya insanlara çıkış sağlar. Cihazlara örnek olarak klavyeler, işaretleme cihazları (fare, dokunmatik ekran vb.) ve oyun kumandaları verilebilir. HID protokolü, işletim sistemi sürücülerini kullanarak bu cihazlara masaüstü bilgisayarlardan erişmenizi sağlar. Web platformu, bu sürücülere dayalı olarak HID cihazlarını destekler.

Yaygın olmayan HID cihazlarına erişememe, özellikle alternatif yardımcı klavyeler (ör. Elgato Stream Deck, Jabra mikrofonlu kulaklıklar, X tuşları) ve egzotik oyun kumandaları desteği söz konusu olduğunda oldukça sıkıntı verir. Masaüstü için tasarlanan oyun kumandalarında, oyun kumandası girişleri (düğmeler, oyun kolları, tetikleyiciler) ve çıkışlar (LED'ler, sesler) için HID kullanılır. Maalesef oyun kumandası 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 sürdürülebilir değildir ve eski ve yaygın olmayan cihazların uzun kuyrukları için kötü bir destekle sonuçlanır. Ayrıca, tarayıcının, belirli cihazların davranışındaki tuhaflıklara da bağlı olmasına neden olur.

Terminoloji

HID, iki temel kavramdan oluşur: raporlar ve rapor açıklayıcıları. Raporlar, bir cihaz ile yazılım istemcisi arasında iletilen verilerdir. Rapor açıklayıcıda, cihazın desteklediği verilerin biçimi ve anlamı açıklanmaktadır.

HID (İnsan Arayüz Cihazı), insanlardan giriş alan veya insanlara çıkış sağlayan bir cihaz türüdür. Ana makine ile cihaz arasında iki yönlü iletişim standardı olan ve kurulum işlemini basitleştirmek için tasarlanmış HID protokolünü de ifade eder. HID protokolü başlangıçta USB cihazlar için geliştirilmişti ancak o zamandan beri Bluetooth da dahil olmak üzere diğer birçok protokol üzerinde uygulanmıştır.

Uygulamalar ve HID cihazları, üç rapor türü üzerinden ikili veri alışverişi yapar:

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

Rapor açıklayıcısı, cihaz tarafından desteklenen raporların ikili biçimini açıklar. Yapısı hiyerarşiktir ve raporları üst düzey koleksiyonda ayrı koleksiyonlar olarak gruplayabilir. Açıklayıcının biçimi, HID spesifikasyonu tarafından tanımlanır.

HID kullanımı, standartlaştırılmış bir giriş veya çıkışa karşılık gelen sayısal bir değerdir. Kullanım değerleri, bir cihazın hem cihazın kullanım amacını hem de raporlarındaki her bir alanın amacını tanımlamasına olanak tanır. Örneğin, bir farenin sol düğmesi için bir tane tanımlanır. Kullanımlar ayrıca kullanım sayfaları şeklinde düzenlenir. Bu sayfalarda, cihazın veya raporun üst düzey kategorisi belirtilir.

WebHID API'yi kullanma

Özellik algılama

WebHID API'nin desteklenip desteklenmediğini kontrol etmek için şunu kullanın:

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

HID bağlantısı açma

WebHID API, giriş beklerken web sitesi kullanıcı arayüzünün engellenmesini önlemek için tasarımı gereği eşzamansızdır. Bu, HID verilerinin her zaman alınabilmesi ve dinlenebilmesi için bir yöntem olması gerektiği için önemlidir.

Bir HID bağlantısı açmak için önce bir HIDDevice nesnesine erişin. Bunun için, kullanıcıdan navigator.hid.requestDevice() numaralı telefonu arayarak bir cihaz seçmesini isteyebilir veya web sitesine daha önce erişim izni verilen cihazların listesini döndüren navigator.hid.getDevices() hizmetinden bir cihaz seçebilirsiniz.

navigator.hid.requestDevice() işlevi, filtreleri tanımlayan zorunlu bir nesne alır. Bunlar; 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 bağlı tüm cihazları eşleştirmek için kullanılır. Bu bilgileri USB ID Deposu'ndan ve HID kullanım tabloları belgesinden edinebilirsiniz.

Bu işlevin döndürdüğü birden fazla HIDDevice nesnesi, aynı fiziksel cihazda birden çok 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 isteminin ekran görüntüsü.
Kullanıcı istemi: Nintendo Switch Joy-Con seçme.

Örneğin, hatalı çalıştığı bilinen bazı cihazları tarayıcı seçiciden hariç tutmak için navigator.hid.requestDevice() içindeki 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 }],
});

Bir HIDDevice nesnesi, cihaz tanımlamaya yönelik USB tedarikçi firması ve ürün tanımlayıcıları 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 döndürülür ve verilerin gönderilebilmesi veya alınabilmesi için open() çağrısı yapılarak açılmalıdır.

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

Giriş raporlarını al

HID bağlantısı kurulduktan sonra, cihazdan "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şkilendirilen 8 bit rapor kimliği (reportId) olarak içerir.

Kırmızı ve mavi nintendo değiştirme fotoğrafı.
Nintendo Switch Joy-Con cihazlar.

Önceki örnekle devam edelim. Aşağıdaki kod, kullanıcının Joy-Con Right cihazında hangi düğmeye bastığını algılayıp evde deneyebilmeniz için nasıl yardımcı olacağını göstermektedir.

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

Çıktı raporlarını gönder

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

Aşağıdaki örnek bir Joy-Con cihazı için geçerlidir ve çıktı raporlarıyla nasıl uyumlu hale getirileceğ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 gelebilen tek insan arabirim veri raporu türüdür. HID cihazlarının ve uygulamalarının standart hale getirilmemiş HID verilerini değiştirmesine izin verirler. 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ü klavye

Bir HID cihazına özellik raporu göndermek için özellik raporuyla (reportId) ilişkilendirilmiş 8 bit rapor kimliğini ve baytları BufferSource (data) olarak device.sendFeatureReport() cihazına iletin. Döndürülen söz, rapor gönderildiğinde geçerli olur. HID cihazı rapor kimliklerini kullanmıyorsa reportId değerini 0 olarak ayarlayın.

Aşağıdaki örnekte, Apple klavye arka aydınlatmalı cihaz isteme, açma ve cihaz yanıp sönmesini sağlama adımları gösterilmektedir.

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

Bir HID cihazından özellik raporu almak için özellik raporuyla (reportId) ilişkilendirilmiş 8 bit rapor kimliğini device.receiveFeatureReport() cihazına iletin. Döndürülen söz, özellik raporunun içeriklerini barındıran bir DataView nesnesiyle çözümlenir. HID cihazı rapor kimliklerini 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ıyı ve bağlantıyı kesmeyi dinleme

Web sitesinin bir HID cihazına erişim izni verildiğinde, "connect" ve "disconnect" etkinliklerini dinleyerek bağlantı ve bağlantı kesme etkinliklerini aktif olarak 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() yöntemini çağırarak 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ı söz konusu olduğunda, kullanıcı tarafından oluşturulmuş çok sayıda izin bulunması kötü bir kullanıcı deneyimine neden olur.

Tek bir HIDDevice örneğinde forget() çağrı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ğıdakilerle desteklenip desteklenmediğini kontrol edin:

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

Geliştiricilere Yönelik İpuçları

Chrome'da HID ve USB cihazlarıyla ilgili tüm etkinlikleri tek bir yerde görebileceğiniz dahili sayfa about://device-log sayesinde kolayca hata ayıklama işlemi yapabilirsiniz.

HID hatalarını ayıklamak için kullanılan dahili sayfanın ekran görüntüsü.
HID hatalarını ayıklamak için Chrome'daki dahili sayfa.

HID cihaz bilgilerini kullanıcıların okuyabileceği bir biçime dönüştürmek için HID gezginine göz atın. Her HID kullanımı için kullanım değerlerinden adlara eşlenir.

Çoğu Linux sisteminde HID cihazları varsayılan olarak salt okuma izinleriyle eşlenir. Chrome'un bir HID cihazını 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"

Örneğin cihazınız bir Nintendo Switch Joy-Con ise yukarıdaki satırda [yourdevicevendor] değeri 057e olur. ATTRS{idProduct}, daha spesifik bir kural için de eklenebilir. user hesabınızın plugdev grubunun üyesi olduğundan emin olun. Ardından cihazınızı yeniden bağlamanız yeterli.

Tarayıcı desteği

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

Demolar

Bazı WebHID demoları web.dev/hid-examples adresinde listelenmiştir. Haydi bir göz atın!

Güvenlik ve gizlilik

Spesifikasyon yazarları, Güçlü Web Platformu Özelliklerine Erişimi Kontrol Etme bölümünde tanımlanan temel ilkeleri (kullanıcı kontrolü, şeffaflık ve ergonomi dahil) kullanarak WebHID API'yi tasarlamıştır ve uygulamıştır. Bu API'nin kullanılabilmesi için öncelikle aynı anda yalnızca tek bir HID cihazına erişim veren bir izin modeli gerekir. Kullanıcı, kullanıcı istemine yanıt olarak belirli bir HID cihazını seçmek için etkin adımlar atmalıdır.

Güvenlik dengelerini anlamak için WebHID spesifikasyonunun Güvenlik ve Gizlilik Hakkında Dikkat Edilmesi Gerekenler bölümüne bakın.

Ayrıca Chrome, her üst düzey koleksiyonun kullanımını inceler ve üst düzey bir koleksiyonun korumalı kullanımı (ör. genel klavye, fare) varsa 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 da (daha güçlü kimlik doğrulama için kullanılan FIDO HID cihazları gibi) Chrome'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.

Bize API tasarımı hakkında bilgi verin

API'de beklendiği gibi çalışmayan bir şey var mı? Yoksa fikrinizi uygulamak için gereken yöntemler veya özellikler eksik mi?

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

Uygulamayla ilgili bir sorunu bildirin

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

WebHID hatalarını bildirme başlıklı makaleye göz atın. Mümkün olduğunca fazla ayrıntı eklediğinizden, hatayı yeniden oluşturmak için basit talimatlar sağladığınızdan ve Bileşenler'i Blink>HID değerine ayarladığınızdan emin olun. Glitch hızlı ve kolay yeniden oluşturmalar paylaşmak için idealdir.

Desteği göster

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ı satıcılarına onları desteklemenin ne kadar kritik olduğunu gösterir.

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

Faydalı bağlantılar

Teşekkür

Bu makaleyle ilgili yaptıkları yorumlar için Matt Reynolds ve Joe Medley'ye teşekkür ederiz. Sara Kurfeß'in kırmızı ve mavi Nintendo Switch fotoğrafı ve Unsplash'te Athul Cyriac Ajay'ın siyah-gümüş renkli dizüstü bilgisayar fotoğrafı.