UNPKG

niimbotlib

Version:

Library for the communication with NIIMBOT printers

131 lines (130 loc) 4.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NiimbotBluetoothClient = exports.BleDefaultConfiguration = void 0; const events_1 = require("../events"); const _1 = require("."); const packets_1 = require("../packets"); const utils_1 = require("../utils"); const printer_models_1 = require("../printer_models"); const getAllModelFirstLetters = () => [...new Set(printer_models_1.modelsLibrary.map((m) => m.model[0]))]; /** * @category Client */ class BleDefaultConfiguration { } exports.BleDefaultConfiguration = BleDefaultConfiguration; BleDefaultConfiguration.SERVICES = ["e7810a71-73ae-499d-8c15-faa9aef0c3f2"]; BleDefaultConfiguration.NAME_FILTERS = [ ...getAllModelFirstLetters().map((l) => ({ namePrefix: l })), ]; /** * Uses [Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API) * * @category Client */ class NiimbotBluetoothClient extends _1.NiimbotAbstractClient { constructor() { super(...arguments); this.gattServer = undefined; this.channel = undefined; this.serviceUuidFilter = BleDefaultConfiguration.SERVICES; } getServiceUuidFilter() { return this.serviceUuidFilter; } setServiceUuidFilter(ids) { this.serviceUuidFilter = ids; } async connect() { await this.disconnect(); const options = { filters: [ ...BleDefaultConfiguration.NAME_FILTERS, { services: this.serviceUuidFilter ?? BleDefaultConfiguration.SERVICES }, ], }; const device = await navigator.bluetooth.requestDevice(options); if (device.gatt === undefined) { throw new Error("Device has no Bluetooth Generic Attribute Profile"); } const disconnectListener = () => { this.gattServer = undefined; this.channel = undefined; this.info = {}; this.emit("disconnect", new events_1.DisconnectEvent()); device.removeEventListener("gattserverdisconnected", disconnectListener); }; device.addEventListener("gattserverdisconnected", disconnectListener); const gattServer = await device.gatt.connect(); const channel = await this.findSuitableBluetoothCharacteristic(gattServer); if (channel === undefined) { gattServer.disconnect(); throw new Error("Suitable device characteristic not found"); } console.log(`Found suitable characteristic ${channel.uuid}`); channel.addEventListener("characteristicvaluechanged", (event) => { const target = event.target; this.processRawPacket(target.value); }); await channel.startNotifications(); this.gattServer = gattServer; this.channel = channel; try { await this.initialNegotiate(); await this.fetchPrinterInfo(); } catch (e) { console.error("Unable to fetch printer info."); console.error(e); } const result = { deviceName: device.name, result: this.info.connectResult ?? packets_1.ConnectResult.FirmwareErrors, }; this.emit("connect", new events_1.ConnectEvent(result)); return result; } async findSuitableBluetoothCharacteristic(gattServer) { const services = await gattServer.getPrimaryServices(); for (const service of services) { if (service.uuid.length < 5) { continue; } const characteristics = await service.getCharacteristics(); for (const c of characteristics) { if (c.properties.notify && c.properties.writeWithoutResponse) { return c; } } } return undefined; } isConnected() { return this.gattServer !== undefined && this.channel !== undefined; } // eslint-disable-next-line @typescript-eslint/require-await async disconnect() { this.stopHeartbeat(); this.gattServer?.disconnect(); this.gattServer = undefined; this.channel = undefined; this.info = {}; } async sendRaw(data, force) { const send = async () => { if (this.channel === undefined) { throw new Error("Channel is closed"); } await utils_1.Utils.sleep(this.packetIntervalMs); await this.channel.writeValueWithoutResponse(data); this.emit("rawpacketsent", new events_1.RawPacketSentEvent(data)); }; if (force) { await send(); } else { await this.mutex.runExclusive(send); } } } exports.NiimbotBluetoothClient = NiimbotBluetoothClient;