mirror of
https://github.com/meshcore-dev/meshcore.js.git
synced 2026-04-20 22:13:49 +00:00
WebBleConnection's constructor calls init(), which is async. Constructors can't be async, so this means that callers who instantiate WebBleConnection can end up with it in an invalid state after construction if init() hasn't completed by the time the caller tries to access class members that depend on init. I think the cleaner solution is to push the init() call to open(), which is async, so it can just await the results of init(), and the caller can trust that after calling open(), the connection is fully initialized.
108 lines
3.3 KiB
JavaScript
108 lines
3.3 KiB
JavaScript
import Constants from "../constants.js";
|
|
import Connection from "./connection.js";
|
|
|
|
class WebBleConnection extends Connection {
|
|
|
|
constructor(bleDevice) {
|
|
super();
|
|
this.bleDevice = bleDevice;
|
|
this.gattServer = null;
|
|
this.rxCharacteristic = null;
|
|
this.txCharacteristic = null;
|
|
}
|
|
|
|
static async open() {
|
|
|
|
// ensure browser supports web bluetooth
|
|
if(!navigator.bluetooth){
|
|
alert("Web Bluetooth is not supported in this browser");
|
|
return;
|
|
}
|
|
|
|
// ask user to select device
|
|
const device = await navigator.bluetooth.requestDevice({
|
|
filters: [
|
|
{
|
|
services: [
|
|
Constants.Ble.ServiceUuid.toLowerCase(),
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
// make sure user selected a device
|
|
if(!device){
|
|
return null;
|
|
}
|
|
|
|
// create connection and initialize it
|
|
const connection = new WebBleConnection(device);
|
|
await connection.init();
|
|
|
|
return connection;
|
|
}
|
|
|
|
async init() {
|
|
|
|
// listen for ble disconnect
|
|
this.bleDevice.addEventListener("gattserverdisconnected", () => {
|
|
this.onDisconnected();
|
|
});
|
|
|
|
// connect to gatt server
|
|
this.gattServer = await this.bleDevice.gatt.connect();
|
|
|
|
// find service
|
|
const service = await this.gattServer.getPrimaryService(Constants.Ble.ServiceUuid.toLowerCase());
|
|
const characteristics = await service.getCharacteristics();
|
|
|
|
// find rx characteristic (we write to this one, it's where the radio reads from)
|
|
this.rxCharacteristic = characteristics.find((characteristic) => {
|
|
return characteristic.uuid.toLowerCase() === Constants.Ble.CharacteristicUuidRx.toLowerCase();
|
|
});
|
|
|
|
// find tx characteristic (we read this one, it's where the radio writes to)
|
|
this.txCharacteristic = characteristics.find((characteristic) => {
|
|
return characteristic.uuid.toLowerCase() === Constants.Ble.CharacteristicUuidTx.toLowerCase();
|
|
});
|
|
|
|
// listen for frames from transmitted to us from the ble device
|
|
await this.txCharacteristic.startNotifications();
|
|
this.txCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
|
|
const frame = new Uint8Array(event.target.value.buffer);
|
|
this.onFrameReceived(frame);
|
|
});
|
|
|
|
// fire connected event
|
|
await this.onConnected();
|
|
|
|
}
|
|
|
|
async close() {
|
|
try {
|
|
this.gattServer?.disconnect();
|
|
this.gattServer = null;
|
|
} catch(e) {
|
|
// ignore error when disconnecting
|
|
}
|
|
}
|
|
|
|
async write(bytes) {
|
|
try {
|
|
// fixme: NetworkError: GATT operation already in progress.
|
|
// todo: implement mutex to prevent multiple writes when another write is in progress
|
|
// we write to the rx characteristic, as that's where the radio reads from
|
|
await this.rxCharacteristic.writeValue(bytes);
|
|
} catch(e) {
|
|
console.log("failed to write to ble device", e);
|
|
}
|
|
}
|
|
|
|
async sendToRadioFrame(frame) {
|
|
this.emit("tx", frame);
|
|
await this.write(frame);
|
|
}
|
|
|
|
}
|
|
|
|
export default WebBleConnection;
|