Skip to main content

设备访问

设备访问

¥Device Access

与基于 Chromium 的浏览器一样,Electron 通过 Web API 提供对设备硬件的访问。大多数情况下,这些 API 的工作方式与在浏览器中的工作方式相同,但需要考虑一些差异。Electron 和浏览器之间的主要区别在于请求设备访问时会发生什么。在浏览器中,用户会看到一个弹出窗口,他们可以在其中授予对单个设备的访问权限。Electron 提供了 API,开发者可以使用这些 API 自动选择设备或提示用户通过开发者创建的界面选择设备。

¥Like Chromium based browsers, Electron provides access to device hardware through web APIs. For the most part these APIs work like they do in a browser, but there are some differences that need to be taken into account. The primary difference between Electron and browsers is what happens when device access is requested. In a browser, users are presented with a popup where they can grant access to an individual device. In Electron APIs are provided which can be used by a developer to either automatically pick a device or prompt users to pick a device via a developer created interface.

网络蓝牙 API

¥Web Bluetooth API

网络蓝牙 API 可用于与蓝牙设备通信。为了在 Electron 中使用此 API,开发者需要处理与设备请求关联的 网络上的 select-bluetooth-device 活动内容

¥The Web Bluetooth API can be used to communicate with bluetooth devices. In order to use this API in Electron, developers will need to handle the select-bluetooth-device event on the webContents associated with the device request.

此外,当需要额外的验证(例如引脚)时,ses.setBluetoothPairingHandler(handler) 可用于处理与 Windows 或 Linux 上的蓝牙设备的配对。

¥Additionally, ses.setBluetoothPairingHandler(handler) can be used to handle pairing to bluetooth devices on Windows or Linux when additional validation such as a pin is needed.

示例

¥Example

此示例演示了一个 Electron 应用,当单击 Test Bluetooth 按钮时,该应用会自动选择第一个可用的蓝牙设备。

¥This example demonstrates an Electron application that automatically selects the first available bluetooth device when the Test Bluetooth button is clicked.

const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')

let bluetoothPinCallback
let selectBluetoothCallback

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
event.preventDefault()
selectBluetoothCallback = callback
const result = deviceList.find((device) => {
return device.deviceName === 'test'
})
if (result) {
callback(result.deviceId)
} else {
// The device wasn't found so we need to either wait longer (eg until the
// device is turned on) or until the user cancels the request
}
})

ipcMain.on('cancel-bluetooth-request', (event) => {
selectBluetoothCallback('')
})

// Listen for a message from the renderer to get the response for the Bluetooth pairing.
ipcMain.on('bluetooth-pairing-response', (event, response) => {
bluetoothPinCallback(response)
})

mainWindow.webContents.session.setBluetoothPairingHandler((details, callback) => {
bluetoothPinCallback = callback
// Send a message to the renderer to prompt the user to confirm the pairing.
mainWindow.webContents.send('bluetooth-pairing-request', details)
})

mainWindow.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()

app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})

WebHID API

WebHID API 可用于访问 HID 设备,例如键盘和游戏句柄。Electron 提供了多个用于使用 WebHID API 的 API:

¥The WebHID API can be used to access HID devices such as keyboards and gamepads. Electron provides several APIs for working with the WebHID API:

  • 当调用 navigator.hid.requestDevice 时,会议上的 select-hid-device 事件 可用于选择 HID 设备。此外,会话上的 hid-device-addedhid-device-removed 事件可用于在处理 select-hid-device 事件时处理插入或拔出的设备。注意:这些事件仅在调用 select-hid-device 的回调之前触发。它们不打算用作通用 hid 设备监听器。

    ¥The select-hid-device event on the Session can be used to select a HID device when a call to navigator.hid.requestDevice is made. Additionally the hid-device-added and hid-device-removed events on the Session can be used to handle devices being plugged in or unplugged when handling the select-hid-device event. Note: These events only fire until the callback from select-hid-device is called. They are not intended to be used as a generic hid device listener.

  • ses.setDevicePermissionHandler(handler) 可用于向设备提供默认权限,而无需首先通过 navigator.hid.requestDevice 请求设备权限。此外,Electron 的默认行为是在相应 WebContent 的生命周期内存储授予的设备权限。如果需要长期存储,开发者可以存储授予的设备权限(例如,在处理 select-hid-device 事件时),然后使用 setDevicePermissionHandler 从该存储中读取。

    ¥ses.setDevicePermissionHandler(handler) can be used to provide default permissioning to devices without first calling for permission to devices via navigator.hid.requestDevice. Additionally, the default behavior of Electron is to store granted device permission through the lifetime of the corresponding WebContents. If longer term storage is needed, a developer can store granted device permissions (eg when handling the select-hid-device event) and then read from that storage with setDevicePermissionHandler.

  • ses.setPermissionCheckHandler(handler) 可用于禁用特定来源的 HID 访问。

    ¥ses.setPermissionCheckHandler(handler) can be used to disable HID access for specific origins.

黑名单

¥Blocklist

默认情况下,Electron 使用与 Chromium 相同的 blocklist。如果你希望覆盖此行为,可以通过设置 disable-hid-blocklist 标志来实现:

¥By default Electron employs the same blocklist used by Chromium. If you wish to override this behavior, you can do so by setting the disable-hid-blocklist flag:

app.commandLine.appendSwitch('disable-hid-blocklist')

示例

¥Example

此示例演示了一个 Electron 应用,当单击 Test WebHID 按钮时,该应用会通过 ses.setDevicePermissionHandler(handler)会议上的 select-hid-device 事件 自动选择 HID 设备。

¥This example demonstrates an Electron application that automatically selects HID devices through ses.setDevicePermissionHandler(handler) and through select-hid-device event on the Session when the Test WebHID button is clicked.

const { app, BrowserWindow } = require('electron/main')

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
})

mainWindow.webContents.session.on('select-hid-device', (event, details, callback) => {
// Add events to handle devices being added or removed before the callback on
// `select-hid-device` is called.
mainWindow.webContents.session.on('hid-device-added', (event, device) => {
console.log('hid-device-added FIRED WITH', device)
// Optionally update details.deviceList
})

mainWindow.webContents.session.on('hid-device-removed', (event, device) => {
console.log('hid-device-removed FIRED WITH', device)
// Optionally update details.deviceList
})

event.preventDefault()
if (details.deviceList && details.deviceList.length > 0) {
callback(details.deviceList[0].deviceId)
}
})

mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
if (permission === 'hid' && details.securityOrigin === 'file:///') {
return true
}
})

mainWindow.webContents.session.setDevicePermissionHandler((details) => {
if (details.deviceType === 'hid' && details.origin === 'file://') {
return true
}
})

mainWindow.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()

app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})

网络串行 API

¥Web Serial API

网络串行 API 可用于访问通过串行端口、USB 或蓝牙连接的串行设备。为了在 Electron 中使用此 API,开发者需要处理与串行端口请求关联的 会议上的 select-serial-port 事件

¥The Web Serial API can be used to access serial devices that are connected via serial port, USB, or Bluetooth. In order to use this API in Electron, developers will need to handle the select-serial-port event on the Session associated with the serial port request.

还有几个额外的 API 可用于使用 Web Serial API:

¥There are several additional APIs for working with the Web Serial API:

  • Session 上的 serial-port-addedserial-port-removed 事件可用于在处理 select-serial-port 事件时处理设备的插入或拔出。注意:这些事件仅在调用 select-serial-port 的回调之前触发。它们不打算用作通用串行端口监听器。

    ¥The serial-port-added and serial-port-removed events on the Session can be used to handle devices being plugged in or unplugged when handling the select-serial-port event. Note: These events only fire until the callback from select-serial-port is called. They are not intended to be used as a generic serial port listener.

  • ses.setDevicePermissionHandler(handler) 可用于向设备提供默认权限,而无需首先通过 navigator.serial.requestPort 请求设备权限。此外,Electron 的默认行为是在相应 WebContent 的生命周期内存储授予的设备权限。如果需要长期存储,开发者可以存储授予的设备权限(例如,在处理 select-serial-port 事件时),然后使用 setDevicePermissionHandler 从该存储中读取。

    ¥ses.setDevicePermissionHandler(handler) can be used to provide default permissioning to devices without first calling for permission to devices via navigator.serial.requestPort. Additionally, the default behavior of Electron is to store granted device permission through the lifetime of the corresponding WebContents. If longer term storage is needed, a developer can store granted device permissions (eg when handling the select-serial-port event) and then read from that storage with setDevicePermissionHandler.

  • ses.setPermissionCheckHandler(handler) 可用于禁用特定来源的串行访问。

    ¥ses.setPermissionCheckHandler(handler) can be used to disable serial access for specific origins.

