This repository has been archived on 2024-09-16. You can view files and clone it, but cannot push or open issues or pull requests.

305 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
AutoShutdownTime,
BatteryChargeLevel,
ConnectResult,
HeartbeatType,
LabelType,
NiimbotPacket,
PacketGenerator,
PrinterInfoType,
PrintOptions,
ResponseCommandId,
SoundSettingsItemType,
SoundSettingsType,
EncodedImage,
NiimbotAbstractClient,
Utils,
Validators,
ProtocolVersion,
} from "..";
import { SequentialDataReader } from "./data_reader";
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;
lidClosed: boolean;
powerLevel: BatteryChargeLevel;
}
export interface SoundSettings {
category: SoundSettingsType;
item: SoundSettingsItemType;
value: boolean;
}
export interface PrinterStatusData {
supportColor: number;
protocolVersion: number;
}
/** Not sure for name. */
export class Abstraction {
private readonly DEFAULT_TIMEOUT: number = 1_000;
private client: NiimbotAbstractClient;
private timeout: number = this.DEFAULT_TIMEOUT;
constructor(client: NiimbotAbstractClient) {
this.client = client;
}
public getTimeout(): number {
return this.timeout;
}
public setTimeout(value: number) {
this.timeout = value;
}
public setDefaultTimeout() {
this.timeout = this.DEFAULT_TIMEOUT;
}
/** Send packet and wait for response */
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());
if (packet.command === ResponseCommandId.In_PrintError) {
Validators.u8ArrayLengthEquals(packet.data, 1);
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet received)`, packet.data[0]);
}
Validators.u8ArrayLengthAtLeast(packet.data, 4); // can be 8, 10, but ignore it for now
const r = new SequentialDataReader(packet.data);
const page = r.readI16();
const pagePrintProgress = r.readI8();
const pageFeedProgress = r.readI8();
if (packet.dataLength === 10) {
r.skip(2);
const error = r.readI8();
if (error !== 0) {
throw new PrintError(`Print error (${ResponseCommandId[packet.command]} packet flag)`, error);
}
}
return { page, pagePrintProgress, pageFeedProgress };
}
public async connectResult(): Promise<ConnectResult> {
const packet = await this.send(PacketGenerator.connect());
Validators.u8ArrayLengthAtLeast(packet.data, 1);
return packet.data[0] as ConnectResult;
}
public async getPrinterStatusData(): Promise<PrinterStatusData> {
let protocolVersion = 0;
const packet = await this.send(PacketGenerator.getPrinterStatusData());
let supportColor = 0;
if (packet.dataLength > 12) {
supportColor = packet.data[10];
const n = packet.data[11] * 100 + packet.data[12];
if (n >= 204 && n < 300) {
protocolVersion = 3;
}
if (n >= 301) {
protocolVersion = 4;
}
}
return {
supportColor,
protocolVersion,
};
}
public async getPrinterModel(): Promise<number> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.PrinterModelId));
Validators.u8ArrayLengthEquals(packet.data, 2);
return Utils.bytesToI16(packet.data);
}
/** 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,
};
if (packet.dataLength === 1) {
return info;
}
const r = new SequentialDataReader(packet.data);
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> {
const packet = await this.send(PacketGenerator.heartbeat(HeartbeatType.Advanced1));
const info: HeartbeatData = {
paperState: -1,
rfidReadState: -1,
lidClosed: false,
powerLevel: BatteryChargeLevel.Charge0,
};
// originally expected packet length is bound to model id, but we make it more robust and simple
const len = packet.dataLength;
const r = new SequentialDataReader(packet.data);
if (len === 10) {
// d110
r.skip(8);
info.lidClosed = r.readBool();
info.powerLevel = r.readI8();
} else if (len === 20) {
r.skip(18);
info.paperState = r.readI8();
info.rfidReadState = r.readI8();
} else if (len === 19) {
r.skip(15);
info.lidClosed = r.readBool();
info.powerLevel = r.readI8();
info.paperState = r.readI8();
info.rfidReadState = r.readI8();
} else if (len === 13) {
// b1
r.skip(9);
info.lidClosed = r.readBool();
info.powerLevel = r.readI8();
info.paperState = r.readI8();
info.rfidReadState = r.readI8();
} else {
throw new Error("Invalid heartbeat length");
}
r.end();
const model: number | undefined = this.client.getPrinterInfo().model_id;
if (model !== undefined && ![512, 514, 513, 2304, 1792, 3584, 5120, 2560, 3840, 4352, 272].includes(model)) {
info.lidClosed = !info.lidClosed;
}
return info;
}
public async getBatteryChargeLevel(): Promise<BatteryChargeLevel> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.BatteryChargeLevel));
Validators.u8ArrayLengthEquals(packet.data, 1);
return packet.data[0] as BatteryChargeLevel;
}
public async getAutoShutDownTime(): Promise<AutoShutdownTime> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.AutoShutdownTime));
Validators.u8ArrayLengthEquals(packet.data, 1);
return packet.data[0] as AutoShutdownTime;
}
public async setAutoShutDownTime(time: AutoShutdownTime): Promise<void> {
await this.send(PacketGenerator.setAutoShutDownTime(time));
}
public async getLabelType(): Promise<LabelType> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.LabelType));
Validators.u8ArrayLengthEquals(packet.data, 1);
return packet.data[0] as LabelType;
}
public async getPrinterSerialNumber(): Promise<string> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.SerialNumber));
Validators.u8ArrayLengthAtLeast(packet.data, 1);
return Utils.u8ArrayToString(packet.data);
}
public async getPrinterBluetoothMacAddress(): Promise<string> {
const packet = await this.send(PacketGenerator.getPrinterInfo(PrinterInfoType.BluetoothAddress));
Validators.u8ArrayLengthAtLeast(packet.data, 1);
return Utils.bufToHex(packet.data.reverse(), ":");
}
public async isSoundEnabled(soundType: SoundSettingsItemType): Promise<boolean> {
const packet = await this.send(PacketGenerator.getSoundSettings(soundType));
Validators.u8ArrayLengthEquals(packet.data, 3);
const value = !!packet.data[2];
return value;
}
public async setSoundEnabled(soundType: SoundSettingsItemType, value: boolean): Promise<void> {
await this.send(PacketGenerator.setSoundSettings(soundType, value));
}
public async print(
protoVersion: ProtocolVersion,
image: EncodedImage,
options?: PrintOptions,
timeout?: number
): Promise<void> {
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());
}
}