UNPKG

lavva.webbluetooth

Version:

Library implementing WebBluetooth custom functionality if underlying platform does support it

270 lines 13.7 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { NativeTypedEvent } from "./ExtendedWebBluetooth"; import { Task } from "./Task"; export class LavvaBluetooth { constructor() { this.receiveBuffer = ''; this._maxCharacteristicValueLength = 20; this.serviceUuid = 0xffe0; this.characteristicUuid = 0xffe1; this.receiveSeparator = '\n'; this.sendSeparator = '\n'; this.selectedDevice = null; this.characteristic = null; this.gattServer = null; this.gattService = null; this.OnGattServiceDisconnectedEvent = new NativeTypedEvent('OnGattServiceDisconnectedEvent'); this.OnCharacteristicValueChangedEvent = new NativeTypedEvent('OnCharacteristicValueChangedEvent'); this.OnConnectedEvent = new NativeTypedEvent('OnConnectedEvent'); this.OnDisconnectedEvent = new NativeTypedEvent('OnDisconnectedEvent'); this.filter = [ { namePrefix: 'Lavva-', services: [this.serviceUuid] }, { namePrefix: 'smartAW', services: [this.serviceUuid] }, ]; this.handleGattServiceDisconnected = (event) => { var _a; console.warn('Gatt Service disconnected'); (_a = this.OnGattServiceDisconnectedEvent) === null || _a === void 0 ? void 0 : _a.Invoke(this.gattServer); }; this.handleCharacteristicValueChanged = (event) => { var _a; try { const characteristic = event.target; const value = new TextDecoder().decode(characteristic.value); console.warn(`Received characteristic value change: ${value}`); for (const c of value) { if (c === this.receiveSeparator) { const data = this.receiveBuffer.trim(); this.receiveBuffer = ''; if (data) { console.warn(`Received data: ${data}`); (_a = this.OnCharacteristicValueChangedEvent) === null || _a === void 0 ? void 0 : _a.Invoke(data); } } else { this.receiveBuffer += c; } } } catch (error) { console.error('Error handling characteristic value changed:', error); } }; } SetFilter(filters) { this.filter = filters; } GetConnectedDevice() { return this.selectedDevice; } RequestDeviceAndConnectAsync() { return __awaiter(this, void 0, void 0, function* () { var _a; let dev; if (this.selectedDevice !== null) return DeviceConnectionError.AnotherDeviceIsAlreadyConnected; try { console.log(`Requesting Bluetooth Device, filter: ${JSON.stringify(this.filter)}`); dev = yield navigator.bluetooth.requestDevice({ filters: this.filter }); if (dev === undefined || dev === null) { console.warn('No device selected'); return DeviceConnectionError.NoDeviceHasBeenSelected; } } catch (error) { console.error('Error requesting device: ', error); return DeviceConnectionError.NoDeviceHasBeenSelected; } try { console.log(`Selected device: ${dev.name} with ID: ${dev.id}`); console.warn(`Connecting to GATT Server of device: ${dev.name}`); this.selectedDevice = dev; dev.addEventListener('gattserverdisconnected', this.handleGattServiceDisconnected.bind(this)); this.gattServer = yield ((_a = dev.gatt) === null || _a === void 0 ? void 0 : _a.connect()); if (!this.gattServer) { console.error('Failed to connect to GATT Server'); return DeviceConnectionError.FailedToConnectToGattServer; } } catch (error) { console.error('Error connecting to GATT Server: ', error); return DeviceConnectionError.FailedToConnectToGattServer; } try { console.warn(`Getting primary service: ${this.serviceUuid}`); this.gattService = yield this.gattServer.getPrimaryService(this.serviceUuid); if (!this.gattService) { console.error('Failed to get primary service'); return DeviceConnectionError.FailedToGetPrimaryService; } } catch (error) { console.error('Error getting primary service:', error); return DeviceConnectionError.FailedToGetPrimaryService; } try { console.warn(`Getting characteristic: ${this.characteristicUuid}`); this.characteristic = yield this.gattService.getCharacteristic(this.characteristicUuid); if (!this.characteristic) { console.error('Failed to get characteristic'); return DeviceConnectionError.FailedToGetCharacteristic; } } catch (error) { console.error('Error getting characteristic:', error); return DeviceConnectionError.FailedToGetCharacteristic; } try { console.warn('Starting notifications'); const notif = yield this.characteristic.startNotifications(); if (!notif) { console.error('Failed to start notifications'); return DeviceConnectionError.FailedToStartNotifications; } notif.addEventListener('characteristicvaluechanged', this.handleCharacteristicValueChanged.bind(this)); } catch (error) { console.error('Error starting notifications:', error); return DeviceConnectionError.FailedToStartNotifications; } console.warn('Connected to device successfully!'); this.OnConnectedEvent.Invoke(dev); return dev; }); } DisconnectDeviceAsync(device) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; try { if (device !== null) { console.warn(`Disconnecting device: ${device.name}`); try { (_a = this.selectedDevice) === null || _a === void 0 ? void 0 : _a.removeEventListener('gattserverdisconnected', this.handleGattServiceDisconnected); } catch (error) { console.error('Error removing event listener (gattserverdisconnected):', error); } try { (_b = this.characteristic) === null || _b === void 0 ? void 0 : _b.removeEventListener('characteristicvaluechanged', this.handleCharacteristicValueChanged); yield ((_c = this.characteristic) === null || _c === void 0 ? void 0 : _c.stopNotifications()); console.warn('Stopped notifications'); } catch (error) { //console.error('Error stopping notifications:', error); //return DeviceDisconnectionStatus.FailedToStopNotifications; } try { (_d = device.gatt) === null || _d === void 0 ? void 0 : _d.disconnect(); yield Task.Delay(1000); } catch (error) { //console.error('Error disconnecting device:', error); //return DeviceDisconnectionStatus.FailedToDisconnect; } try { yield device.forget(); } catch (error) { //console.error('Error forgetting device:', error); //return DeviceDisconnectionStatus.FailedToForgetDevice; } this.selectedDevice = null; this.characteristic = null; this.gattServer = null; this.gattService = null; this.OnDisconnectedEvent.Invoke(device); } else { return DeviceDisconnectionStatus.NoDeviceConnected; } } catch (error) { console.error('Error disconnecting device:', error); return DeviceDisconnectionStatus.FailedToDisconnect; } return DeviceDisconnectionStatus.DisconnectedSuccesfully; }); } SendDataAsync(data) { return __awaiter(this, void 0, void 0, function* () { if (!data) return WriteDataStatus.DataIsEmpty; try { data += this.sendSeparator; const chunks = this.SplitByLength(data, this._maxCharacteristicValueLength); if (!chunks) return WriteDataStatus.FailedToWriteValue; if (!this.characteristic) return WriteDataStatus.NoDeviceConnected; // Write first chunk to the characteristic immediately let res = yield this.WriteValueAsync(chunks[0]); if (!res) return WriteDataStatus.FailedToWriteValue; // Write rest of the chunks to the characteristic for (let i = 1; i < chunks.length; i++) { res = yield this.WriteValueAsync(chunks[i]); if (!res) return WriteDataStatus.FailedToWriteValue; } return WriteDataStatus.Sucess; } catch (error) { console.error('Error sending data:', error); return WriteDataStatus.FailedToWriteValue; } }); } WriteValueAsync(value) { return __awaiter(this, void 0, void 0, function* () { if (!this.characteristic) return false; const writer = new TextEncoder().encode(value); try { console.warn(`Writing characteristic [${this.characteristic.uuid}] value: ${value}`); yield this.characteristic.writeValue(writer); return true; } catch (error) { console.error(`Error writing value: ${value} error:`, error); return false; } }); } SplitByLength(text, length) { return text.match(new RegExp(`(.|[\r\n]){1,${length}}`, 'g')); } } export var DeviceConnectionError; (function (DeviceConnectionError) { DeviceConnectionError[DeviceConnectionError["NoDeviceHasBeenSelected"] = 0] = "NoDeviceHasBeenSelected"; DeviceConnectionError[DeviceConnectionError["AnotherDeviceIsAlreadyConnected"] = 1] = "AnotherDeviceIsAlreadyConnected"; DeviceConnectionError[DeviceConnectionError["FailedToConnectToGattServer"] = 2] = "FailedToConnectToGattServer"; DeviceConnectionError[DeviceConnectionError["FailedToGetPrimaryService"] = 3] = "FailedToGetPrimaryService"; DeviceConnectionError[DeviceConnectionError["FailedToDiscoverService"] = 4] = "FailedToDiscoverService"; DeviceConnectionError[DeviceConnectionError["FailedToGetCharacteristic"] = 5] = "FailedToGetCharacteristic"; DeviceConnectionError[DeviceConnectionError["FailedToStartNotifications"] = 6] = "FailedToStartNotifications"; })(DeviceConnectionError || (DeviceConnectionError = {})); export var DeviceDisconnectionStatus; (function (DeviceDisconnectionStatus) { DeviceDisconnectionStatus[DeviceDisconnectionStatus["NoDeviceConnected"] = 0] = "NoDeviceConnected"; DeviceDisconnectionStatus[DeviceDisconnectionStatus["FailedToForgetDevice"] = 1] = "FailedToForgetDevice"; DeviceDisconnectionStatus[DeviceDisconnectionStatus["FailedToDisconnect"] = 2] = "FailedToDisconnect"; DeviceDisconnectionStatus[DeviceDisconnectionStatus["FailedToStopNotifications"] = 3] = "FailedToStopNotifications"; DeviceDisconnectionStatus[DeviceDisconnectionStatus["DisconnectedSuccesfully"] = 4] = "DisconnectedSuccesfully"; })(DeviceDisconnectionStatus || (DeviceDisconnectionStatus = {})); export var WriteDataStatus; (function (WriteDataStatus) { WriteDataStatus[WriteDataStatus["Sucess"] = 0] = "Sucess"; WriteDataStatus[WriteDataStatus["DataIsEmpty"] = 1] = "DataIsEmpty"; WriteDataStatus[WriteDataStatus["NoDeviceConnected"] = 2] = "NoDeviceConnected"; WriteDataStatus[WriteDataStatus["FailedToWriteValue"] = 3] = "FailedToWriteValue"; })(WriteDataStatus || (WriteDataStatus = {})); //# sourceMappingURL=LavvaBluetooth.js.map