进阶 electronjs.org 2026-10-07 23:51:43 · 6 阅读
第18章 设备访问
设备访问
与基于 Chromium 的浏览器类似,Electron 通过 Web API 提供对设备硬件的访问。这些 API 的行为大多与浏览器中一致,但也存在一些需要留意的差异。Electron 与浏览器最核心的区别在于请求访问设备时的处理机制:在浏览器中,系统会弹出窗口让用户授权访问特定设备;而在 Electron 中,API 允许开发者自行选择设备,或通过自定义界面引导用户选择设备。
Web Bluetooth API
Web Bluetooth API 可用于与蓝牙设备通信。要在 Electron 中使用此 API,开发者需要处理与设备请求关联的 webContents 的 select-bluetooth-device 事件。
此外,在 Windows 或 Linux 上,如果需要 PIN 码等额外验证,可以使用 ses.setBluetoothPairingHandler(handler) 来处理蓝牙设备的配对。
示例
以下示例展示了一个 Electron 应用程序:当点击“测试蓝牙”按钮时,应用会自动选择第一个可用的蓝牙设备。
docs/fiddles/features/web-bluetooth (44.6.0)Open in Fiddlemain.jspreload.jsindex.htmlrenderer.jsconst { 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', () => {
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
// 向渲染进程发送消息,提示用户确认配对。
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()
})
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {
cancelBluetoothRequest: () => ipcRenderer.send('cancel-bluetooth-request'),
bluetoothPairingRequest: (callback) => ipcRenderer.on('bluetooth-pairing-request', () => callback()),
bluetoothPairingResponse: (response) => ipcRenderer.send('bluetooth-pairing-response', response)
})
Web Bluetooth API
async function testIt () {
const device = await navigator.bluetooth.requestDevice({
acceptAllDevices: true
})
document.getElementById('device-name').innerHTML = device.name || `ID: ${device.id}`
}
document.getElementById('clickme').addEventListener('click', testIt)
function cancelRequest () {
window.electronAPI.cancelBluetoothRequest()
}
document.getElementById('cancel').addEventListener('click', cancelRequest)
window.electronAPI.bluetoothPairingRequest((event, details) => {
const response = {}
switch (details.pairingKind) {
case 'confirm': {
response.confirmed = window.confirm(`是否要连接到设备 ${details.deviceId}?`)
break
}
case 'confirmPin': {
response.confirmed = window.confirm(`设备 ${details.deviceId} 上显示的 pin 是否为 ${details.pin}?`)
break
}
case 'providePin': {
const pin = window.prompt(`请输入 ${details.deviceId} 的 pin。`)
if (pin) {
response.pin = pin
response.confirmed = true
} else {
response.confirmed = false
}
}
}
window.electronAPI.bluetoothPairingResponse(response)
})
WebHID API WebHID API 可用于访问键盘、游戏手柄等 HID 设备。Electron 提供了多个与 WebHID API 配合使用的接口: Session 上的 select-hid-device 事件可在调用 navigator.hid.requestDevice 时用于选择 HID 设备。此外,Session 上的 hid-device-added 和 hid-device-removed 事件可用于处理 select-hid-device 事件期间设备的插入和拔出。 注意:这些事件只在 select-hid-device 的回调被调用之前触发,并不适合用作通用的 HID 设备监听器。 ses.setDevicePermissionHandler(handler) 可以为设备提供默认权限,而无需先通过 navigator.hid.requestDevice 请求权限。另外,Electron 的默认行为是将已授予的设备权限保存到对应 WebContents 的整个生命周期中。如果需要更长期的存储,开发者可以自行保存已授予的设备权限(例如在处理 select-hid-device 事件时),然后在 setDevicePermissionHandler 中读取该存储。 ses.setPermissionCheckHandler(handler) 可用于禁止特定 origin 访问 HID。 Blocklist(黑名单) 默认情况下,Electron 使用与 Chromium 相同的黑名单。如果想覆盖这个行为,可以设置 disable-hid-blocklist 标志: app.commandLine.appendSwitch('disable-hid-blocklist')
示例 这个示例演示了一个 Electron 应用:点击 Test WebHID 按钮时,通过 ses.setDevicePermissionHandler(handler) 和 Session 上的 select-hid-device 事件自动选择 HID 设备。 docs/fiddles/features/web-hid (44.6.0)Open in Fiddlemain.jsindex.htmlrenderer.jsconst { 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) => {
// 在调用 `select-hid-device` 的回调函数之前,先添加用于处理设备添加或移除事件的逻辑。
mainWindow.webContents.session.on('hid-device-added', (event, device) => {
console.log('hid-device-added FIRED WITH', device)
// 可选择性地更新 details.deviceList
})
mainWindow.webContents.session.on('hid-device-removed', (event, device) => {
console.log('hid-device-removed FIRED WITH', device)
// 可选择性地更新 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()
})
WebHID API
function formatDevices (devices) {
return devices.map(device => device.productName).join('
')
}
async function testIt () {
document.getElementById('granted-devices').innerHTML = formatDevices(await navigator.hid.getDevices())
document.getElementById('granted-devices2').innerHTML = formatDevices(await navigator.hid.requestDevice({ filters: [] }))
}
document.getElementById('clickme').addEventListener('click', testIt)
Web Serial API Web Serial API 可用于访问通过串口、USB 或蓝牙连接的设备。要在 Electron 中使用该 API,开发者需要处理与串口请求关联的 Session 上的 select-serial-port 事件。 Electron 提供了几个额外的 API 来配合 Web Serial API 使用: * 可以通过监听 Session 上的 serial-port-added 和 serial-port-removed 事件,在 select-serial-port 回调中处理设备插入或拔出的情况。 注意:这些事件仅在 select-serial-port 的回调被调用之前触发。它们不应作为通用的串口监听器使用。 * ses.setDevicePermissionHandler(handler) 可用于在通过 navigator.serial.requestPort 请求设备权限之前,为设备提供默认权限。此外,Electron 的默认行为是在对应的 WebContents 生命周期内存储已授予的设备权限。如果需要更长期的存储,开发者可以存储已授予的设备权限(例如在处理 select-serial-port 事件时),然后通过 setDevicePermissionHandler 从该存储中读取。 * ses.setPermissionCheckHandler(handler) 可用于禁用特定源(origins)的串口访问权限。 黑名单 默认情况下,Electron 使用 Chromium 相同的黑名单。如果希望覆盖此行为,可以设置 disable-serial-blocklist 标志: app.commandLine.appendSwitch('disable-serial-blocklist')
示例 本示例展示了一个 Electron 应用程序,该程序通过 ses.setDevicePermissionHandler(handler) 自动选择串口设备,并在点击“测试 Web Serial”按钮时,通过 Session 上的 select-serial-port 事件选择第一个可用的 Arduino Uno 串口设备(如果已连接)。 docs/fiddles/features/web-serial (44.6.0)Open in Fiddlemain.jsindex.htmlrenderer.jsconst { 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) => {
// 添加监听器,处理在 `select-serial-port` 回调
// 被调用之前新增或移除的端口。
mainWindow.webContents.session.on('serial-port-added', (event, port) => {
console.log('serial-port-added FIRED WITH', port)
// 可选择更新 portList,加入新端口
})
mainWindow.webContents.session.on('serial-port-removed', (event, port) => {
console.log('serial-port-removed FIRED WITH', port)
// 可选择更新 portList,移除该端口
})
event.preventDefault()
if (portList && portList.length > 0) {
callback(portList[0].portId)
} else {
callback('') // 找不到匹配的设备
}
})
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()
})
Web Serial API
async function testIt () {
const filters = [
{ usbVendorId: 0x2341, usbProductId: 0x0043 },
{ usbVendorId: 0x2341, usbProductId: 0x0001 }
]
try {
const port = await navigator.serial.requestPort({ filters })
const portInfo = port.getInfo()
The WebUSB API can be used to access USB devices. Electron provides several APIs for working with the WebUSB API: The select-usb-device event on the Session can be used to select a USB device when a call to navigator.usb.requestDevice is made. Additionally, the usb-device-added and usb-device-removed events on the Session can be used to handle devices being plugged in or unplugged when handling the select-usb-device event. Note: These two events only fire until the callback from select-usb-device is called. They are not intended to be used as a generic USB device listener. The usb-device-revoked event on the Session can be used to respond when device.forget() is called on a USB device. ses.setDevicePermissionHandler(handler) can be used to provide default permissioning to devices without first calling for permission to devices via navigator.usb.requestDevice. Additionally, the default behavior of Electron is to store granted device permissions through the lifetime of the corresponding WebContents. If longer-term storage is needed, a developer can store granted device permissions (e.g., when handling the select-usb-device event) and then read from that storage with setDevicePermissionHandler. ses.setPermissionCheckHandler(handler) can be used to disable USB access for specific origins. `ses.setUSBProtectedClassesHandler` can be used to allow usage of protected USB classes that are not available by default. 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-usb-blocklist flag: `app.commandLine.appendSwitch('disable-usb-blocklist')` 示例 本示例演示了一个 Electron 应用程序,当点击“测试 WebUSB”按钮时,会通过 `ses.setDevicePermissionHandler(handler)` 和 Session 上的 `select-usb-device` 事件,自动选择已连接的 USB 设备。 docs/fiddles/features/web-usb (44.6.0) Open in Fiddle main.js index.html renderer.js 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) => {
// 在 `select-usb-device` 回调被调用前,添加事件以处理设备的添加或移除。
mainWindow.webContents.session.on('usb-device-added', (event, device) => {
console.log('usb-device-added FIRED WITH', device)
// 可选:更新 details.deviceList
})
mainWindow.webContents.session.on('usb-device-removed', (event, device) => {
console.log('usb-device-removed FIRED WITH', device)
// 可选:更新 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) => {
// 排除音频类以外的所有类
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()
})
WebUSB API
function getDeviceDetails (device) {
return device.productName || `未知设备 ${device.deviceId}`
}
async function testIt () {
const noDevicesFoundMsg = '未找到设备'
const grantedDevices = await navigator.usb.getDevices()
let grantedDeviceList = ''
if (grantedDevices.length > 0) {
for (const device of grantedDevices) {
grantedDeviceList += `
${getDeviceDetails(device)}`
}
} else {
grantedDeviceList = noDevicesFoundMsg
}
document.getElementById('granted-devices').innerHTML = grantedDeviceList
grantedDeviceList = ''
try {
const grantedDevice = await navigator.usb.requestDevice({
filters: []
})
grantedDeviceList += `
${getDeviceDetails(grantedDevice)}`
} catch (ex) {
if (ex.name === 'NotFoundError') {
grantedDeviceList = noDevicesFoundMsg
}
}
document.getElementById('granted-devices2').innerHTML = grantedDeviceList
}
document.getElementById('clickme').addEventListener('click', testIt)
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', () => {
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
// 向渲染进程发送消息,提示用户确认配对。
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()
})
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {
cancelBluetoothRequest: () => ipcRenderer.send('cancel-bluetooth-request'),
bluetoothPairingRequest: (callback) => ipcRenderer.on('bluetooth-pairing-request', () => callback()),
bluetoothPairingResponse: (response) => ipcRenderer.send('bluetooth-pairing-response', response)
})
当前选中的蓝牙设备:
async function testIt () {
const device = await navigator.bluetooth.requestDevice({
acceptAllDevices: true
})
document.getElementById('device-name').innerHTML = device.name || `ID: ${device.id}`
}
document.getElementById('clickme').addEventListener('click', testIt)
function cancelRequest () {
window.electronAPI.cancelBluetoothRequest()
}
document.getElementById('cancel').addEventListener('click', cancelRequest)
window.electronAPI.bluetoothPairingRequest((event, details) => {
const response = {}
switch (details.pairingKind) {
case 'confirm': {
response.confirmed = window.confirm(`是否要连接到设备 ${details.deviceId}?`)
break
}
case 'confirmPin': {
response.confirmed = window.confirm(`设备 ${details.deviceId} 上显示的 pin 是否为 ${details.pin}?`)
break
}
case 'providePin': {
const pin = window.prompt(`请输入 ${details.deviceId} 的 pin。`)
if (pin) {
response.pin = pin
response.confirmed = true
} else {
response.confirmed = false
}
}
}
window.electronAPI.bluetoothPairingResponse(response)
})
WebHID API WebHID API 可用于访问键盘、游戏手柄等 HID 设备。Electron 提供了多个与 WebHID API 配合使用的接口: Session 上的 select-hid-device 事件可在调用 navigator.hid.requestDevice 时用于选择 HID 设备。此外,Session 上的 hid-device-added 和 hid-device-removed 事件可用于处理 select-hid-device 事件期间设备的插入和拔出。 注意:这些事件只在 select-hid-device 的回调被调用之前触发,并不适合用作通用的 HID 设备监听器。 ses.setDevicePermissionHandler(handler) 可以为设备提供默认权限,而无需先通过 navigator.hid.requestDevice 请求权限。另外,Electron 的默认行为是将已授予的设备权限保存到对应 WebContents 的整个生命周期中。如果需要更长期的存储,开发者可以自行保存已授予的设备权限(例如在处理 select-hid-device 事件时),然后在 setDevicePermissionHandler 中读取该存储。 ses.setPermissionCheckHandler(handler) 可用于禁止特定 origin 访问 HID。 Blocklist(黑名单) 默认情况下,Electron 使用与 Chromium 相同的黑名单。如果想覆盖这个行为,可以设置 disable-hid-blocklist 标志: app.commandLine.appendSwitch('disable-hid-blocklist')
示例 这个示例演示了一个 Electron 应用:点击 Test WebHID 按钮时,通过 ses.setDevicePermissionHandler(handler) 和 Session 上的 select-hid-device 事件自动选择 HID 设备。 docs/fiddles/features/web-hid (44.6.0)Open in Fiddlemain.jsindex.htmlrenderer.jsconst { 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) => {
// 在调用 `select-hid-device` 的回调函数之前,先添加用于处理设备添加或移除事件的逻辑。
mainWindow.webContents.session.on('hid-device-added', (event, device) => {
console.log('hid-device-added FIRED WITH', device)
// 可选择性地更新 details.deviceList
})
mainWindow.webContents.session.on('hid-device-removed', (event, device) => {
console.log('hid-device-removed FIRED WITH', device)
// 可选择性地更新 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()
})
WebHID API
通过 setDevicePermissionHandler 自动授予访问权限的 HID 设备
通过 select-hid-device 自动授予访问权限的 HID 设备
function formatDevices (devices) {
return devices.map(device => device.productName).join('
')
}
async function testIt () {
document.getElementById('granted-devices').innerHTML = formatDevices(await navigator.hid.getDevices())
document.getElementById('granted-devices2').innerHTML = formatDevices(await navigator.hid.requestDevice({ filters: [] }))
}
document.getElementById('clickme').addEventListener('click', testIt)
Web Serial API Web Serial API 可用于访问通过串口、USB 或蓝牙连接的设备。要在 Electron 中使用该 API,开发者需要处理与串口请求关联的 Session 上的 select-serial-port 事件。 Electron 提供了几个额外的 API 来配合 Web Serial API 使用: * 可以通过监听 Session 上的 serial-port-added 和 serial-port-removed 事件,在 select-serial-port 回调中处理设备插入或拔出的情况。 注意:这些事件仅在 select-serial-port 的回调被调用之前触发。它们不应作为通用的串口监听器使用。 * ses.setDevicePermissionHandler(handler) 可用于在通过 navigator.serial.requestPort 请求设备权限之前,为设备提供默认权限。此外,Electron 的默认行为是在对应的 WebContents 生命周期内存储已授予的设备权限。如果需要更长期的存储,开发者可以存储已授予的设备权限(例如在处理 select-serial-port 事件时),然后通过 setDevicePermissionHandler 从该存储中读取。 * ses.setPermissionCheckHandler(handler) 可用于禁用特定源(origins)的串口访问权限。 黑名单 默认情况下,Electron 使用 Chromium 相同的黑名单。如果希望覆盖此行为,可以设置 disable-serial-blocklist 标志: app.commandLine.appendSwitch('disable-serial-blocklist')
示例 本示例展示了一个 Electron 应用程序,该程序通过 ses.setDevicePermissionHandler(handler) 自动选择串口设备,并在点击“测试 Web Serial”按钮时,通过 Session 上的 select-serial-port 事件选择第一个可用的 Arduino Uno 串口设备(如果已连接)。 docs/fiddles/features/web-serial (44.6.0)Open in Fiddlemain.jsindex.htmlrenderer.jsconst { 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) => {
// 添加监听器,处理在 `select-serial-port` 回调
// 被调用之前新增或移除的端口。
mainWindow.webContents.session.on('serial-port-added', (event, port) => {
console.log('serial-port-added FIRED WITH', port)
// 可选择更新 portList,加入新端口
})
mainWindow.webContents.session.on('serial-port-removed', (event, port) => {
console.log('serial-port-removed FIRED WITH', port)
// 可选择更新 portList,移除该端口
})
event.preventDefault()
if (portList && portList.length > 0) {
callback(portList[0].portId)
} else {
callback('') // 找不到匹配的设备
}
})
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()
})
Web Serial API
匹配的 Arduino Uno 设备:
async function testIt () {
const filters = [
{ usbVendorId: 0x2341, usbProductId: 0x0043 },
{ usbVendorId: 0x2341, usbProductId: 0x0001 }
]
try {
const port = await navigator.serial.requestPort({ filters })
const portInfo = port.getInfo()
The WebUSB API can be used to access USB devices. Electron provides several APIs for working with the WebUSB API: The select-usb-device event on the Session can be used to select a USB device when a call to navigator.usb.requestDevice is made. Additionally, the usb-device-added and usb-device-removed events on the Session can be used to handle devices being plugged in or unplugged when handling the select-usb-device event. Note: These two events only fire until the callback from select-usb-device is called. They are not intended to be used as a generic USB device listener. The usb-device-revoked event on the Session can be used to respond when device.forget() is called on a USB device. ses.setDevicePermissionHandler(handler) can be used to provide default permissioning to devices without first calling for permission to devices via navigator.usb.requestDevice. Additionally, the default behavior of Electron is to store granted device permissions through the lifetime of the corresponding WebContents. If longer-term storage is needed, a developer can store granted device permissions (e.g., when handling the select-usb-device event) and then read from that storage with setDevicePermissionHandler. ses.setPermissionCheckHandler(handler) can be used to disable USB access for specific origins. `ses.setUSBProtectedClassesHandler` can be used to allow usage of protected USB classes that are not available by default. 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-usb-blocklist flag: `app.commandLine.appendSwitch('disable-usb-blocklist')` 示例 本示例演示了一个 Electron 应用程序,当点击“测试 WebUSB”按钮时,会通过 `ses.setDevicePermissionHandler(handler)` 和 Session 上的 `select-usb-device` 事件,自动选择已连接的 USB 设备。 docs/fiddles/features/web-usb (44.6.0) Open in Fiddle main.js index.html renderer.js 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) => {
// 在 `select-usb-device` 回调被调用前,添加事件以处理设备的添加或移除。
mainWindow.webContents.session.on('usb-device-added', (event, device) => {
console.log('usb-device-added FIRED WITH', device)
// 可选:更新 details.deviceList
})
mainWindow.webContents.session.on('usb-device-removed', (event, device) => {
console.log('usb-device-removed FIRED WITH', device)
// 可选:更新 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) => {
// 排除音频类以外的所有类
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()
})
WebUSB API
通过 setDevicePermissionHandler 自动授权访问的 USB 设备
通过 select-usb-device 自动授权访问的 USB 设备
function getDeviceDetails (device) {
return device.productName || `未知设备 ${device.deviceId}`
}
async function testIt () {
const noDevicesFoundMsg = '未找到设备'
const grantedDevices = await navigator.usb.getDevices()
let grantedDeviceList = ''
if (grantedDevices.length > 0) {
for (const device of grantedDevices) {
grantedDeviceList += `
${getDeviceDetails(device)}`
}
} else {
grantedDeviceList = noDevicesFoundMsg
}
document.getElementById('granted-devices').innerHTML = grantedDeviceList
grantedDeviceList = ''
try {
const grantedDevice = await navigator.usb.requestDevice({
filters: []
})
grantedDeviceList += `
${getDeviceDetails(grantedDevice)}`
} catch (ex) {
if (ex.name === 'NotFoundError') {
grantedDeviceList = noDevicesFoundMsg
}
}
document.getElementById('granted-devices2').innerHTML = grantedDeviceList
}
document.getElementById('clickme').addEventListener('click', testIt)