UNPKG

@fdm-monster/server

Version:

FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.

233 lines (232 loc) 7.57 kB
import { uploadFileInputSchema } from "./printer-api.interface.js"; import { BambuMqttAdapter } from "./bambu/bambu-mqtt.adapter.js"; import { statSync } from "node:fs"; //#region src/services/bambu.api.ts const defaultLog = { adapter: "bambu-lab" }; /** * Bambu Lab printer API implementation * Implements the IPrinterApi interface for fdm-monster integration * * Note: MQTT adapter is managed by PrinterSocketStore * * Credentials mapping: * - printerURL: Host IP address * - password: Access code (8-character authentication code) * - username: Serial number */ var BambuApi = class BambuApi { logger; client; printerLogin; printerSocketStore; printerId; constructor(bambuClient, printerLogin, printerSocketStore, loggerFactory) { this.logger = loggerFactory(BambuApi.name); this.client = bambuClient; this.printerLogin = printerLogin; this.printerSocketStore = printerSocketStore; this.logger.debug("Constructed Bambu API client", this.logMeta()); } /** * Set the printer ID for accessing the MQTT adapter */ setPrinterId(printerId) { this.printerId = printerId; } /** * Get the MQTT adapter from the printer socket store */ getMqttAdapter() { if (!this.printerId) throw new Error("Printer ID not set. Cannot access MQTT adapter."); const adapter = this.printerSocketStore.getPrinterSocket(this.printerId); if (!adapter) throw new Error(`MQTT adapter not found for printer ${this.printerId}`); if (!(adapter instanceof BambuMqttAdapter)) throw new Error(`Adapter for printer ${this.printerId} is not a BambuMqttAdapter`); return adapter; } /** * Ensure FTP is connected, auto-connect if needed */ async ensureFtpConnected() { if (!this.client.isConnected) { this.logger.debug("FTP not connected, connecting automatically"); await this.client.connect(this.printerLogin); } } get type() { return 3; } set login(login) { this.printerLogin = login; } async getVersion() { return (await this.client.getApiVersion(this.printerLogin)).version; } async validateConnection() { this.logger.debug("Validating Bambu connection", this.logMeta()); try { await this.ensureFtpConnected(); const files = await this.client.ftp.listFiles("/"); this.logger.debug(`FTP connection successful - found ${files.length} directories`, this.logMeta()); } catch (ftpError) { this.logger.debug(`FTP validation failed: ${ftpError}`, this.logMeta()); throw new Error(`Bambu FTP connection failed: ${ftpError}`); } } async connect() { await this.client.connect(this.printerLogin); } async disconnect() { await this.client.disconnect(); } restartServer() { this.logger.warn("restartServer not supported by Bambu Lab printers"); throw new Error("Method not supported"); } restartHost() { this.logger.warn("restartHost not supported by Bambu Lab printers"); throw new Error("Method not supported"); } restartPrinterFirmware() { this.logger.warn("restartPrinterFirmware not supported by Bambu Lab printers"); throw new Error("Method not supported"); } async startPrint(path) { this.logger.log(`Starting print: ${path}`, this.logMeta()); await this.getMqttAdapter().startPrint(path); } async pausePrint() { this.logger.log("Pausing print", this.logMeta()); await this.getMqttAdapter().pausePrint(); } async resumePrint() { this.logger.log("Resuming print", this.logMeta()); await this.getMqttAdapter().resumePrint(); } async cancelPrint() { this.logger.log("Canceling print", this.logMeta()); await this.getMqttAdapter().stopPrint(); } async quickStop() { this.logger.log("Quick stop (same as cancel for Bambu)", this.logMeta()); await this.getMqttAdapter().stopPrint(); } async sendGcode(script) { this.logger.log(`Sending GCode: ${script}`, this.logMeta()); await this.getMqttAdapter().sendGcode(script); } movePrintHead(amounts) { this.logger.warn("movePrintHead not implemented for Bambu Lab printers"); throw new Error("Method not implemented"); } homeAxes(axes) { this.logger.warn("homeAxes not implemented for Bambu Lab printers"); throw new Error("Method not implemented"); } async getFile(path) { this.logger.debug(`Getting file info: ${path}`, this.logMeta()); await this.ensureFtpConnected(); const file = (await this.client.ftp.listFiles("/")).find((f) => f.name === path || f.name.endsWith(path)); if (!file) throw new Error(`File not found: ${path}`); return { path: file.name, size: file.size, date: file.modifiedAt ? new Date(file.modifiedAt).getTime() : null, dir: file.isDirectory }; } async getFiles(recursive = false, startDir = "/") { if (recursive) throw new Error("Recursive listing not supported for Bambu Lab printers"); this.logger.debug(`Listing files from ${startDir}`, this.logMeta()); await this.ensureFtpConnected(); const mapped = (await this.client.ftp.listFiles(startDir)).map((item) => { return { path: startDir === "/" ? `/${item.name}` : `${startDir}/${item.name}`, size: item.size, date: item.modifiedAt ? new Date(item.modifiedAt).getTime() : null, dir: item.isDirectory }; }); return { dirs: mapped.filter((i) => i.dir), files: mapped.filter((i) => !i.dir) }; } async downloadFile(path) { this.logger.log(`Downloading file via FTP: ${path}`, this.logMeta()); await this.ensureFtpConnected(); const { stream, tempPath } = await this.client.ftp.downloadFileAsStream(path); const stats = statSync(tempPath); const filename = path.split("/").pop() || "download"; return { data: stream, status: 200, statusText: "OK", headers: { "content-type": "application/octet-stream", "content-length": String(stats.size), "content-disposition": `attachment; filename="${filename}"` }, config: { headers: {} } }; } getFileChunk(path, startBytes, endBytes) { this.logger.warn("getFileChunk not implemented for Bambu Lab printers"); throw new Error("Method not implemented"); } async uploadFile(input) { const validated = uploadFileInputSchema.parse(input); this.logger.log(`Uploading file: ${validated.fileName} (${validated.contentLength} bytes)`, this.logMeta()); try { await this.ensureFtpConnected(); await this.client.ftp.uploadFile(validated.stream, validated.fileName, validated.uploadToken); if (validated.startPrint) { this.logger.log(`Starting print after upload: ${validated.fileName}`, this.logMeta()); await this.getMqttAdapter().startPrint(validated.fileName); } } catch (error) { this.logger.error(`Upload failed: ${error.message}`, this.logMeta()); throw error; } } async deleteFile(path) { this.logger.log(`Deleting file: ${path}`, this.logMeta()); await this.ensureFtpConnected(); await this.client.ftp.deleteFile(path); } deleteFolder(path) { this.logger.warn("deleteFolder not implemented for Bambu Lab printers"); throw new Error("Method not implemented"); } getSettings() { this.logger.warn("getSettings not implemented for Bambu Lab printers"); throw new Error("Method not implemented"); } async getReprintState() { const state = this.getMqttAdapter().getLastState(); if (!state) return { reprintState: 0, connectionState: null }; const lastFile = state.gcode_file; if (!lastFile) return { reprintState: 1, connectionState: null }; return { file: { path: lastFile, size: -1, date: null, dir: false }, reprintState: 2, connectionState: null }; } logMeta() { return defaultLog; } }; //#endregion export { BambuApi }; //# sourceMappingURL=bambu.api.js.map