示例

¥Example

此示例演示了一个 Electron 应用,该应用通过 ses.setDevicePermissionHandler(handler) 自动选择串行设备,并演示了单击 Test Web Serial 按钮时通过 会议上的 select-serial-port 事件 选择第一个可用的 Arduino Uno 串行设备(如果已连接)。

¥This example demonstrates an Electron application that automatically selects serial devices through ses.setDevicePermissionHandler(handler) as well as demonstrating selecting the first available Arduino Uno serial device (if connected) through select-serial-port event on the Session when the Test Web Serial button is clicked.

const { app, BrowserWindow } = require('electron/main')

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
})

mainWindow.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
// Add listeners to handle ports being added or removed before the callback for `select-serial-port`
// is called.
mainWindow.webContents.session.on('serial-port-added', (event, port) => {
console.log('serial-port-added FIRED WITH', port)
// Optionally update portList to add the new port
})

mainWindow.webContents.session.on('serial-port-removed', (event, port) => {
console.log('serial-port-removed FIRED WITH', port)
// Optionally update portList to remove the port
})

event.preventDefault()
if (portList && portList.length > 0) {
callback(portList[0].portId)
} else {
// eslint-disable-next-line n/no-callback-literal
callback('') // Could not find any matching devices
}
})

mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
if (permission === 'serial' && details.securityOrigin === 'file:///') {
return true
}

return false
})

mainWindow.webContents.session.setDevicePermissionHandler((details) => {
if (details.deviceType === 'serial' && details.origin === 'file://') {
return true
}

return false
})

mainWindow.loadFile('index.html')

mainWindow.webContents.openDevTools()
}

app.whenReady().then(() => {
createWindow()

app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})

网络 USB API

¥WebUSB API

网络 USB API 可用于访问 USB 设备。Electron 提供了几个用于使用 WebUSB API 的 API:

¥The WebUSB API can be used to access USB devices. Electron provides several APIs for working with the WebUSB API:

示例

¥Example

此示例演示了一个 Electron 应用,当单击 Test WebUSB 按钮时,该应用会通过 ses.setDevicePermissionHandler(handler)会议上的 select-usb-device 事件 自动选择 USB 设备(如果已连接)。

¥This example demonstrates an Electron application that automatically selects USB devices (if they are attached) through ses.setDevicePermissionHandler(handler) and through select-usb-device event on the Session when the Test WebUSB button is clicked.

const { app, BrowserWindow } = require('electron/main')

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
})

let grantedDeviceThroughPermHandler

mainWindow.webContents.session.on('select-usb-device', (event, details, callback) => {
// Add events to handle devices being added or removed before the callback on
// `select-usb-device` is called.
mainWindow.webContents.session.on('usb-device-added', (event, device) => {
console.log('usb-device-added FIRED WITH', device)
// Optionally update details.deviceList
})

mainWindow.webContents.session.on('usb-device-removed', (event, device) => {
console.log('usb-device-removed FIRED WITH', device)
// Optionally update details.deviceList
})

event.preventDefault()
if (details.deviceList && details.deviceList.length > 0) {
const deviceToReturn = details.deviceList.find((device) => {
return !grantedDeviceThroughPermHandler || (device.deviceId !== grantedDeviceThroughPermHandler.deviceId)
})
if (deviceToReturn) {
callback(deviceToReturn.deviceId)
} else {
callback()
}
}
})

mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
if (permission === 'usb' && details.securityOrigin === 'file:///') {
return true
}
})

mainWindow.webContents.session.setDevicePermissionHandler((details) => {
if (details.deviceType === 'usb' && details.origin === 'file://') {
if (!grantedDeviceThroughPermHandler) {
grantedDeviceThroughPermHandler = details.device
return true
} else {
return false
}
}
})

mainWindow.webContents.session.setUSBProtectedClassesHandler((details) => {
return details.protectedClasses.filter((usbClass) => {
// Exclude classes except for audio classes
return usbClass.indexOf('audio') === -1
})
})

mainWindow.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()

app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})

app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})