设备访问

优质
小牛编辑
135浏览
2023-12-01

类似基于 Chromium 的浏览器一样, Electron 也提供了通过 web API 访问设备硬件的方法。 大部分接口就像在浏览器调用的 API 一样,但有一些差异需要考虑到。 Electron和浏览器之间的主要区别是请求访问设备时发生的情况。 在浏览器中,用户可以在弹出窗口中允许访问单独的设备。 在 Electron API中,提供了可供开发者自动选择设备或提示用户通过开发者创建的接口选择设备。

Web Bluetooth API

Web Bluetooth API 可以被用来连接蓝牙设备。 为了在 Electron 中使用此 API , 开发者将需要在 webContent 处理 select-bluetooth-device 事件 ,从而与设备请求相关联。

示例

这个示例演示了一个 Electron 应用程序,当点击了 Test Bluetooth 按钮时,它会自动选择第一个可用的蓝牙设备。

  • index.html
  • main.js
  • renderer.js
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Web Bluetooth API</title>
  </head>
  <body>
    <h1>Web Bluetooth API</h1>

    <button id="clickme">Test Bluetooth</button>

    <p>Currently selected bluetooth device: <strong id="device-name""></strong></p>

    <script src="./renderer.js"></script>
  </body>
</html>

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

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

  mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
    event.preventDefault()
    if (deviceList && deviceList.length > 0) {
      callback(deviceList[0].deviceId)
    } 
  })

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

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)
Open in Fiddle

WebHID API

WebHID API 可以用于访问HID 设备,例如 键盘和游戏机。 Electron 提供了几个使用 WebHID API的接口:

  • 调用 navigator.hid.requestDevice 并选择高清设备,将触发会话内的 select-hid-device 事件 此外,在调用 navigator.hid.requestDevice 过程中, 如果设备发生插拔(挂载和卸载)将触发 hid-device-addedhid-device-removed 事件。
  • 在第一次调用 navigator.hid.requestDevice 前, 可以通过 ses.setDevicePermissionHandler(handler) 给予设备默认权限, 此外,在 WebContents 的生命周期内,Electron 默认会存储授予的设备许可信息。 如果需要更长期的存储,开发人员可以存储设备许可信息(比如: 在处理 select-hid-device 事件时), 然后通过 setDevicePermissionHandler 从存储的信息中读取
  • ses.setPermissionCheckHandler(handler) 可以用于禁用特定来源的 HID 访问。

Blocklist

默认情况下,Electron 使用与 Chromium 相同的 blocklist 如果您想要覆盖此行为,您可以通过设置 disable-hid-blocklist 标志来做到这一点:

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

示例

这个示例演示了,当 Test WebHID 按钮被点击后,一个Electron 应用将通过 ses.setDevicePermissionHandler(handler)select-hid-device 会话事件 自动选择 HID 设备

  • index.html
  • main.js
  • renderer.js
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>WebHID API</title>
  </head>
  <body>
    <h1>WebHID API</h1>

    <button id="clickme">Test WebHID</button>

    <h3>HID devices automatically granted access via <i>setDevicePermissionHandler</i></h3>
    <div id="granted-devices"></div>
    
    <h3>HID devices automatically granted access via <i>select-hid-device</i></h3>
    <div id="granted-devices2"></div>

    <script src="./renderer.js"></script>
  </body>
</html>

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

function createWindow () {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600
  })
  
  mainWindow.webContents.session.on('select-hid-device', (event, details, callback) => {
    event.preventDefault()
    if (details.deviceList && details.deviceList.length > 0) {
      callback(details.deviceList[0].deviceId)
    }
  })

  mainWindow.webContents.session.on('hid-device-added', (event, device) => {    
    console.log('hid-device-added FIRED WITH', device)
  })

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

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

async function testIt() {
  const grantedDevices = await navigator.hid.getDevices()
  let grantedDeviceList = ''
  grantedDevices.forEach(device => {
    grantedDeviceList += `<hr>${device.productName}</hr>`
  })
  document.getElementById('granted-devices').innerHTML = grantedDeviceList
  const grantedDevices2 = await navigator.hid.requestDevice({
    filters: []
  })

  grantedDeviceList = ''
   grantedDevices2.forEach(device => {
    grantedDeviceList += `<hr>${device.productName}</hr>`
  })
  document.getElementById('granted-devices2').innerHTML = grantedDeviceList
}

document.getElementById('clickme').addEventListener('click',testIt)

Open in Fiddle

Web Serial API

Web Serial API 可以被用来访问串口设备比如 USB 或蓝牙。 为了在 Electron 中使用这个 API, 开发者需要先定义关联在串口请求中的 select-serial-port 会话事件 .

有几个额外的 API 用于与 Web Serial API 合作:

  • serial-port-addedserial-port-removed 会话事件可以被用来处理 navigator.serial.requestPort 串口进程中设备的加载和卸载事件
  • 在第一次调用 navigator.serial.requestPort 前, 可以通过 ses.setDevicePermissionHandler(handler) 给予设备默认权限, 此外,在 WebContents 的生命周期内,Electron 默认会存储授予的设备许可信息。 如果需要更长期的存储,开发人员可以存储设备许可信息(比如: 在处理 select-serial-port 事件时), 然后通过 setDevicePermissionHandler 从存储的信息中读取
  • ses.setPermissionCheckHandler(handler) 可以用于禁用特定来源的串口访问。

示例

这个示例项目既演示了一个 Electron 应用通过 ses.setDevicePermissionHandler(handler) 自动选择串口设备,又演示了当 Test Web Serial 按钮被点击后,通过 select-serial-port event on the Session 选择第一个可用的(若已连接) Arduino Uno 串口设备的流程。

  • index.html
  • main.js
  • renderer.js
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Web Serial API</title>
  <body>
    <h1>Web Serial API</h1>

    <button id="clickme">Test Web Serial API</button>

    <p>Matching Arduino Uno device: <strong id="device-name""></strong></p>

    <script src="./renderer.js"></script>
  </body>
</html>
const {app, BrowserWindow} = require('electron')
const path = require('path')

function createWindow () {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600
  })
  
  mainWindow.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
    event.preventDefault()
    if (portList && portList.length > 0) {
      callback(portList[0].portId)
    } else {
      callback('') //Could not find any matching devices
    }
  })

  mainWindow.webContents.session.on('serial-port-added', (event, port) => {
    console.log('serial-port-added FIRED WITH', port)
  })

  mainWindow.webContents.session.on('serial-port-removed', (event, port) => {
    console.log('serial-port-removed FIRED WITH', port)
  })

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

  mainWindow.webContents.session.setDevicePermissionHandler((details) => {
    if (details.deviceType === 'serial' && details.origin === 'file://') {
      return true
    }
  })
  
  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()
})

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();
    document.getElementById('device-name').innerHTML = `vendorId: ${portInfo.usbVendorId} | productId: ${portInfo.usbProductId} `
  } catch (ex) {
    if (ex.name === 'NotFoundError') {
      document.getElementById('device-name').innerHTML = 'Device NOT found'
    } else {
      document.getElementById('device-name').innerHTML = ex
    }
  }
}

document.getElementById('clickme').addEventListener('click',testIt)

Open in Fiddle