diff --git a/src/bridge-hid.test.ts b/src/bridge-hid.test.ts index 0a9e7c28..74c07301 100644 --- a/src/bridge-hid.test.ts +++ b/src/bridge-hid.test.ts @@ -39,6 +39,8 @@ test("drives a device end to end over the socket", async () => { const listing = hid.getDevices(); assert.equal(fake.sent[0].type, "list"); assert.ok(Array.isArray(fake.sent[0].vendorIds) && (fake.sent[0].vendorIds as number[]).length > 0); + // Razer's Bluetooth id, so a V3 Pro in Bluetooth mode can explain itself. + assert.ok((fake.sent[0].vendorIds as number[]).includes(0x068e)); fake.reply({ id: fake.sent[0].id, ok: true, devices: [MOUSE] }); const [device] = await listing; diff --git a/src/bridge-hid.ts b/src/bridge-hid.ts index 365583f4..cae0f7b8 100644 --- a/src/bridge-hid.ts +++ b/src/bridge-hid.ts @@ -18,7 +18,7 @@ // socket. Safari does not, and blocks it as mixed content — Safari needs // Bridge to serve the app itself over loopback, which is a separate change. -import { SUPPORTED_HID_FILTERS } from "@openmouse/protocol/drivers/vendors"; +import { HID_FILTERS } from "./device/hid-filters.ts"; import { describeHidDevice, markHidActivity } from "./hid-diagnostics.ts"; @@ -444,7 +444,7 @@ class BridgeHid implements HID { } async #listDevices(): Promise { - const reply = await this.#client.request({ type: "list", vendorIds: vendorIdsFor(SUPPORTED_HID_FILTERS) }); + const reply = await this.#client.request({ type: "list", vendorIds: vendorIdsFor(HID_FILTERS) }); const { devices, added, removed } = this.#client.reconcile(reply.devices ?? []); if (this.#enumerated) { for (const device of added) this.#emit("connect", device); diff --git a/src/device/controller.ts b/src/device/controller.ts index 054d21ed..f45e03c3 100644 --- a/src/device/controller.ts +++ b/src/device/controller.ts @@ -161,7 +161,8 @@ import { type KeychronNapeButtonAction, type KeychronNapeLayerKeymap, } from "@openmouse/protocol/keychron"; -import { SUPPORTED_HID_FILTERS, VENDOR_ID } from "@openmouse/protocol/drivers/vendors"; +import { VENDOR_ID } from "@openmouse/protocol/drivers/vendors"; +import { BASILISK_V3_PRO_BLUETOOTH, HID_FILTERS } from "./hid-filters"; import { WLMouseHidClient } from "@openmouse/protocol/drivers/wlmouse/hid"; import { MicrosoftHidClient } from "@openmouse/protocol/drivers/microsoft/hid"; import { DareuHidClient } from "@openmouse/protocol/drivers/dareu/hid"; @@ -2289,7 +2290,7 @@ function handleHidDisconnect(event: HIDConnectionEvent): void { async function requestSupportedClient(): Promise { if (!navigator.hid) throw new Error(st("ctl.noWebHid")); - const devices = await navigator.hid.requestDevice({ filters: SUPPORTED_HID_FILTERS }); + const devices = await navigator.hid.requestDevice({ filters: HID_FILTERS }); if (devices.length === 0) return null; if (devices.some((device) => eggWeIsSupported(device))) { @@ -2307,6 +2308,11 @@ async function requestSupportedClient(): Promise { return best.client; } + if (devices.some((device) => device.vendorId === BASILISK_V3_PRO_BLUETOOTH.vendorId + && device.productId === BASILISK_V3_PRO_BLUETOOTH.productId)) { + throw new Error(st("ctl.razerBluetooth")); + } + const details = devices.map((device) => describeHidDevice(device)).join(" · "); const nativeOnly = devices.find((device) => { const product = RAZER_PRODUCTS.get(device.productId) as { nativeOnly?: boolean } | undefined; diff --git a/src/device/hid-filters.ts b/src/device/hid-filters.ts new file mode 100644 index 00000000..8af367ad --- /dev/null +++ b/src/device/hid-filters.ts @@ -0,0 +1,12 @@ +import { SUPPORTED_HID_FILTERS } from "@openmouse/protocol/drivers/vendors"; + +/** + * The Basilisk V3 Pro in Bluetooth mode. It enumerates under Razer's Bluetooth + * SIG id rather than 0x1532, and takes settings over a vendor GATT service + * instead of HID, so no driver can claim it. It is offered anyway so picking + * it explains itself rather than the mouse silently never appearing. + */ +export const BASILISK_V3_PRO_BLUETOOTH = { vendorId: 0x068e, productId: 0x00ac }; + +/** Everything the app asks the browser (or Bridge) for. */ +export const HID_FILTERS: HIDDeviceFilter[] = [...SUPPORTED_HID_FILTERS, BASILISK_V3_PRO_BLUETOOTH]; diff --git a/src/i18n-ar.ts b/src/i18n-ar.ts index 62f12c2e..4cde6080 100644 --- a/src/i18n-ar.ts +++ b/src/i18n-ar.ts @@ -677,6 +677,7 @@ export const ar: Record = { "ctl.reconnecting": "جارٍ إعادة الاتصال", "ctl.readingPrev": "جارٍ قراءة الجهاز المصرّح به سابقًا.", "ctl.useAdd": "استخدم «إضافة جهاز» إن لم يُعد الماوس الاتصال تلقائيًا.", + "ctl.razerBluetooth": "لا يمكن ضبط Basilisk V3 Pro عبر البلوتوث بعد. حوّله إلى HyperSpeed (2.4 GHz) مع الدونجل، أو وصّل الكابل.", "ctl.couldNotReconnect": "تعذّرت إعادة اتصال الماوس", "ctl.dpiUnsupported": "قيمة DPI هذه غير مدعومة من هذا الماوس.", "ctl.closestStep": "لا يستطيع هذا الماوس تنفيذ {dpi} DPI. أقرب خطوة يدعمها هي {closest}.", diff --git a/src/i18n-de.ts b/src/i18n-de.ts index 7aa93016..81b7e6ce 100644 --- a/src/i18n-de.ts +++ b/src/i18n-de.ts @@ -678,6 +678,7 @@ export const de: Record = { "ctl.reconnecting": "Wird wiederverbunden", "ctl.readingPrev": "Das zuvor autorisierte Gerät wird gelesen.", "ctl.useAdd": "Nutze Gerät hinzufügen, falls sich die Maus nicht automatisch neu verbindet.", + "ctl.razerBluetooth": "Die Basilisk V3 Pro lässt sich über Bluetooth noch nicht einstellen. Stelle sie mit dem Dongle auf HyperSpeed (2,4 GHz) um oder schließe das Kabel an.", "ctl.couldNotReconnect": "Die Maus konnte nicht wiederverbunden werden", "ctl.dpiUnsupported": "Dieser DPI-Wert wird von dieser Maus nicht unterstützt.", "ctl.closestStep": "Diese Maus kann keine {dpi} DPI. Der nächstgelegene unterstützte Schritt ist {closest}.", diff --git a/src/i18n-es.ts b/src/i18n-es.ts index 4b2e7281..79004d20 100644 --- a/src/i18n-es.ts +++ b/src/i18n-es.ts @@ -678,6 +678,7 @@ export const es: Record = { "ctl.reconnecting": "Reconectando", "ctl.readingPrev": "Leyendo el dispositivo previamente autorizado.", "ctl.useAdd": "Usa Agregar dispositivo si el mouse no se reconecta automáticamente.", + "ctl.razerBluetooth": "El Basilisk V3 Pro aún no se puede configurar por Bluetooth. Cámbialo a HyperSpeed (2,4 GHz) con el dongle o conecta el cable.", "ctl.couldNotReconnect": "No se pudo reconectar el mouse", "ctl.dpiUnsupported": "Ese valor de DPI no es compatible con este mouse.", "ctl.closestStep": "Este mouse no puede hacer {dpi} DPI. El paso más cercano que admite es {closest}.", diff --git a/src/i18n-fr.ts b/src/i18n-fr.ts index fa29ef5d..675ca501 100644 --- a/src/i18n-fr.ts +++ b/src/i18n-fr.ts @@ -678,6 +678,7 @@ export const fr: Record = { "ctl.reconnecting": "Reconnexion", "ctl.readingPrev": "Lecture de l'appareil précédemment autorisé.", "ctl.useAdd": "Utilisez Ajouter un appareil si la souris ne se reconnecte pas automatiquement.", + "ctl.razerBluetooth": "La Basilisk V3 Pro ne peut pas encore être configurée en Bluetooth. Passez-la en HyperSpeed (2,4 GHz) avec le dongle, ou branchez le câble.", "ctl.couldNotReconnect": "Impossible de reconnecter la souris", "ctl.dpiUnsupported": "Cette valeur DPI n'est pas prise en charge par cette souris.", "ctl.closestStep": "Cette souris ne peut pas faire {dpi} DPI. Le pas le plus proche pris en charge est {closest}.", diff --git a/src/i18n-ja.ts b/src/i18n-ja.ts index 0b9d35b6..ccd5545d 100644 --- a/src/i18n-ja.ts +++ b/src/i18n-ja.ts @@ -678,6 +678,7 @@ export const ja: Record = { "ctl.reconnecting": "再接続中", "ctl.readingPrev": "以前に許可されたデバイスを読み取り中です。", "ctl.useAdd": "マウスが自動的に再接続しない場合は「デバイスを追加」を使用してください。", + "ctl.razerBluetooth": "Basilisk V3 Pro はまだ Bluetooth では設定できません。ドングルで HyperSpeed(2.4 GHz)に切り替えるか、ケーブルを接続してください。", "ctl.couldNotReconnect": "マウスを再接続できませんでした", "ctl.dpiUnsupported": "このDPI値はこのマウスでサポートされていません。", "ctl.closestStep": "このマウスは{dpi} DPIに対応していません。対応している最も近い値は{closest}です。", diff --git a/src/i18n-ko.ts b/src/i18n-ko.ts index 3b59959a..5fd86dd3 100644 --- a/src/i18n-ko.ts +++ b/src/i18n-ko.ts @@ -678,6 +678,7 @@ export const ko: Record = { "ctl.reconnecting": "재연결 중", "ctl.readingPrev": "이전에 승인된 장치를 읽는 중입니다.", "ctl.useAdd": "마우스가 자동으로 재연결되지 않으면 \"장치 추가\"를 사용하세요.", + "ctl.razerBluetooth": "Basilisk V3 Pro는 아직 블루투스로 설정할 수 없습니다. 동글을 사용해 HyperSpeed(2.4GHz)로 전환하거나 케이블을 연결하세요.", "ctl.couldNotReconnect": "마우스를 재연결할 수 없습니다", "ctl.dpiUnsupported": "이 DPI 값은 이 마우스에서 지원되지 않습니다.", "ctl.closestStep": "이 마우스는 {dpi} DPI를 지원하지 않습니다. 지원되는 가장 가까운 값은 {closest}입니다.", diff --git a/src/i18n-pt.ts b/src/i18n-pt.ts index 7ef466a2..a1ec7f9e 100644 --- a/src/i18n-pt.ts +++ b/src/i18n-pt.ts @@ -678,6 +678,7 @@ export const pt: Record = { "ctl.reconnecting": "Reconectando", "ctl.readingPrev": "Lendo o dispositivo autorizado anterior.", "ctl.useAdd": "Use Adicionar dispositivo se o mouse não reconectar sozinho.", + "ctl.razerBluetooth": "O Basilisk V3 Pro ainda não pode ser configurado por Bluetooth. Mude-o para HyperSpeed (2,4 GHz) com o dongle ou conecte o cabo.", "ctl.couldNotReconnect": "Não foi possível reconectar o mouse", "ctl.dpiUnsupported": "Este valor de DPI não é suportado por este mouse.", "ctl.closestStep": "Este mouse não faz {dpi} DPI. O passo mais próximo é {closest}.", diff --git a/src/i18n-ru.ts b/src/i18n-ru.ts index 5b0232d3..21142f75 100644 --- a/src/i18n-ru.ts +++ b/src/i18n-ru.ts @@ -677,6 +677,7 @@ export const ru: Record = { "ctl.reconnecting": "Переподключение", "ctl.readingPrev": "Читаю ранее разрешённое устройство.", "ctl.useAdd": "Нажмите «Добавить устройство», если мышь не подключилась сама.", + "ctl.razerBluetooth": "Basilisk V3 Pro пока нельзя настроить по Bluetooth. Переключите мышь в режим HyperSpeed (2,4 ГГц) с донглом или подключите кабель.", "ctl.couldNotReconnect": "Не удалось переподключить мышь", "ctl.dpiUnsupported": "Это значение DPI не поддерживается данной мышью.", "ctl.closestStep": "Эта мышь не умеет {dpi} DPI. Ближайший поддерживаемый шаг — {closest}.", diff --git a/src/i18n-vi.ts b/src/i18n-vi.ts index 0261042d..97dd0e60 100644 --- a/src/i18n-vi.ts +++ b/src/i18n-vi.ts @@ -626,6 +626,7 @@ export const vi: Record = { "ctl.reconnecting": "Đang kết nối lại", "ctl.readingPrev": "Đang đọc thiết bị đã được cấp quyền trước đó.", "ctl.useAdd": "Dùng Thêm thiết bị nếu chuột không tự động kết nối lại.", + "ctl.razerBluetooth": "Basilisk V3 Pro chưa thể cài đặt qua Bluetooth. Hãy chuyển sang HyperSpeed (2,4 GHz) với dongle hoặc cắm cáp.", "ctl.couldNotReconnect": "Không thể kết nối lại với chuột", "ctl.dpiUnsupported": "Giá trị DPI đó không được chuột này hỗ trợ.", "ctl.closestStep": "Chuột này không hỗ trợ {dpi} DPI. Mức gần nhất được hỗ trợ là {closest}.", diff --git a/src/i18n-zh.ts b/src/i18n-zh.ts index 46f3c9d6..f1eaaeeb 100644 --- a/src/i18n-zh.ts +++ b/src/i18n-zh.ts @@ -678,6 +678,7 @@ export const zh: Record = { "ctl.reconnecting": "重新连接中", "ctl.readingPrev": "正在读取此前已授权的设备。", "ctl.useAdd": "如果鼠标未能自动重连,请使用“添加设备”。", + "ctl.razerBluetooth": "Basilisk V3 Pro 暂不支持通过蓝牙设置。请使用接收器切换到 HyperSpeed(2.4 GHz)模式,或连接数据线。", "ctl.couldNotReconnect": "无法重新连接鼠标", "ctl.dpiUnsupported": "此鼠标不支持该 DPI 数值。", "ctl.closestStep": "此鼠标不支持 {dpi} DPI。最接近的支持挡位是 {closest}。", diff --git a/src/i18n.ts b/src/i18n.ts index 7fd1227d..47263068 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -681,6 +681,7 @@ const en = { "ctl.reconnecting": "Reconnecting", "ctl.readingPrev": "Reading the previously authorized device.", "ctl.useAdd": "Use Add device if the mouse does not reconnect automatically.", + "ctl.razerBluetooth": "The Basilisk V3 Pro can't be set up over Bluetooth yet. Switch it to HyperSpeed (2.4 GHz) with the dongle, or plug in the cable.", "ctl.couldNotReconnect": "Could not reconnect the mouse", "ctl.dpiUnsupported": "That DPI value is not supported by this mouse.", "ctl.closestStep": "This mouse cannot do {dpi} DPI. The closest step it supports is {closest}.",