2024-07-22 00:00:01 +03:00
|
|
|
|
import {
|
2024-07-23 00:00:01 +03:00
|
|
|
|
AutoShutdownTime,
|
|
|
|
|
BatteryChargeLevel,
|
2024-07-25 00:00:01 +03:00
|
|
|
|
ConnectResult,
|
2024-07-23 00:00:01 +03:00
|
|
|
|
HeartbeatType,
|
2024-07-22 00:00:01 +03:00
|
|
|
|
LabelType,
|
|
|
|
|
NiimbotPacket,
|
|
|
|
|
PacketGenerator,
|
|
|
|
|
PrinterInfoType,
|
2024-07-31 01:00:02 +03:00
|
|
|
|
PrintOptions,
|
2024-07-22 00:00:01 +03:00
|
|
|
|
ResponseCommandId,
|
|
|
|
|
SoundSettingsItemType,
|
|
|
|
|
SoundSettingsType,
|
2024-07-31 01:00:02 +03:00
|
|
|
|
EncodedImage,
|
|
|
|
|
NiimbotAbstractClient,
|
|
|
|
|
Utils,
|
|
|
|
|
Validators,
|
|
|
|
|
ProtocolVersion,
|
|
|
|
|
} from "..";
|
2024-07-23 00:00:01 +03:00
|
|
|
|
import { SequentialDataReader } from "./data_reader";
|
2024-07-22 00:00:01 +03:00
|
|
|
|
|
|
|
|
|
export class PrintError extends Error {
|
|
|
|
|
public readonly reasonId: number;
|
|
|
|
|
|
|
|
|
|
constructor(message: string, reasonId: number) {
|
|
|
|
|
super(message);
|
|
|
|
|
this.reasonId = reasonId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface PrintStatus {
|
|
|
|
|
/** 0 – n */
|
|
|
|
|
page: number;
|
|
|
|
|
/** 0 – 100 */
|
|
|
|
|
pagePrintProgress: number;
|
|
|
|
|
/** 0 – 100 */
|
|
|
|
|
pageFeedProgress: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface RfidInfo {
|
|
|
|
|
tagPresent: boolean;
|
|
|
|
|
uuid: string;
|
|
|
|
|
barCode: string;
|
|
|
|
|
serialNumber: string;
|
|
|
|
|
allPaper: number;
|
|
|
|
|
usedPaper: number;
|
|
|
|
|
consumablesType: LabelType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** closingState inverted on some printers */
|
|
|
|
|
export interface HeartbeatData {
|
|
|
|
|
paperState: number;
|
|
|
|
|
rfidReadState: number;
|
2024-07-25 00:00:01 +03:00
|
|
|
|
lidClosed: boolean;
|
|
|
|
|
powerLevel: BatteryChargeLevel;
|
2024-07-22 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 00:00:01 +03:00
|
|
|
|
export interface SoundSettings {
|
|
|
|
|
category: SoundSettingsType;
|
|
|
|
|
item: SoundSettingsItemType;
|
|
|
|
|
value: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 00:00:01 +03:00
|
|
|
|
export interface PrinterStatusData {
|
|
|
|
|
supportColor: number;
|
|
|
|
|
protocolVersion: number;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 00:00:01 +03:00
|
|
|
|
/** Not sure for name. */
|
2024-07-22 00:00:01 +03:00
|
|
|
|
export class Abstraction {
|
2024-07-31 01:00:02 +03:00
|
|
|
|
private readonly DEFAULT_TIMEOUT: number = 1_000;
|
2024-07-22 00:00:01 +03:00
|
|
|
|
private client: NiimbotAbstractClient;
|
2024-07-31 01:00:02 +03:00
|
|
|
|
private timeout: number = this.DEFAULT_TIMEOUT;
|
2024-07-22 00:00:01 +03:00
|
|
|
|
|
|
|
|
|
constructor(client: NiimbotAbstractClient) {
|
|
|
|
|
this.client = client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getTimeout(): number {
|
|
|
|
|
return this.timeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public setTimeout(value: number) {
|
|
|
|
|
this.timeout = value;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-31 01:00:02 +03:00
|
|
|
|
public setDefaultTimeout() {
|
|
|
|
|
this.timeout = this.DEFAULT_TIMEOUT;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 00:00:01 +03:00
|
|
|
|
/** Send packet and wait for response */
|
2024-07-22 00:00:01 +03:00
|
|
|
|
private async send(packet: NiimbotPacket): Promise<NiimbotPacket> {
|
|
|
|
|
return this.client.sendPacketWaitResponse(packet, this.timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getPrintStatus(): Promise<PrintStatus> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.printStatus());
|
|
|
|
|
|
2024-07-26 00:00:02 +03:00
|
|
|
|
if (packet.command === ResponseCommandId.In_PrintError) {
|
|
|
|
|
Validators.u8ArrayLengthEquals(packet.data, 1);
|
2024-07-28 00:00:01 +03:00
|
|
|
|
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet received)`, packet.data[0]);
|
2024-07-22 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthAtLeast(packet.data, 4); // can be 8, 10, but ignore it for now
|
2024-07-22 00:00:01 +03:00
|
|
|
|
|
2024-07-26 00:00:02 +03:00
|
|
|
|
const r = new SequentialDataReader(packet.data);
|
2024-07-22 00:00:01 +03:00
|
|
|
|
const page = r.readI16();
|
|
|
|
|
const pagePrintProgress = r.readI8();
|
|
|
|
|
const pageFeedProgress = r.readI8();
|
|
|
|
|
|
2024-07-26 00:00:02 +03:00
|
|
|
|
if (packet.dataLength === 10) {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
r.skip(2);
|
|
|
|
|
const error = r.readI8();
|
2024-07-28 00:00:01 +03:00
|
|
|
|
|
|
|
|
|
if (error !== 0) {
|
|
|
|
|
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet flag)`, error);
|
|
|
|
|
}
|
2024-07-22 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { page, pagePrintProgress, pageFeedProgress };
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 00:00:01 +03:00
|
|
|
|
public async connectResult(): Promise<ConnectResult> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.connect());
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthAtLeast(packet.data, 1);
|
|
|
|
|
return packet.data[0] as ConnectResult;
|
2024-07-25 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getPrinterStatusData(): Promise<PrinterStatusData> {
|
|
|
|
|
let protocolVersion = 0;
|
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterStatusData());
|
|
|
|
|
let supportColor = 0;
|
|
|
|
|
|
2024-07-28 00:00:01 +03:00
|
|
|
|
if (packet.dataLength > 12) {
|
|
|
|
|
supportColor = packet.data[10];
|
2024-07-25 00:00:01 +03:00
|
|
|
|
|
2024-08-01 01:00:02 +03:00
|
|
|
|
const n = packet.data[11] * 100 + packet.data[12];
|
2024-07-28 00:00:01 +03:00
|
|
|
|
if (n >= 204 && n < 300) {
|
|
|
|
|
protocolVersion = 3;
|
2024-07-25 00:00:01 +03:00
|
|
|
|
}
|
2024-07-28 00:00:01 +03:00
|
|
|
|
if (n >= 301) {
|
|
|
|
|
protocolVersion = 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-25 00:00:01 +03:00
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
supportColor,
|
2024-07-28 00:00:01 +03:00
|
|
|
|
protocolVersion,
|
|
|
|
|
};
|
2024-07-25 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 01:00:02 +03:00
|
|
|
|
public async getPrinterModel(): Promise<number> {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.PrinterModelId));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthEquals(packet.data, 2);
|
2024-08-01 01:00:02 +03:00
|
|
|
|
return Utils.bytesToI16(packet.data);
|
2024-07-22 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Read paper nfc tag info */
|
|
|
|
|
public async rfidInfo(): Promise<RfidInfo> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.rfidInfo());
|
|
|
|
|
|
|
|
|
|
const info: RfidInfo = {
|
|
|
|
|
tagPresent: false,
|
|
|
|
|
uuid: "",
|
|
|
|
|
barCode: "",
|
|
|
|
|
serialNumber: "",
|
|
|
|
|
allPaper: -1,
|
|
|
|
|
usedPaper: -1,
|
|
|
|
|
consumablesType: LabelType.Invalid,
|
|
|
|
|
};
|
|
|
|
|
|
2024-07-26 00:00:02 +03:00
|
|
|
|
if (packet.dataLength === 1) {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 00:00:02 +03:00
|
|
|
|
const r = new SequentialDataReader(packet.data);
|
2024-07-22 00:00:01 +03:00
|
|
|
|
info.tagPresent = true;
|
|
|
|
|
info.uuid = Utils.bufToHex(r.readBytes(8), "");
|
|
|
|
|
info.barCode = r.readVString();
|
|
|
|
|
info.serialNumber = r.readVString();
|
|
|
|
|
info.allPaper = r.readI16();
|
|
|
|
|
info.usedPaper = r.readI16();
|
|
|
|
|
info.consumablesType = r.readI8() as LabelType;
|
|
|
|
|
r.end();
|
|
|
|
|
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async heartbeat(): Promise<HeartbeatData> {
|
2024-07-29 00:00:01 +03:00
|
|
|
|
const packet = await this.send(PacketGenerator.heartbeat(HeartbeatType.Advanced1));
|
2024-07-22 00:00:01 +03:00
|
|
|
|
|
|
|
|
|
const info: HeartbeatData = {
|
|
|
|
|
paperState: -1,
|
|
|
|
|
rfidReadState: -1,
|
2024-07-25 00:00:01 +03:00
|
|
|
|
lidClosed: false,
|
|
|
|
|
powerLevel: BatteryChargeLevel.Charge0,
|
2024-07-22 00:00:01 +03:00
|
|
|
|
};
|
|
|
|
|
|
2024-07-23 00:00:01 +03:00
|
|
|
|
// originally expected packet length is bound to model id, but we make it more robust and simple
|
2024-07-26 00:00:02 +03:00
|
|
|
|
const len = packet.dataLength;
|
|
|
|
|
const r = new SequentialDataReader(packet.data);
|
2024-07-22 00:00:01 +03:00
|
|
|
|
|
2024-07-23 00:00:01 +03:00
|
|
|
|
if (len === 10) {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
// d110
|
|
|
|
|
r.skip(8);
|
2024-07-25 00:00:01 +03:00
|
|
|
|
info.lidClosed = r.readBool();
|
2024-07-22 00:00:01 +03:00
|
|
|
|
info.powerLevel = r.readI8();
|
2024-07-23 00:00:01 +03:00
|
|
|
|
} else if (len === 20) {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
r.skip(18);
|
|
|
|
|
info.paperState = r.readI8();
|
|
|
|
|
info.rfidReadState = r.readI8();
|
2024-07-23 00:00:01 +03:00
|
|
|
|
} else if (len === 19) {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
r.skip(15);
|
2024-07-25 00:00:01 +03:00
|
|
|
|
info.lidClosed = r.readBool();
|
2024-07-22 00:00:01 +03:00
|
|
|
|
info.powerLevel = r.readI8();
|
|
|
|
|
info.paperState = r.readI8();
|
|
|
|
|
info.rfidReadState = r.readI8();
|
2024-07-23 00:00:01 +03:00
|
|
|
|
} else if (len === 13) {
|
2024-07-22 00:00:01 +03:00
|
|
|
|
// b1
|
|
|
|
|
r.skip(9);
|
2024-07-25 00:00:01 +03:00
|
|
|
|
info.lidClosed = r.readBool();
|
2024-07-22 00:00:01 +03:00
|
|
|
|
info.powerLevel = r.readI8();
|
|
|
|
|
info.paperState = r.readI8();
|
|
|
|
|
info.rfidReadState = r.readI8();
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error("Invalid heartbeat length");
|
|
|
|
|
}
|
|
|
|
|
r.end();
|
|
|
|
|
|
2024-08-01 01:00:02 +03:00
|
|
|
|
const model: number | undefined = this.client.getPrinterInfo().model_id;
|
2024-07-25 00:00:01 +03:00
|
|
|
|
|
2024-08-01 01:00:02 +03:00
|
|
|
|
if (model !== undefined && ![512, 514, 513, 2304, 1792, 3584, 5120, 2560, 3840, 4352, 272].includes(model)) {
|
2024-07-25 00:00:01 +03:00
|
|
|
|
info.lidClosed = !info.lidClosed;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 00:00:01 +03:00
|
|
|
|
return info;
|
|
|
|
|
}
|
2024-07-23 00:00:01 +03:00
|
|
|
|
|
|
|
|
|
public async getBatteryChargeLevel(): Promise<BatteryChargeLevel> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.BatteryChargeLevel));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthEquals(packet.data, 1);
|
|
|
|
|
return packet.data[0] as BatteryChargeLevel;
|
2024-07-23 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getAutoShutDownTime(): Promise<AutoShutdownTime> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.AutoShutdownTime));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthEquals(packet.data, 1);
|
|
|
|
|
return packet.data[0] as AutoShutdownTime;
|
2024-07-23 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-28 00:00:01 +03:00
|
|
|
|
public async setAutoShutDownTime(time: AutoShutdownTime): Promise<void> {
|
|
|
|
|
await this.send(PacketGenerator.setAutoShutDownTime(time));
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 00:00:01 +03:00
|
|
|
|
public async getLabelType(): Promise<LabelType> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.LabelType));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthEquals(packet.data, 1);
|
|
|
|
|
return packet.data[0] as LabelType;
|
2024-07-23 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getPrinterSerialNumber(): Promise<string> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.SerialNumber));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthAtLeast(packet.data, 1);
|
|
|
|
|
return Utils.u8ArrayToString(packet.data);
|
2024-07-23 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getPrinterBluetoothMacAddress(): Promise<string> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.BluetoothAddress));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthAtLeast(packet.data, 1);
|
|
|
|
|
return Utils.bufToHex(packet.data.reverse(), ":");
|
2024-07-23 00:00:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async isSoundEnabled(soundType: SoundSettingsItemType): Promise<boolean> {
|
|
|
|
|
const packet = await this.send(PacketGenerator.getSoundSettings(soundType));
|
2024-07-26 00:00:02 +03:00
|
|
|
|
Validators.u8ArrayLengthEquals(packet.data, 3);
|
|
|
|
|
const value = !!packet.data[2];
|
2024-07-23 00:00:01 +03:00
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async setSoundEnabled(soundType: SoundSettingsItemType, value: boolean): Promise<void> {
|
|
|
|
|
await this.send(PacketGenerator.setSoundSettings(soundType, value));
|
|
|
|
|
}
|
2024-07-31 01:00:02 +03:00
|
|
|
|
|
2024-08-01 01:00:02 +03:00
|
|
|
|
public async print(
|
|
|
|
|
protoVersion: ProtocolVersion,
|
|
|
|
|
image: EncodedImage,
|
|
|
|
|
options?: PrintOptions,
|
|
|
|
|
timeout?: number
|
|
|
|
|
): Promise<void> {
|
2024-07-31 01:00:02 +03:00
|
|
|
|
this.setTimeout(timeout ?? 10_000);
|
|
|
|
|
const packets: NiimbotPacket[] = PacketGenerator.generatePrintSequence(protoVersion, image, options);
|
|
|
|
|
try {
|
|
|
|
|
for (const element of packets) {
|
|
|
|
|
await this.send(element);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
this.setDefaultTimeout();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async printEnd(): Promise<void> {
|
|
|
|
|
await this.send(PacketGenerator.printEnd());
|
|
|
|
|
}
|
2024-07-22 00:00:01 +03:00
|
|
|
|
}
|