Last active
February 15, 2024 18:43
-
-
Save Scavanger/40be235292e44377b4eaec8218c33d83 to your computer and use it in GitHub Desktop.
Bluetooth Device Chooser for Electron
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
background-color: black; | |
margin: 10px; | |
} | |
h1 { | |
font-size: 20px; | |
font-weight: bold; | |
color: white; | |
} | |
#id { | |
margin: 5px; | |
} | |
#list { | |
color: white; | |
} | |
.item { | |
margin: 5px; | |
padding: 5px; | |
height: 40px; | |
line-height: 40px; | |
width: 345px; | |
display: inline-block; | |
vertical-align: middle; | |
border: 1px solid whitesmoke; | |
} | |
.item:hover { | |
background-color: rgb(88, 88, 192); | |
} | |
#cancel { | |
text-align: center; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> | |
<link type="text/css" rel="stylesheet" href="./bluetoothDeviceChooser.css" media="all" /> | |
<script src="./bluetoothDeviceChooserRenderer.js"></script> | |
<title>Bluetooth Device Chooser</title> | |
</head> | |
<body> | |
<h1>Select Bluetooth device:</h1> | |
<div id="list"> | |
<div id="cancel" class="item"> | |
Cancel | |
</div> | |
</div | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
document.addEventListener("DOMContentLoaded", () => { | |
window.electronAPI.bleScan(data => { | |
console.log(data); | |
data.forEach(device => { | |
var dev = document.getElementById(device.deviceId) | |
if (dev) { | |
dev.parentElement.removeChild(dev); | |
} | |
var item = document.createElement('div'); | |
item.className = 'item' | |
item.id = device.deviceId; | |
item.addEventListener('click', () => { | |
window.electronAPI.deviceSelected(item.id); | |
window.close(); | |
}); | |
item.appendChild(document.createTextNode(device.deviceName + ' (' + device.deviceId + ')')); | |
document.getElementById('list').prepend(item); | |
}); | |
}); | |
document.getElementById('cancel').addEventListener('click', () => { | |
window.close(); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { contextBridge, ipcRenderer } = require('electron/renderer'); | |
contextBridge.exposeInMainWorld('electronAPI', { | |
bleScan: (callback) => ipcRenderer.on('ble-scan', (_event, data) => callback(data)), | |
deviceSelected: (deviceId) => ipcRenderer.send('deviceSelected', deviceId) | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> | |
<title>Bluetooth Device Chooser</title> | |
</head> | |
<body> | |
<h1>Bluetooth Device Chooser</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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { app, BrowserWindow, ipcMain } = require('electron/main') | |
const path = require('node:path') | |
let bluetoothPinCallback; | |
let selectBluetoothCallback; | |
let mainWindow; | |
let bluetoothDeviceChooser = null; | |
let btDeviceList = null; | |
function createDeviceChooser() { | |
bluetoothDeviceChooser = new BrowserWindow({ | |
parent: mainWindow, | |
width: 400, | |
height: 400, | |
webPreferences: { | |
preload: path.join(__dirname, 'bluetoothDeviceChoserPreload.js') | |
} | |
}); | |
bluetoothDeviceChooser.removeMenu(); | |
bluetoothDeviceChooser.loadFile('bluetoothDeviceChooser.html'); | |
bluetoothDeviceChooser.webContents.openDevTools(); | |
bluetoothDeviceChooser.on('closed', () => { | |
btDeviceList = null; | |
if (selectBluetoothCallback) { | |
selectBluetoothCallback(''); | |
selectBluetoothCallback = null; | |
} | |
bluetoothDeviceChooser = null; | |
}); | |
ipcMain.on('deviceSelected', (_event, deviceID) => { | |
if (selectBluetoothCallback) { | |
selectBluetoothCallback(deviceID); | |
selectBluetoothCallback = null; | |
} | |
}); | |
} | |
function createWindow () { | |
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; | |
console.log(deviceList); | |
const compare= (a, b) => { | |
if (a.length !== b.length) { | |
return false; | |
} | |
a.every((element, index) => { | |
if (element.deviceId !== b[index].deviceId) { | |
return false; | |
} | |
}) | |
return true; | |
} | |
if (!btDeviceList || !compare(btDeviceList, deviceList)) { | |
btDeviceList = [...deviceList]; | |
//console.log(btDeviceList); | |
if (!bluetoothDeviceChooser) { | |
createDeviceChooser(); | |
} | |
bluetoothDeviceChooser.webContents.send('ble-scan', btDeviceList); | |
} | |
}); | |
ipcMain.on('cancel-bluetooth-request', (event) => { | |
if (selectBluetoothCallback) { | |
selectBluetoothCallback('') | |
selectBluetoothCallback = null; | |
} | |
}) | |
// 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() | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "Bluetooth Demo", | |
"productName": "Bluetooth Device Chooser Demo", | |
"description": "Bluetooth Device Chooser for Electron", | |
"keywords": [], | |
"main": "./main.js", | |
"version": "1.0.0", | |
"author": "Scavanger", | |
"scripts": { | |
"start": "electron ." | |
}, | |
"dependencies": {}, | |
"devDependencies": { | |
"electron": "28.2.2" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function testIt () { | |
try { | |
const device = await navigator.bluetooth.requestDevice({ | |
acceptAllDevices: true | |
}) | |
document.getElementById('device-name').innerHTML = device.name || `ID: ${device.id}` | |
} catch { | |
document.getElementById('device-name').innerHTML = ''; | |
} | |
} | |
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) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* styles.css */ | |
/* Add styles here to customize the appearance of your app */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment