Accès aux périphériques
Comme les navigateurs basés sur Chromium, Electron fournit un accès au matériel du périphérique via des API Web. Ces API fonctionnent pour la plupart, comme dans un navigateur, mais il y a quelques différences qui doivent être prises en compte. La principale différence entre Electron et les navigateurs est ce qui se passe lorsque l'accès à l'appareil est demandé . Dans un navigateur, les utilisateurs se voient présenter une popup où ils peuvent accorder l' accès à un appareil individuel. Dans Electron des API sont fournies pouvant être utilisées par un développeur pour choisir automatiquement un périphérique ou demander aux utilisateurs de choisir un périphérique via une interface créée par un développeur.
API Web Bluetooth
L’API Web Bluetooth peut être utilisée pour communiquer avec des périphériques Bluetooth. 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.
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.
Exemple
Cet exemple montre une application Electron qui sélectionne automatiquement le premier périphérique Bluetooth disponible lorsque le bouton Test Bluetooth
est cliqué.
- main.js
- preload.js
- index.html
- renderer.js
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()
})
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)
})
<!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>
<button id="cancel">Cancel Bluetooth Request</button>
<p>Currently selected bluetooth device: <strong id="device-name"></strong></p>
<script src="./renderer.js"></script>
</body>
</html>
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(`Do you want to connect to device ${details.deviceId}?`)
break
}
case 'confirmPin': {
response.confirmed = window.confirm(`Does the pin ${details.pin} match the pin displayed on device ${details.deviceId}?`)
break
}
case 'providePin': {
const pin = window.prompt(`Please provide a pin for ${details.deviceId}.`)
if (pin) {
response.pin = pin
response.confirmed = true
} else {
response.confirmed = false
}
}
}
window.electronAPI.bluetoothPairingResponse(response)
})
API WebHID
L' API WebHID peut être utilisée pour accéder aux périphériques HID tels que les claviers et les manettes. Electron fournit plusieurs API pour travailler avec l'API WebHID:
- The
select-hid-device
event on the Session can be used to select a HID device when a call tonavigator.hid.requestDevice
is made. Additionally thehid-device-added
andhid-device-removed
events on the Session can be used to handle devices being plugged in or unplugged when handling theselect-hid-device
event. Note: Ces événements pourront se déclencher tant que la fonction de callback deselect-hid-device
n'a pas pas encore été appelée. Ils ne sont pas, d'ailleurs, destinés à être utilisés comme écouteurs génériques de port série. ses.setDevicePermissionHandler(handler)
peut être utilisé pour fournir une autorisation par défaut aux périphériques sans avoir à en faire la demande préalablement vianavigator.hid.requestDevice
. De plus, le comportement par défaut d'Electron est de stocker les autorisations accordées à l'appareil pendant la durée de vie du WebContents. Si un stockage à plus long terme est nécessaire, le développeur peut stocker les autorisations accordées à l'appareil (par exemple lors de la gestion de l'événementselect-hid-device
) puis les lire à partir de ce stockage avecsetDevicePermissionHandler
.ses.setPermissionCheckHandler(handler)
can be used to disable HID access for specific origins.
Blocklist
Par défaut, Electron utilise la même liste noire que celle utilisée par Chromium. Si vous souhaitez outrepasser ce comportement, vous pouvez le faire en définissant le drapeau disable-hid-blocklist
:
app.commandLine.appendSwitch('disable-hid-blocklist')
Exemple
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.
- main.js
- index.html
- renderer.js
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()
})
<!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>
function formatDevices (devices) {
return devices.map(device => device.productName).join('<hr>')
}
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)
API Web Serial
L' API Web Serial peut être utilisée pour accéder aux périphériques série qui sont connectés par port série, USB, ou 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.
Il existe plusieurs API supplémentaires pour travailler avec l'API Web Serial :
- The
serial-port-added
andserial-port-removed
events on the Session can be used to handle devices being plugged in or unplugged when handling theselect-serial-port
event. Note: Ces événements pourront se déclencher tant que la fonction de callback deselect-serial-port
n'a pas pas encore étét appelée. Ils ne sont pas d'ailleurs destinés à être utilisés comme écouteurs génériques de port série. ses.setDevicePermissionHandler(handler)
peut être utilisé pour fournir une autorisation par défaut aux périphériques sans avoir à en faire la demande préalablement vianavigator.serial.requestPort
. De plus, le comportement par défaut d'Electron est de stocker les autorisations accordées à l'appareil pendant la durée de vie du WebContents. Si un stockage à plus long terme est nécessaire, le développeur peut stocker les autorisations accordées à l'appareil (par exemple lors de la gestion de l'événementselect-serial-port
) puis les lire à partir de ce stockage avecsetDevicePermissionHandler
.ses.setPermissionCheckHandler(handler)
can be used to disable serial access for specific origins.
Exemple
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.
- main.js
- index.html
- renderer.js
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()
})
<!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>
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)
API WebUSB
L' API WebUSB peut être utilisée pour accéder aux périphériques USB. Electron fournit plusieurs API pour travailler avec l'API WebUSB :
- The
select-usb-device
event on the Session can be used to select a USB device when a call tonavigator.usb.requestDevice
is made. Additionally theusb-device-added
andusb-device-removed
events on the Session can be used to handle devices being plugged in or unplugged when handling theselect-usb-device
event. Note: Ces deux événements ne se déclenchent qu'après l'appel à la callback deselect-usb-device
. Ils ne sont pas destinés à être utilisés comme un écouteur de périphérique Usb générique. - 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)
peut être utilisé pour fournir une autorisation par défaut aux périphériques sans avoir à en faire la demande préalablement vianavigator.usb.requestDevice
. De plus, le comportement par défaut d'Electron est de stocker les autorisations accordées à l'appareil pendant la durée de vie du WebContents. Si un stockage à plus long terme est nécessaire, un développeur peut stocker les autorisations de périphérique accordées (par exemple lors de la gestion l’événementselect-usb-device
) afin de pouvoir ensuite relire à partir de ce stockage avecsetDevicePermissionHandler
.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.
Exemple
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.
- 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) => {
// 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()
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>WebUSB API</title>
</head>
<body>
<h1>WebUSB API</h1>
<button id="clickme">Test WebUSB</button>
<h3>USB devices automatically granted access via <i>setDevicePermissionHandler</i></h3>
<div id="granted-devices"></div>
<h3>USB devices automatically granted access via <i>select-usb-device</i></h3>
<div id="granted-devices2"></div>
<script src="./renderer.js"></script>
</body>
</html>
function getDeviceDetails (device) {
return device.productName || `Unknown device ${device.deviceId}`
}
async function testIt () {
const noDevicesFoundMsg = 'No devices found'
const grantedDevices = await navigator.usb.getDevices()
let grantedDeviceList = ''
if (grantedDevices.length > 0) {
for (const device of grantedDevices) {
grantedDeviceList += `<hr>${getDeviceDetails(device)}</hr>`
}
} else {
grantedDeviceList = noDevicesFoundMsg
}
document.getElementById('granted-devices').innerHTML = grantedDeviceList
grantedDeviceList = ''
try {
const grantedDevice = await navigator.usb.requestDevice({
filters: []
})
grantedDeviceList += `<hr>${getDeviceDetails(grantedDevice)}</hr>`
} catch (ex) {
if (ex.name === 'NotFoundError') {
grantedDeviceList = noDevicesFoundMsg
}
}
document.getElementById('granted-devices2').innerHTML = grantedDeviceList
}
document.getElementById('clickme').addEventListener('click', testIt)