Skip to content

Commit

Permalink
feat(adapter): implement bluetooth adapter, closes #34
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Dec 18, 2022
1 parent 39147bf commit f18bedf
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 155 deletions.
1 change: 1 addition & 0 deletions packages/bluetooth-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@abandonware/noble": "^1.9.2-17",
"@node-escpos/adapter": "workspace:*",
"eventemitter3": "^4.0.7",
"mutable-buffer": "^2.0.3",
Expand Down
204 changes: 116 additions & 88 deletions packages/bluetooth-adapter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,141 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import { Adapter } from "@node-escpos/adapter";
import Noble from "@abandonware/noble";
import { TextEncoder } from "util";

import { Adapter, NotImplementedException } from "@node-escpos/adapter";
// @ts-ignore
const noble = new Noble({ extended: false });

export default class Bluetooth extends Adapter<[]> {
static bluetooth;
static device;
static connection;
address: string;
channel: number;
export interface Device {
peripheral: Noble.Peripheral
characteristic: Noble.Characteristic
}

/**
* Bluetooth device
* @param {[type]} port
* @param {[type]} options
*/
export default class Bluetooth extends Adapter<[timeout?: number]> {
private devices: Device[] = [];
private address: string;

constructor(address: string, channel: number) {
constructor(address: string, options: any) {
super();

this.address = address;
this.channel = channel;
Bluetooth.loadBluetoothDependency();
if (Bluetooth.bluetooth)
Bluetooth.device = new Bluetooth.bluetooth.DeviceINQ();
}

static loadBluetoothDependency() {
if (!this.bluetooth) {
// this.bluetooth = [ bluetooth library ];
}
}
noble.on('stateChange', async (state: string) => {
if (state === 'poweredOn') {
await noble.startScanningAsync(['18f0']);
}
});

static async findPrinters() {
Bluetooth.loadBluetoothDependency();
if (Bluetooth.device === null)
Bluetooth.device = new Bluetooth.bluetooth.DeviceINQ();

const devices = await Bluetooth.device.scan();
const printers = await Promise.all(devices.map(({ address, name }) => {
return new Promise((resolve) => {
Bluetooth.device.findSerialPortChannel(address, (channel: number) => {
if (channel === -1) {
resolve(undefined);
}
else {
resolve({
address,
name,
channel,
});
}
noble.on('discover', async (peripheral: Noble.Peripheral) => {
if (this.devices.find(d => d.peripheral.id === peripheral.id) === undefined) {
if (peripheral.state !== 'connected') {
await peripheral.connectAsync();
}

const { characteristics } = await peripheral.discoverSomeServicesAndCharacteristicsAsync(['18f0'], ['2af1']);
const characteristic = characteristics[0];

const device = { peripheral, characteristic };
this.devices.push(device);

peripheral.once('disconnect', () => {
this.emit("disconnect", device);
this.devices = this.devices.filter(d => d.peripheral.id !== peripheral.id);
});
});
}));
return printers;
}
});
}

static async getDevice(address: string, channel: number) {
return new Promise((resolve, reject) => {
const device = new Bluetooth(address, channel);
device.open((err) => {
if (err) return reject(err);
resolve(device);
});
});
/**
* List Printers
* @returns {[Device]}
*/
list() {
return this.devices;
}

get device(): Device | null {
let device = this.devices.find(d => d.peripheral.address === this.address);
if (device === undefined) {
device = this.devices.find(d => !d.peripheral.address);
}
return device || null;
}

/**
* open device
* @param {Function} callback
* @return {[type]}
*/
open(callback?: (error: Error | null) => void) {
Bluetooth.bluetooth.connect(this.address, this.channel, (err: Error, conn) => {
if (err) {
callback?.(err);
}
else {
Bluetooth.connection = conn;
this.emit("connect", Bluetooth.connection);
callback?.(null);
const device = this.device;
if (device === null)
throw new Error("Bluetooth device disconnected");
else {
if (device.peripheral.state !== 'connected') {
device.peripheral.connect((error) => {
if (callback !== undefined) {
callback(error ? new Error(error) : null);
}
});
} else if (callback !== undefined) {
callback(null);
}
});
}
return this;
}

close(callback?: (error: Error | null) => void) {
if (Bluetooth.connection === null) {
callback?.(null);
}
else {
Bluetooth.connection.close((err: Error) => {
if (err) {
callback?.(err);
}
else {
this.emit("disconnect", Bluetooth.connection);
Bluetooth.connection = null;
callback?.(null);
}
});
}
/**
* write data to bluetooth device
* @param {[type]} data [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
write(data: Buffer | string, callback?: (error: Error | null) => void) {
const device = this.device;
if (device === null) throw new Error("Bluetooth device disconnected");
const message = typeof data === 'string' ? Buffer.from(new TextEncoder().encode(data).buffer) : data;
device.characteristic.write(message, false, (error) => {
if (callback) callback(error ? new Error(error) : null);
});
return this;
}

write(data: string | Buffer, callback?: (error: Error | null) => void) {
if (Bluetooth.connection === null) {
callback?.(new Error("No open bluetooth connection."));
}
else {
Bluetooth.connection.write(data, () => {
this.emit("write", data);
callback?.(null);
});
}
/**
* close device
* @param {Function} callback [description]
* @param {int} timeout [allow manual timeout for emulated COM ports (bluetooth, ...)]
* @return {[type]} [description]
*/
close(callback?: (error: Error | null, device: Device) => void, timeout = 0) {
const device = this.device;
if (device === null) return this;

device.peripheral.disconnect(() => {
if (callback) {
callback(null, device);
}
});
return this;
}

read() {
return NotImplementedException;
/**
* read buffer from the printer
* @param {Function} callback
* @return {Serial}
*/
read(callback?: (data: Buffer) => void) {
const device = this.device;
if (device === null) throw new Error("Bluetooth device disconnected");
device.characteristic.read((error, data) => {
if (callback && !error) {
callback(data);
}
})
return this;
}
}

67 changes: 0 additions & 67 deletions packages/bluetooth-adapter/src/rawbt.ts

This file was deleted.

0 comments on commit f18bedf

Please sign in to comment.