-
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(adapter): implement bluetooth adapter, closes #34
- Loading branch information
1 parent
39147bf
commit f18bedf
Showing
3 changed files
with
117 additions
and
155 deletions.
There are no files selected for viewing
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
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
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; | ||
} | ||
} | ||
|
This file was deleted.
Oops, something went wrong.