bambu-printer-mcp 1.0.8 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +517 -477
- package/dist/3mf_parser.d.ts +15 -1
- package/dist/3mf_parser.js +258 -1
- package/dist/ams-mapping.d.ts +3 -0
- package/dist/ams-mapping.js +45 -0
- package/dist/index.js +1536 -80
- package/dist/printers/bambu.d.ts +134 -0
- package/dist/printers/bambu.js +1013 -53
- package/dist/slicer/profile-flatten.d.ts +75 -0
- package/dist/slicer/profile-flatten.js +446 -0
- package/dist/stl/stl-manipulator.d.ts +31 -3
- package/dist/stl/stl-manipulator.js +300 -52
- package/dist/types.d.ts +35 -0
- package/package.json +8 -3
- package/patches/bambu-node+3.22.21.patch +16 -0
- package/src/3mf_parser.ts +301 -3
- package/src/ams-mapping.ts +47 -0
- package/src/index.ts +1982 -81
- package/src/printers/bambu.ts +1228 -56
- package/src/slicer/profile-flatten.ts +562 -0
- package/src/stl/stl-manipulator.ts +384 -55
- package/src/types.ts +41 -0
|
@@ -9,8 +9,19 @@ import { promisify } from 'util';
|
|
|
9
9
|
import { EventEmitter } from 'events';
|
|
10
10
|
import * as crypto from 'crypto';
|
|
11
11
|
import { execFile } from 'child_process';
|
|
12
|
+
import { flattenForCli, detectProfilesRoot } from '../slicer/profile-flatten.js';
|
|
12
13
|
const readFileAsync = promisify(fs.readFile);
|
|
13
14
|
const writeFileAsync = promisify(fs.writeFile);
|
|
15
|
+
const BAMBU_PROFILE_ROOTS = [
|
|
16
|
+
'/Applications/BambuStudio.app/Contents/Resources/profiles/BBL',
|
|
17
|
+
'/Applications/OrcaSlicer.app/Contents/Resources/profiles/BBL',
|
|
18
|
+
];
|
|
19
|
+
const BAMBU_CLI_BED_TYPES = {
|
|
20
|
+
textured_plate: 'Textured PEI Plate',
|
|
21
|
+
cool_plate: 'Cool Plate',
|
|
22
|
+
engineering_plate: 'Engineering Plate',
|
|
23
|
+
hot_plate: 'High Temp Plate',
|
|
24
|
+
};
|
|
14
25
|
export const SLICER_TYPES = [
|
|
15
26
|
'bambustudio',
|
|
16
27
|
'orcaslicer',
|
|
@@ -55,8 +66,13 @@ export function isBambuCompatibleSlicer(slicerType) {
|
|
|
55
66
|
slicerType === 'orcaslicer' ||
|
|
56
67
|
slicerType === 'orcaslicer-bambulab');
|
|
57
68
|
}
|
|
58
|
-
function isPathLikeExecutable(
|
|
59
|
-
return path.isAbsolute(
|
|
69
|
+
function isPathLikeExecutable(slicerPath) {
|
|
70
|
+
return (path.isAbsolute(slicerPath) ||
|
|
71
|
+
slicerPath.startsWith("./") ||
|
|
72
|
+
slicerPath.startsWith("../") ||
|
|
73
|
+
slicerPath.includes("/") ||
|
|
74
|
+
slicerPath.includes("\\") ||
|
|
75
|
+
/^[A-Za-z]:/.test(slicerPath));
|
|
60
76
|
}
|
|
61
77
|
function configuredBambuProfileDirs() {
|
|
62
78
|
const override = process.env.BAMBU_SLICER_PROFILE_DIRS;
|
|
@@ -82,28 +98,7 @@ function configuredBambuProfileDirs() {
|
|
|
82
98
|
...(appData ? [appData] : []),
|
|
83
99
|
...(localAppData ? [localAppData] : []),
|
|
84
100
|
];
|
|
85
|
-
return roots.flatMap((root) => appNames.map((appName) => path.join(root, appName, 'system', 'BBL'
|
|
86
|
-
}
|
|
87
|
-
function resolveBambuPrinterProfile(printerPreset) {
|
|
88
|
-
if (printerPreset.includes('/') || printerPreset.includes('\\')) {
|
|
89
|
-
return undefined;
|
|
90
|
-
}
|
|
91
|
-
for (const profileDir of configuredBambuProfileDirs()) {
|
|
92
|
-
const profilePath = path.join(profileDir, `${printerPreset}.json`);
|
|
93
|
-
if (fs.existsSync(profilePath)) {
|
|
94
|
-
return profilePath;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
function generatedBambuPrinterSettings(printerPreset) {
|
|
100
|
-
return {
|
|
101
|
-
type: 'machine',
|
|
102
|
-
from: 'system',
|
|
103
|
-
name: printerPreset,
|
|
104
|
-
inherits: printerPreset,
|
|
105
|
-
printer_settings_id: printerPreset,
|
|
106
|
-
};
|
|
101
|
+
return roots.flatMap((root) => appNames.map((appName) => path.join(root, appName, 'system', 'BBL')));
|
|
107
102
|
}
|
|
108
103
|
export class STLManipulator extends EventEmitter {
|
|
109
104
|
constructor(tempDir = path.join(process.cwd(), 'temp')) {
|
|
@@ -121,6 +116,266 @@ export class STLManipulator extends EventEmitter {
|
|
|
121
116
|
generateOperationId() {
|
|
122
117
|
return crypto.randomUUID();
|
|
123
118
|
}
|
|
119
|
+
getAvailableProfileRoots() {
|
|
120
|
+
return [...BAMBU_PROFILE_ROOTS, ...configuredBambuProfileDirs()].filter((root) => fs.existsSync(root));
|
|
121
|
+
}
|
|
122
|
+
findProfileFile(category, profileName) {
|
|
123
|
+
if (!profileName) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
for (const root of this.getAvailableProfileRoots()) {
|
|
127
|
+
const candidate = path.join(root, category, `${profileName}.json`);
|
|
128
|
+
if (fs.existsSync(candidate)) {
|
|
129
|
+
return candidate;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
buildFilamentIdIndex() {
|
|
135
|
+
const index = new Map();
|
|
136
|
+
for (const root of this.getAvailableProfileRoots()) {
|
|
137
|
+
const filamentDir = path.join(root, 'filament');
|
|
138
|
+
if (!fs.existsSync(filamentDir)) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
for (const entry of fs.readdirSync(filamentDir)) {
|
|
142
|
+
if (!entry.endsWith('.json')) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const filePath = path.join(filamentDir, entry);
|
|
146
|
+
try {
|
|
147
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
148
|
+
const filamentId = typeof parsed?.filament_id === 'string' ? parsed.filament_id.trim() : '';
|
|
149
|
+
if (filamentId && !index.has(filamentId)) {
|
|
150
|
+
index.set(filamentId, filePath);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Ignore malformed JSON.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return index;
|
|
159
|
+
}
|
|
160
|
+
readJsonFile(filePath) {
|
|
161
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
162
|
+
}
|
|
163
|
+
stripAbsoluteExtruderResets(value) {
|
|
164
|
+
if (typeof value === 'string') {
|
|
165
|
+
return value
|
|
166
|
+
.split(/\r?\n/)
|
|
167
|
+
.filter((line) => line.trim().toUpperCase() !== 'G92 E0')
|
|
168
|
+
.join('\n');
|
|
169
|
+
}
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
return value.filter((line) => String(line).trim().toUpperCase() !== 'G92 E0');
|
|
172
|
+
}
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
sanitizeProcessForOrca(processConfig, printerPreset) {
|
|
176
|
+
const sanitized = { ...processConfig };
|
|
177
|
+
sanitized.type = 'process';
|
|
178
|
+
if (!sanitized.from || sanitized.from === 'project') {
|
|
179
|
+
sanitized.from = 'User';
|
|
180
|
+
}
|
|
181
|
+
if (printerPreset) {
|
|
182
|
+
sanitized.compatible_printers = [printerPreset];
|
|
183
|
+
}
|
|
184
|
+
if (sanitized.prime_tower_brim_width !== undefined &&
|
|
185
|
+
Number(sanitized.prime_tower_brim_width) < 0) {
|
|
186
|
+
sanitized.prime_tower_brim_width = '0';
|
|
187
|
+
}
|
|
188
|
+
else if (sanitized.prime_tower_brim_width !== undefined) {
|
|
189
|
+
sanitized.prime_tower_brim_width = String(sanitized.prime_tower_brim_width);
|
|
190
|
+
}
|
|
191
|
+
sanitized.use_relative_e_distances = '0';
|
|
192
|
+
sanitized.before_layer_change_gcode = this.stripAbsoluteExtruderResets(sanitized.before_layer_change_gcode);
|
|
193
|
+
sanitized.layer_gcode = this.stripAbsoluteExtruderResets(sanitized.layer_gcode);
|
|
194
|
+
sanitized.layer_change_gcode = this.stripAbsoluteExtruderResets(sanitized.layer_change_gcode);
|
|
195
|
+
return sanitized;
|
|
196
|
+
}
|
|
197
|
+
writeTempJson(outputBase, suffix, value) {
|
|
198
|
+
const outPath = path.join(this.tempDir, `${outputBase}_${suffix}.json`);
|
|
199
|
+
fs.writeFileSync(outPath, JSON.stringify(value, null, 2));
|
|
200
|
+
return outPath;
|
|
201
|
+
}
|
|
202
|
+
resolveBambuLikeSettingsBundle(outputBase, slicerType, slicerProfile, printerPreset, bambuOptions) {
|
|
203
|
+
const machinePath = this.findProfileFile('machine', printerPreset);
|
|
204
|
+
const machineConfig = machinePath ? this.readJsonFile(machinePath) : null;
|
|
205
|
+
const hasSlicerProfile = !!slicerProfile && fs.existsSync(slicerProfile);
|
|
206
|
+
const filamentPaths = [];
|
|
207
|
+
let processPath;
|
|
208
|
+
let parsedProfile = null;
|
|
209
|
+
if (hasSlicerProfile) {
|
|
210
|
+
try {
|
|
211
|
+
parsedProfile = this.readJsonFile(slicerProfile);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
parsedProfile = null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (parsedProfile && typeof parsedProfile === 'object') {
|
|
218
|
+
const inheritedProcessName = (typeof parsedProfile.inherits === 'string' && parsedProfile.inherits) ||
|
|
219
|
+
(typeof parsedProfile.print_settings_id === 'string' &&
|
|
220
|
+
parsedProfile.print_settings_id !== parsedProfile.name
|
|
221
|
+
? parsedProfile.print_settings_id
|
|
222
|
+
: undefined) ||
|
|
223
|
+
(typeof parsedProfile.default_print_profile === 'string'
|
|
224
|
+
? parsedProfile.default_print_profile
|
|
225
|
+
: undefined) ||
|
|
226
|
+
(typeof machineConfig?.default_print_profile === 'string'
|
|
227
|
+
? machineConfig.default_print_profile
|
|
228
|
+
: undefined);
|
|
229
|
+
const inheritedProcessPath = this.findProfileFile('process', inheritedProcessName);
|
|
230
|
+
const inheritedProcess = inheritedProcessPath && fs.existsSync(inheritedProcessPath)
|
|
231
|
+
? this.readJsonFile(inheritedProcessPath)
|
|
232
|
+
: {};
|
|
233
|
+
const mergedProcess = {
|
|
234
|
+
...inheritedProcess,
|
|
235
|
+
...parsedProfile,
|
|
236
|
+
type: 'process',
|
|
237
|
+
};
|
|
238
|
+
processPath = this.writeTempJson(outputBase, slicerType === 'orcaslicer' ? 'process_orca' : 'process', slicerType === 'orcaslicer'
|
|
239
|
+
? this.sanitizeProcessForOrca(mergedProcess, printerPreset)
|
|
240
|
+
: mergedProcess);
|
|
241
|
+
const defaultFilamentProfiles = Array.isArray(parsedProfile.default_filament_profile)
|
|
242
|
+
? parsedProfile.default_filament_profile
|
|
243
|
+
: [];
|
|
244
|
+
for (const profileName of defaultFilamentProfiles) {
|
|
245
|
+
const filamentPath = this.findProfileFile('filament', String(profileName));
|
|
246
|
+
if (filamentPath) {
|
|
247
|
+
filamentPaths.push(filamentPath);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (filamentPaths.length === 0 && Array.isArray(parsedProfile.filament_ids)) {
|
|
251
|
+
const filamentIdIndex = this.buildFilamentIdIndex();
|
|
252
|
+
for (const filamentId of parsedProfile.filament_ids) {
|
|
253
|
+
const filamentPath = filamentIdIndex.get(String(filamentId));
|
|
254
|
+
if (filamentPath) {
|
|
255
|
+
filamentPaths.push(filamentPath);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (hasSlicerProfile) {
|
|
261
|
+
processPath = slicerProfile;
|
|
262
|
+
}
|
|
263
|
+
if (!processPath) {
|
|
264
|
+
const defaultProcessName = typeof machineConfig?.default_print_profile === 'string'
|
|
265
|
+
? machineConfig.default_print_profile
|
|
266
|
+
: undefined;
|
|
267
|
+
const defaultProcessPath = this.findProfileFile('process', defaultProcessName);
|
|
268
|
+
if (defaultProcessPath) {
|
|
269
|
+
processPath =
|
|
270
|
+
slicerType === 'orcaslicer'
|
|
271
|
+
? this.writeTempJson(outputBase, 'process_default_orca', this.sanitizeProcessForOrca(this.readJsonFile(defaultProcessPath), printerPreset))
|
|
272
|
+
: defaultProcessPath;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (!bambuOptions?.loadFilaments &&
|
|
276
|
+
filamentPaths.length === 0 &&
|
|
277
|
+
Array.isArray(machineConfig?.default_filament_profile)) {
|
|
278
|
+
for (const profileName of machineConfig.default_filament_profile) {
|
|
279
|
+
const filamentPath = this.findProfileFile('filament', String(profileName));
|
|
280
|
+
if (filamentPath) {
|
|
281
|
+
filamentPaths.push(filamentPath);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (bambuOptions?.loadFilaments) {
|
|
286
|
+
filamentPaths.length = 0;
|
|
287
|
+
for (const filamentPath of bambuOptions.loadFilaments.split(';')) {
|
|
288
|
+
const trimmed = filamentPath.trim();
|
|
289
|
+
if (trimmed) {
|
|
290
|
+
filamentPaths.push(trimmed);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const settingsParts = [machinePath, processPath].filter((entry) => Boolean(entry));
|
|
295
|
+
return {
|
|
296
|
+
settingsArg: settingsParts.length > 0 ? settingsParts.join(';') : undefined,
|
|
297
|
+
filamentPaths: Array.from(new Set(filamentPaths)),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Optionally rewrite a Bambu-like settings bundle so the paths point at
|
|
302
|
+
* fully-flattened temp configs instead of the BBL-shipped leaf JSONs.
|
|
303
|
+
*
|
|
304
|
+
* BambuStudio's CLI does not resolve the `inherits` chain when loading
|
|
305
|
+
* profiles via --load-settings / --load-filaments, which causes a
|
|
306
|
+
* cluster of upstream bugs (see https://github.com/bambulab/BambuStudio/issues/9636
|
|
307
|
+
* and #9968). Our flattener (src/slicer/profile-flatten.ts) reproduces
|
|
308
|
+
* what the GUI does at slice time so the CLI accepts the configs.
|
|
309
|
+
*
|
|
310
|
+
* Opt-in via `BAMBU_CLI_FLATTEN=true`. When the env var is unset or
|
|
311
|
+
* not "true"/"1", returns the bundle unchanged so behavior is
|
|
312
|
+
* backward-compatible. When enabled, only BBL-shipped leaves get
|
|
313
|
+
* flattened; user-provided custom configs pass through untouched.
|
|
314
|
+
*/
|
|
315
|
+
async maybeFlattenBundle(bundle, bambuOptions, activeSlicerPath) {
|
|
316
|
+
const flag = process.env.BAMBU_CLI_FLATTEN;
|
|
317
|
+
if (flag !== 'true' && flag !== '1')
|
|
318
|
+
return bundle;
|
|
319
|
+
if (!bundle.settingsArg)
|
|
320
|
+
return bundle;
|
|
321
|
+
// settingsArg is "machine.json;process.json".
|
|
322
|
+
const parts = bundle.settingsArg.split(';').filter(Boolean);
|
|
323
|
+
if (parts.length < 2)
|
|
324
|
+
return bundle;
|
|
325
|
+
const [machinePath, processPath] = parts;
|
|
326
|
+
// Pull leaf names from each JSON's `name` field. If any is missing or
|
|
327
|
+
// looks non-BBL (e.g. a user-saved custom config), bail out and
|
|
328
|
+
// return the original bundle untouched.
|
|
329
|
+
const machineLeaf = this.readLeafName(machinePath);
|
|
330
|
+
const processLeaf = this.readLeafName(processPath);
|
|
331
|
+
const filamentLeaves = bundle.filamentPaths
|
|
332
|
+
.map((p) => this.readLeafName(p))
|
|
333
|
+
.filter((n) => Boolean(n));
|
|
334
|
+
if (!machineLeaf || !processLeaf || filamentLeaves.length === 0) {
|
|
335
|
+
console.log('[cli-flatten] skipping: could not derive BBL leaf names from bundle paths');
|
|
336
|
+
return bundle;
|
|
337
|
+
}
|
|
338
|
+
const bedType = this.resolveBambuStudioBedType(bambuOptions?.bedType);
|
|
339
|
+
try {
|
|
340
|
+
const profilesRoot = detectProfilesRoot(activeSlicerPath || process.env.SLICER_PATH);
|
|
341
|
+
const flat = await flattenForCli({
|
|
342
|
+
machineLeaf,
|
|
343
|
+
processLeaf,
|
|
344
|
+
filamentLeaves,
|
|
345
|
+
profilesRoot,
|
|
346
|
+
tempDir: this.tempDir,
|
|
347
|
+
bedType,
|
|
348
|
+
});
|
|
349
|
+
console.log(`[cli-flatten] applied for ${machineLeaf} (cliOverlay=${flat.meta.cliOverlayApplied})`);
|
|
350
|
+
return {
|
|
351
|
+
settingsArg: `${flat.machinePath};${flat.processPath}`,
|
|
352
|
+
filamentPaths: flat.filamentPaths,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
console.error(`[cli-flatten] failed, falling back to unflattened bundle: ${err?.message ?? err}`);
|
|
357
|
+
return bundle;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
resolveBambuStudioBedType(bedType) {
|
|
361
|
+
if (!bedType)
|
|
362
|
+
return undefined;
|
|
363
|
+
const normalized = bedType.trim().toLowerCase();
|
|
364
|
+
if (normalized === 'supertack_plate') {
|
|
365
|
+
throw new Error('BambuStudio CLI SuperTack bed type is not verified; use a pre-sliced 3MF or choose textured_plate, cool_plate, engineering_plate, or hot_plate.');
|
|
366
|
+
}
|
|
367
|
+
return BAMBU_CLI_BED_TYPES[normalized] || bedType;
|
|
368
|
+
}
|
|
369
|
+
/** Read a profile JSON's top-level `name` field, or null if unreadable. */
|
|
370
|
+
readLeafName(filePath) {
|
|
371
|
+
try {
|
|
372
|
+
const data = this.readJsonFile(filePath);
|
|
373
|
+
return typeof data?.name === 'string' && data.name.length > 0 ? data.name : null;
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
124
379
|
/**
|
|
125
380
|
* Load STL file and return geometry and bounding box
|
|
126
381
|
*/
|
|
@@ -828,12 +1083,12 @@ export class STLManipulator extends EventEmitter {
|
|
|
828
1083
|
/**
|
|
829
1084
|
* Slice an STL or 3MF file using the specified slicer
|
|
830
1085
|
* @param stlFilePath Path to the input STL or 3MF file
|
|
831
|
-
* @param slicerType Type of slicer (prusaslicer, cura, slic3r, orcaslicer,
|
|
1086
|
+
* @param slicerType Type of slicer (prusaslicer, cura, slic3r, orcaslicer, bambustudio)
|
|
832
1087
|
* @param slicerPath Path to the slicer executable
|
|
833
1088
|
* @param slicerProfile Optional path to the slicer profile/config file
|
|
834
1089
|
* @param progressCallback Optional callback for progress updates
|
|
835
|
-
* @param printerPreset Optional
|
|
836
|
-
* @param bambuOptions Optional
|
|
1090
|
+
* @param printerPreset Optional BambuStudio printer preset name (e.g., "Bambu Lab P1S 0.4 nozzle")
|
|
1091
|
+
* @param bambuOptions Optional BambuStudio-specific CLI flags
|
|
837
1092
|
* @returns Path to the generated G-code or sliced 3MF file
|
|
838
1093
|
*/
|
|
839
1094
|
async sliceSTL(stlFilePath, slicerType, slicerPath, slicerProfile, progressCallback, printerPreset, bambuOptions) {
|
|
@@ -882,40 +1137,31 @@ export class STLManipulator extends EventEmitter {
|
|
|
882
1137
|
case 'orcaslicer':
|
|
883
1138
|
case 'orcaslicer-bambulab':
|
|
884
1139
|
// Bambu-compatible CLI: slice and export as 3MF with embedded gcode.
|
|
885
|
-
// FULU's OrcaSlicer-bambulab fork keeps
|
|
886
|
-
// --slice/--export-3mf path, so it must not use the generic Prusa
|
|
887
|
-
// --output G-code flow.
|
|
1140
|
+
// FULU's OrcaSlicer-bambulab fork keeps this Bambu-style path.
|
|
888
1141
|
// For 3MF input: slice in place and export sliced 3MF
|
|
889
1142
|
// For STL input: slice and export as 3MF
|
|
890
1143
|
{
|
|
891
1144
|
const is3mf = stlFilePath.toLowerCase().endsWith('.3mf');
|
|
892
1145
|
const outputBase = path.basename(stlFilePath, is3mf ? '.3mf' : '.stl');
|
|
893
1146
|
const bambuOutputPath = path.join(this.tempDir, outputBase + '_sliced.3mf');
|
|
1147
|
+
const outputDir = path.dirname(bambuOutputPath);
|
|
1148
|
+
const rawBundle = this.resolveBambuLikeSettingsBundle(outputBase, slicerType, slicerProfile, printerPreset, bambuOptions);
|
|
1149
|
+
// Opt-in flatten step: walks the BBL `inherits` chain so the
|
|
1150
|
+
// CLI accepts what we hand it. See maybeFlattenBundle docstring.
|
|
1151
|
+
const settingsBundle = slicerType === 'bambustudio'
|
|
1152
|
+
? await this.maybeFlattenBundle(rawBundle, bambuOptions, slicerPath)
|
|
1153
|
+
: rawBundle;
|
|
894
1154
|
args = [
|
|
895
1155
|
'--slice', String(bambuOptions?.slicePlate ?? 0),
|
|
896
|
-
'--
|
|
1156
|
+
'--outputdir', outputDir,
|
|
1157
|
+
'--export-3mf', path.basename(bambuOutputPath),
|
|
897
1158
|
];
|
|
898
|
-
if (
|
|
899
|
-
args.push('--load-settings',
|
|
1159
|
+
if (settingsBundle.settingsArg) {
|
|
1160
|
+
args.push('--load-settings', settingsBundle.settingsArg);
|
|
900
1161
|
}
|
|
901
1162
|
// Always allow newer-version 3MF files (the CLI rejects them by default)
|
|
902
1163
|
args.push('--allow-newer-file');
|
|
903
|
-
//
|
|
904
|
-
// prefer the slicer's installed machine profile. Real BambuStudio rejects
|
|
905
|
-
// skeletal JSON without profile metadata, so only fall back to generated
|
|
906
|
-
// settings when no installed profile can be found.
|
|
907
|
-
if (printerPreset && !slicerProfile) {
|
|
908
|
-
const installedProfile = resolveBambuPrinterProfile(printerPreset);
|
|
909
|
-
if (installedProfile) {
|
|
910
|
-
args.push('--load-settings', installedProfile);
|
|
911
|
-
}
|
|
912
|
-
else {
|
|
913
|
-
const presetJson = path.join(this.tempDir, '_printer_preset.json');
|
|
914
|
-
fs.writeFileSync(presetJson, JSON.stringify(generatedBambuPrinterSettings(printerPreset), null, 2));
|
|
915
|
-
args.push('--load-settings', presetJson);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
// Bambu-compatible slicer flags
|
|
1164
|
+
// BambuSliceOptions flags
|
|
919
1165
|
if (bambuOptions?.uptodate)
|
|
920
1166
|
args.push('--uptodate');
|
|
921
1167
|
if (bambuOptions?.ensureOnBed)
|
|
@@ -946,12 +1192,14 @@ export class STLManipulator extends EventEmitter {
|
|
|
946
1192
|
args.push('--clone-objects', bambuOptions.cloneObjects);
|
|
947
1193
|
if (bambuOptions?.skipObjects)
|
|
948
1194
|
args.push('--skip-objects', bambuOptions.skipObjects);
|
|
949
|
-
if (
|
|
950
|
-
args.push('--load-filaments',
|
|
1195
|
+
if (settingsBundle.filamentPaths.length > 0) {
|
|
1196
|
+
args.push('--load-filaments', settingsBundle.filamentPaths.join(';'));
|
|
1197
|
+
args.push('--load-defaultfila');
|
|
1198
|
+
}
|
|
951
1199
|
if (bambuOptions?.loadFilamentIds)
|
|
952
1200
|
args.push('--load-filament-ids', bambuOptions.loadFilamentIds);
|
|
953
1201
|
args.push(stlFilePath);
|
|
954
|
-
// Override outputFilePath
|
|
1202
|
+
// Override outputFilePath for bambustudio since it produces 3MF, not gcode
|
|
955
1203
|
outputFilePath = bambuOutputPath;
|
|
956
1204
|
}
|
|
957
1205
|
break;
|
package/dist/types.d.ts
CHANGED
|
@@ -55,3 +55,38 @@ export interface ThreeMFData {
|
|
|
55
55
|
};
|
|
56
56
|
slicerConfig?: Partial<BambuSlicerConfig>;
|
|
57
57
|
}
|
|
58
|
+
export interface CollarCharmRole {
|
|
59
|
+
role: 'inner' | 'outer';
|
|
60
|
+
objectIndex: number;
|
|
61
|
+
name: string;
|
|
62
|
+
area: number;
|
|
63
|
+
filamentPosition: number;
|
|
64
|
+
}
|
|
65
|
+
export interface CollarCharmAnalysis {
|
|
66
|
+
plateIndex: number;
|
|
67
|
+
usedFilamentPositions: number[];
|
|
68
|
+
amsSlots: number[];
|
|
69
|
+
roles: CollarCharmRole[];
|
|
70
|
+
}
|
|
71
|
+
export interface ThreeMFFilamentRequirement {
|
|
72
|
+
filamentPosition: number;
|
|
73
|
+
filamentId: number;
|
|
74
|
+
tray_info_idx: string | null;
|
|
75
|
+
type: string | null;
|
|
76
|
+
color: string | null;
|
|
77
|
+
}
|
|
78
|
+
export interface ThreeMFAmsRequirements {
|
|
79
|
+
plateIndex: number;
|
|
80
|
+
usedFilamentPositions: number[];
|
|
81
|
+
filaments: ThreeMFFilamentRequirement[];
|
|
82
|
+
}
|
|
83
|
+
export interface ThreeMFPlateObject {
|
|
84
|
+
id: number;
|
|
85
|
+
name: string;
|
|
86
|
+
area: number | null;
|
|
87
|
+
bbox: unknown;
|
|
88
|
+
}
|
|
89
|
+
export interface ThreeMFPlateObjects {
|
|
90
|
+
plateIndex: number;
|
|
91
|
+
objects: ThreeMFPlateObject[];
|
|
92
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bambu-printer-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "MCP server for Bambu Lab 3D printers with STL manipulation, slicing, and print management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"start:stdio": "MCP_TRANSPORT=stdio node dist/index.js",
|
|
14
14
|
"start:http": "MCP_TRANSPORT=streamable-http node dist/index.js",
|
|
15
15
|
"build": "tsc",
|
|
16
|
-
"test": "npm run build && node --test tests/**/*.test.mjs"
|
|
16
|
+
"test": "npm run build && node --test tests/*.test.mjs tests/**/*.test.mjs",
|
|
17
|
+
"postinstall": "patch-package"
|
|
17
18
|
},
|
|
18
19
|
"bin": {
|
|
19
20
|
"bambu-printer-mcp": "dist/index.js"
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"files": [
|
|
22
23
|
"dist",
|
|
23
24
|
"src",
|
|
25
|
+
"patches",
|
|
24
26
|
"README.md",
|
|
25
27
|
"LICENSE"
|
|
26
28
|
],
|
|
@@ -42,12 +44,15 @@
|
|
|
42
44
|
"basic-ftp": "^5.0.5",
|
|
43
45
|
"dotenv": "^16.3.1",
|
|
44
46
|
"jszip": "^3.10.1",
|
|
47
|
+
"mqtt": "^5.15.1",
|
|
48
|
+
"patch-package": "^8.0.1",
|
|
45
49
|
"three": "^0.173.0",
|
|
46
50
|
"xml2js": "^0.6.2"
|
|
47
51
|
},
|
|
48
52
|
"devDependencies": {
|
|
49
53
|
"@types/node": "^18.0.0",
|
|
50
54
|
"@types/three": "^0.173.0",
|
|
51
|
-
"@types/xml2js": "^0.4.14"
|
|
55
|
+
"@types/xml2js": "^0.4.14",
|
|
56
|
+
"typescript": "^6.0.3"
|
|
52
57
|
}
|
|
53
58
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
diff --git a/node_modules/bambu-node/dist/index.js b/node_modules/bambu-node/dist/index.js
|
|
2
|
+
index 916e924..6cc3f33 100644
|
|
3
|
+
--- a/node_modules/bambu-node/dist/index.js
|
|
4
|
+
+++ b/node_modules/bambu-node/dist/index.js
|
|
5
|
+
@@ -1,4 +1,4 @@
|
|
6
|
+
-var B=Object.defineProperty;var he=(e,_,t)=>_ in e?B(e,_,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[_]=t;var r=(e,_)=>B(e,"name",{value:_,configurable:!0});var n=(e,_,t)=>(he(e,typeof _!="symbol"?_+"":_,t),t);var U=0;function H(){return U+=1,U}r(H,"getSequenceId");var v=class v{constructor(_={}){n(this,"extra");this.extra=_}invoke(_){return _.publish({[this.category]:{sequence_id:H(),command:this.command,...this.extra}})}};r(v,"AbstractCommand");var f=v;var A=class A extends f{constructor(t){super({param:t.join(`
|
|
7
|
+
+var B=Object.defineProperty;var he=(e,_,t)=>_ in e?B(e,_,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[_]=t;var r=(e,_)=>B(e,"name",{value:_,configurable:!0});var n=(e,_,t)=>(he(e,typeof _!="symbol"?_+"":_,t),t);var U=0;function H(){return String(U+=1)}r(H,"getSequenceId");var v=class v{constructor(_={}){n(this,"extra");this.extra=_}invoke(_){return _.publish({[this.category]:{sequence_id:H(),command:this.command,...this.extra}})}};r(v,"AbstractCommand");var f=v;var A=class A extends f{constructor(t){super({param:t.join(`
|
|
8
|
+
`)+`
|
|
9
|
+
`});n(this,"category","print")}};r(A,"GCodeCommand");var d=A;function y(e){return!!e?.print&&!!e?.print?.command&&["push_status","resume","gcode_line","gcode_file","project_file"].includes(e.print.command)}r(y,"isPrintMessage");function q(e){return e.command==="gcode_file"}r(q,"isGcodeFileCommand");function j(e){return e.command==="gcode_line"}r(j,"isGCodeLineCommand");function Oe(e){return e.command==="project_file"}r(Oe,"isProjectFileCommand");function J(e){return e.command==="push_status"&&Object.keys(e).length<40}r(J,"isPushStatusCommand");var k={GFB00:{type:"filament",name:"Bambu ABS @base",inherits:"fdm_filament_abs",from:"system",instantiation:"false",activate_air_filtration:["1"],cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["90"],eng_plate_temp_initial_layer:["90"],fan_cooling_layer_time:["30"],fan_max_speed:["80"],fan_min_speed:["10"],filament_cost:["24.99"],filament_density:["1.04"],filament_max_volumetric_speed:["28.6"],filament_type:["ABS"],hot_plate_temp:["90"],hot_plate_temp_initial_layer:["90"],nozzle_temperature:["270"],nozzle_temperature_initial_layer:["260"],nozzle_temperature_range_high:["280"],nozzle_temperature_range_low:["240"],overhang_fan_speed:["80"],overhang_fan_threshold:["25%"],reduce_fan_stop_start_freq:["1"],slow_down_layer_time:["3"],slow_down_min_speed:["20"],textured_plate_temp:["90"],textured_plate_temp_initial_layer:["90"],filament_id:"GFB00",filament_flow_ratio:["0.95"],filament_vendor:["Bambu Lab"]},GFB01:{type:"filament",name:"Bambu ASA @base",inherits:"fdm_filament_asa",from:"system",instantiation:"false",activate_air_filtration:["1"],cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["90"],eng_plate_temp_initial_layer:["90"],fan_cooling_layer_time:["35"],fan_max_speed:["35"],fan_min_speed:["10"],filament_cost:["31.99"],filament_density:["1.05"],filament_max_volumetric_speed:["18"],filament_type:["ASA"],hot_plate_temp:["90"],hot_plate_temp_initial_layer:["90"],nozzle_temperature:["270"],nozzle_temperature_initial_layer:["270"],nozzle_temperature_range_high:["280"],nozzle_temperature_range_low:["240"],overhang_fan_speed:["80"],overhang_fan_threshold:["25%"],reduce_fan_stop_start_freq:["1"],slow_down_layer_time:["12"],slow_down_min_speed:["20"],textured_plate_temp:["90"],textured_plate_temp_initial_layer:["90"],filament_id:"GFB01",filament_flow_ratio:["0.95"],filament_vendor:["Bambu Lab"]},GFN03:{type:"filament",name:"Bambu PA-CF @base",inherits:"fdm_filament_pa",from:"system",instantiation:"false",activate_air_filtration:["1"],cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["100"],eng_plate_temp_initial_layer:["100"],fan_cooling_layer_time:["4"],fan_max_speed:["60"],fan_min_speed:["0"],filament_cost:["84.99"],filament_density:["1.09"],filament_max_volumetric_speed:["8"],filament_type:["PA-CF"],hot_plate_temp:["100"],hot_plate_temp_initial_layer:["100"],nozzle_temperature:["280"],nozzle_temperature_initial_layer:["280"],nozzle_temperature_range_high:["300"],nozzle_temperature_range_low:["260"],overhang_fan_speed:["40"],required_nozzle_HRC:["40"],slow_down_layer_time:["2"],slow_down_min_speed:["20"],temperature_vitrification:["170"],textured_plate_temp:["100"],textured_plate_temp_initial_layer:["100"],filament_id:"GFN03",filament_flow_ratio:["0.96"],filament_vendor:["Bambu Lab"],overhang_fan_threshold:["0%"]},GFN05:{type:"filament",name:"Bambu PA6-CF @base",inherits:"fdm_filament_pa",from:"system",instantiation:"false",activate_air_filtration:["1"],cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["100"],eng_plate_temp_initial_layer:["100"],fan_cooling_layer_time:["5"],fan_max_speed:["30"],fan_min_speed:["10"],filament_cost:["79.99"],filament_density:["1.10"],filament_max_volumetric_speed:["8"],filament_type:["PA6-CF"],hot_plate_temp:["100"],hot_plate_temp_initial_layer:["100"],nozzle_temperature:["275"],nozzle_temperature_initial_layer:["275"],nozzle_temperature_range_high:["300"],nozzle_temperature_range_low:["260"],overhang_fan_speed:["40"],required_nozzle_HRC:["40"],slow_down_layer_time:["2"],slow_down_min_speed:["20"],temperature_vitrification:["170"],textured_plate_temp:["100"],textured_plate_temp_initial_layer:["100"],filament_id:"GFN05",filament_flow_ratio:["0.96"],filament_vendor:["Bambu Lab"],overhang_fan_threshold:["0%"]},GFN08:{type:"filament",name:"Bambu PA6-GF @base",inherits:"fdm_filament_pa",from:"system",instantiation:"false",activate_air_filtration:["1"],cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["100"],eng_plate_temp_initial_layer:["100"],fan_cooling_layer_time:["5"],fan_max_speed:["30"],fan_min_speed:["10"],filament_cost:["59.99"],filament_density:["1.14"],filament_max_volumetric_speed:["8"],filament_type:["PA-GF"],hot_plate_temp:["100"],hot_plate_temp_initial_layer:["100"],nozzle_temperature:["290"],nozzle_temperature_initial_layer:["290"],nozzle_temperature_range_high:["300"],nozzle_temperature_range_low:["260"],overhang_fan_speed:["40"],required_nozzle_HRC:["40"],slow_down_layer_time:["2"],slow_down_min_speed:["20"],temperature_vitrification:["180"],textured_plate_temp:["100"],textured_plate_temp_initial_layer:["100"],filament_id:"GFN08",filament_flow_ratio:["0.96"],filament_vendor:["Bambu Lab"],full_fan_speed_layer:["2"],overhang_fan_threshold:["0%"]},GFN04:{type:"filament",name:"Bambu PAHT-CF @base",inherits:"fdm_filament_pa",from:"system",instantiation:"false",activate_air_filtration:["1"],cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["100"],eng_plate_temp_initial_layer:["100"],fan_cooling_layer_time:["5"],fan_max_speed:["30"],fan_min_speed:["10"],filament_cost:["94.99"],filament_density:["1.06"],filament_max_volumetric_speed:["8"],filament_type:["PA-CF"],hot_plate_temp:["100"],hot_plate_temp_initial_layer:["100"],nozzle_temperature:["290"],nozzle_temperature_initial_layer:["290"],nozzle_temperature_range_high:["300"],nozzle_temperature_range_low:["260"],overhang_fan_speed:["40"],required_nozzle_HRC:["40"],slow_down_layer_time:["2"],slow_down_min_speed:["20"],temperature_vitrification:["180"],textured_plate_temp:["100"],textured_plate_temp_initial_layer:["100"],filament_id:"GFN04",full_fan_speed_layer:["2"],filament_vendor:["Bambu Lab"],filament_flow_ratio:["0.96"],overhang_fan_threshold:["0%"]},GFC00:{type:"filament",name:"Bambu PC @base",inherits:"fdm_filament_pc",from:"system",instantiation:"false",cool_plate_temp:["0"],cool_plate_temp_initial_layer:["0"],eng_plate_temp:["110"],eng_plate_temp_initial_layer:["110"],fan_cooling_layer_time:["30"],fan_max_speed:["60"],fan_min_speed:["10"],filament_cost:["39.99"],filament_density:["1.04"],filament_max_volumetric_speed:["18"],filament_type:["PC"],hot_plate_temp:["110"],hot_plate_temp_initial_layer:["110"],nozzle_temperature:["280"],nozzle_temperature_initial_layer:["270"],nozzle_temperature_range_high:["290"],nozzle_temperature_range_low:["260"],overhang_fan_speed:["60"],overhang_fan_threshold:["25%"],reduce_fan_stop_start_freq:["1"],slow_down_layer_time:["2"],slow_down_min_speed:["20"],temperature_vitrification:["120"],textured_plate_temp:["110"],textured_plate_temp_initial_layer:["110"],filament_id:"GFC00",filament_vendor:["Bambu Lab"],filament_flow_ratio:["0.94"]},GFT01:{type:"filament",name:"Bambu PET-CF @base",inherits:"fdm_filament_pet",from:"system",instantiation:"false",eng_plate_temp:["80"],eng_plate_temp_initial_layer:["80"],fan_cooling_layer_time:["5"],fan_min_speed:["10"],filament_cost:["84.99"],filament_density:["1.29"],filament_max_volumetric_speed:["8"],filament_type:["PET-CF"],hot_plate_temp:["100"],hot_plate_temp_initial_layer:["100"],nozzle_temperature:["270"],nozzle_temperature_initial_layer:["270"],nozzle_temperature_range_high:["290"],nozzle_temperature_range_low:["260"],reduce_fan_stop_start_freq:["1"],temperature_vitrification:["185"],textured_plate_temp:["100"],textured_plate_temp_initial_layer:["100"],filament_start_gcode:[`; filament start gcode
|
|
10
|
+
{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255
|
|
11
|
+
@@ -330,4 +330,4 @@ M106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]}
|
|
12
|
+
|
|
13
|
+
{if activate_air_filtration[current_extruder] && support_air_filtration}
|
|
14
|
+
M106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]}
|
|
15
|
+
-{endif}`],filament_id:"GFL01",filament_flow_ratio:["0.98"],filament_vendor:["Polymaker"]}};var W;(function(e){e[e.PRINTING=0]="PRINTING",e[e.BED_LEVELING=1]="BED_LEVELING",e[e.HEATBED_PREHEATING=2]="HEATBED_PREHEATING",e[e.XY_MECH_MODE_SWEEP=3]="XY_MECH_MODE_SWEEP",e[e.CHANGE_MATERIAL=4]="CHANGE_MATERIAL",e[e.M400_PAUSE=5]="M400_PAUSE",e[e.FILAMENT_RUNOUT_PAUSE=6]="FILAMENT_RUNOUT_PAUSE",e[e.HOTEND_HEATING=7]="HOTEND_HEATING",e[e.EXTRUDE_COMPENSATION_SCAN=8]="EXTRUDE_COMPENSATION_SCAN",e[e.BED_SCAN=9]="BED_SCAN",e[e.FIRST_LAYER_SCAN=10]="FIRST_LAYER_SCAN",e[e.BE_SURFACE_TYPT_IDENTIFICATION=11]="BE_SURFACE_TYPT_IDENTIFICATION",e[e.SCANNER_EXTRINSIC_PARA_CALI=12]="SCANNER_EXTRINSIC_PARA_CALI",e[e.TOOLHEAD_HOMING=13]="TOOLHEAD_HOMING",e[e.NOZZLE_TIP_CLEANING=14]="NOZZLE_TIP_CLEANING",e[e.EXTRUDER_TEMP_PROTECT_CALI=15]="EXTRUDER_TEMP_PROTECT_CALI",e[e.USER_PAUSE=16]="USER_PAUSE",e[e.TOOLHEAD_SHELL_OFF_PAUSE=17]="TOOLHEAD_SHELL_OFF_PAUSE",e[e.SCANNER_LASER_PARA_CALI=18]="SCANNER_LASER_PARA_CALI",e[e.EXTRUDER_ABSOLUTE_FLOW_CALI=19]="EXTRUDER_ABSOLUTE_FLOW_CALI",e[e.HOTEND_TEMPERATURE_ERROR_PAUSE=20]="HOTEND_TEMPERATURE_ERROR_PAUSE",e[e.HEATED_BED_TEMPERATURE_ERROR_PAUSE=21]="HEATED_BED_TEMPERATURE_ERROR_PAUSE",e[e.FILAMENT_UNLOADING=22]="FILAMENT_UNLOADING",e[e.SKIP_STEP_PAUSE=23]="SKIP_STEP_PAUSE",e[e.FILAMENT_LOADING=24]="FILAMENT_LOADING",e[e.MOTOR_NOISE_CALIBRATION=25]="MOTOR_NOISE_CALIBRATION",e[e.AMS_LOST_PAUSE=26]="AMS_LOST_PAUSE",e[e.HEAT_BREAK_FAN_PAUSE=27]="HEAT_BREAK_FAN_PAUSE",e[e.CHAMBER_TEMPERATURE_CONTROL_ERROR_PAUSE=28]="CHAMBER_TEMPERATURE_CONTROL_ERROR_PAUSE",e[e.CHAMBER_COOLING=29]="CHAMBER_COOLING",e[e.USER_INSERT_GCODE_PAUSE=30]="USER_INSERT_GCODE_PAUSE",e[e.MOTOR_NOISE_SHOWOFF=31]="MOTOR_NOISE_SHOWOFF",e[e.NOZZLE_FILAMENT_COVERED_DETECTED_PAUSE=32]="NOZZLE_FILAMENT_COVERED_DETECTED_PAUSE",e[e.CUTTER_ERROR_PAUSE=33]="CUTTER_ERROR_PAUSE",e[e.FIRST_LAYER_ERROR_PAUSE=34]="FIRST_LAYER_ERROR_PAUSE",e[e.NOZZLE_CLOG_PAUSE=35]="NOZZLE_CLOG_PAUSE"})(W||(W={}));var X;(function(e){e.SILENT="1",e.NORMAL="2",e.SPORT="3",e.LUDICROUS="4"})(X||(X={}));var V;(function(e){e[e.IDLE=0]="IDLE",e[e.READING=1]="READING",e[e.GCODE_TRANS=2]="GCODE_TRANS",e[e.GCODE_RUNNING=3]="GCODE_RUNNING",e[e.ASSISTANT=4]="ASSISTANT",e[e.SWITCH_FILAMENT=5]="SWITCH_FILAMENT",e[e.HAS_FILAMENT=6]="HAS_FILAMENT"})(V||(V={}));var $;(function(e){e.DOWNLOADING="DOWNLOADING",e.UPGRADE_REQUEST="UPGRADE_REQUEST",e.PRE_FLASH_START="PRE_FLASH_START",e.PRE_FLASH_SUCCESS="PRE_FLASH_SUCCESS"})($||($={}));var Z;(function(e){e.UPGRADE_SUCCESS="UPGRADE_SUCCESS",e.DOWNLOAD_FAIL="DOWNLOAD_FAIL",e.FLASH_FAIL="FLASH_FAIL",e.PRE_FLASH_FAIL="PRE_FLASH_FAIL",e.UPGRADE_FAIL="UPGRADE_FAIL"})(Z||(Z={}));var Y;(function(e){e[e.UpgradeNoError=0]="UpgradeNoError",e[e.UpgradeDownloadFailed=1]="UpgradeDownloadFailed",e[e.UpgradeVerifyFailed=2]="UpgradeVerifyFailed",e[e.UpgradeFlashFailed=3]="UpgradeFlashFailed",e[e.UpgradePrinting=4]="UpgradePrinting"})(Y||(Y={}));var K;(function(e){e[e.UNAVAILABLE=0]="UNAVAILABLE",e[e.IDLE=1]="IDLE",e[e.UPGRADING=2]="UPGRADING",e[e.UPGRADE_FINISHED=3]="UPGRADE_FINISHED"})(K||(K={}));var Q;(function(e){e.PLA="PLA",e.ABS="ABS",e.TPU="TPU",e.PC="PC",e.ASA="ASA",e.PA_CF="PA-CF",e.PA6_CF="PA6-CF",e.PET_CF="PET-CF",e.PETG="PETG",e.PETG_CF="PETG-CF",e.PLA_AERO="PLA-AERO",e.PLA_CF="PLA-CF",e.PPA_CF="PPA-CF",e.PPA_GF="PPA-GF",e.PA="PA",e.HIPS="HIPS",e.PPS="PPS",e.PPS_CF="PPS-CF",e.PVA="PVA",e.PLA_S="PLA-S",e.PA_S="PLA-S"})(Q||(Q={}));function Je(e){return k[e]??null}r(Je,"getFilamentConfig");function h(e){return e.command==="push_status"&&Object.keys(e).length>=40}r(h,"isPushAllCommand");function ee(e){return e.command==="gcode_line"&&!!e?.param?.startsWith("M106")}r(ee,"isUpdateFanCommand");function te(e){return e.command==="push_status"&&e.hasOwnProperty("lights_report")}r(te,"isUpdateLightCommand");function _e(e){return e.command==="push_status"&&(e.hasOwnProperty("spd_lvl")||e.hasOwnProperty("spd_mag"))}r(_e,"isUpdateSpeedCommand");function re(e){return e.command===e.command}r(re,"isUpdateStateCommand");function ae(e){return e.command==="gcode_line"&&!!(e?.param?.startsWith("M104")||e?.param?.startsWith("M140"))}r(ae,"isUpdateTempCommand");function g(e){return!!e?.mc_print&&!!e?.mc_print?.command&&["push_info"].includes(e.mc_print.command)}r(g,"isMCPrintMessage");function ht(e){return e.command==="push_info"}r(ht,"isPushInfoCommand");function gt(e){let[,_,t,l]=e.param.match(/^\[(.+?)](?:\[(.+?)])?\s*(.+)$/)??[];return{sequenceId:Number(e.sequence_id),category:_,subcategory:t,content:l,rawParam:e.param}}r(gt,"getCleanPushInfoCommand");function x(e){return e.command==="get_version"}r(x,"isGetVersionCommand");function z(e){return!!e?.info&&!!e?.info?.command&&["get_version"].includes(e.info.command)}r(z,"isInfoMessage");var E=class E extends f{constructor({fileName:t}){super({param:t});n(this,"category","print");n(this,"command","gcode_file");n(this,"ownsResponse",q)}};r(E,"GCodeFileCommand");var ie=E;var S=class S extends d{constructor({gcodes:t}){super(t);n(this,"command","gcode_line");n(this,"ownsResponse",j)}};r(S,"GCodeLineCommand");var ne=S;var G=class G extends f{constructor(){super();n(this,"category","info");n(this,"command","get_version");n(this,"ownsResponse",x)}};r(G,"GetVersionCommand");var b=G;var M=class M extends f{constructor(){super();n(this,"category","pushing");n(this,"command","pushall");n(this,"ownsResponse",h)}};r(M,"PushAllCommand");var c=M;var le;(function(e){e[e.PART_COOLING_FAN=1]="PART_COOLING_FAN",e[e.AUXILIARY_FAN=2]="AUXILIARY_FAN",e[e.CHAMBER_FAN=3]="CHAMBER_FAN"})(le||(le={}));var L=class L extends d{constructor({fan:t,speed:l}){super([`M106 P${t.toString()} S${255*l/100}`]);n(this,"command","gcode_line");n(this,"ownsResponse",ee)}};r(L,"UpdateFanCommand");var me=L;var F=class F extends f{constructor({light:t,mode:l,loopOptions:a={led_on_time:0,led_off_time:0,loop_times:0,interval_time:0}}){super({led_node:t,led_mode:l,...a});n(this,"category","system");n(this,"command","ledctrl");n(this,"ownsResponse",te)}};r(F,"UpdateLightCommand");var oe=F;var N=class N extends f{constructor({speed:t}){super({param:t.toString()});n(this,"category","print");n(this,"command","print_speed");n(this,"ownsResponse",_e)}};r(N,"UpdateSpeedCommand");var pe=N;var C=class C extends f{constructor({state:t}){super();n(this,"category","print");n(this,"command");n(this,"ownsResponse",re);this.command=t}};r(C,"UpdateStateCommand");var se=C;var fe;(function(e){e.BED="M140",e.EXTRUDER="M104"})(fe||(fe={}));var T=class T extends d{constructor({part:t,temperature:l}){super([`${t} S${l}`]);n(this,"command","gcode_line");n(this,"ownsResponse",ae)}};r(T,"UpdateTempCommand");var ue=T;var p;(function(e){e.X1C="X1C",e.X1="X1",e.X1E="X1E",e.P1P="P1P",e.P1S="P1S",e.A1="A1",e.A1M="A1M"})(p||(p={}));import{createId as xe}from"@paralleldrive/cuid2";var ze=new RegExp(/\.[^/.]+$/),R=class R{constructor(_,t,l,a=new Date){n(this,"_jobData");let o=[p.P1P,p.P1S].includes(l);this._jobData={id:xe(),printerId:t,printerModel:l,name:_.subtask_name.replace(ze,""),fileName:_.subtask_name,gcodeName:_.gcode_file,jobOrigin:_.print_type,totalLayerNumber:_.total_layer_num,totalPrintTime:_.mc_remaining_time,status:_.gcode_state,stage:_.stg_cur,percentDone:_.mc_percent,layerNumber:_.layer_num,remainingTime:_.mc_remaining_time,skippedObjects:_.s_obj,statusHistory:[{status:_.gcode_state,changedAt:a}],speedLevelHistory:[{speedLevel:_.spd_lvl,speedMagnitude:_.spd_mag,changedAt:a}],printingStageHistory:[{stage:_.stg_cur,changedAt:a}],temperatureHistory:[{bedTemp:_.bed_temper,bedTargetTemp:_.bed_target_temper,interiorTemp:o?_.frame_temper:_.chamber_temper,nozzleTemp:_.nozzle_temper,nozzleTargetTemp:_.nozzle_target_temper,changedAt:a}],tempLastUpdatedAt:new Date,errorCodes:_.hms.map(m=>({attr:m.attr,code:m.code,thrownAt:a,layerNumber:_.layer_num})),createdAt:a}}update(_){if(!this._jobData)return;let t=this._jobData,l=new Date,a=[p.P1P,p.P1S].includes(this._jobData.printerModel);_.gcode_state&&t.statusHistory.at(-1)?.status!==_.gcode_state&&t.statusHistory.push({status:_.gcode_state,changedAt:l}),_.spd_lvl&&_.spd_mag&&t.speedLevelHistory.at(-1)?.speedLevel!==_.spd_lvl&&t.speedLevelHistory.push({speedLevel:_.spd_lvl,speedMagnitude:_.spd_mag,changedAt:l}),_.stg_cur&&t.printingStageHistory.at(-1)?.stage!==_.stg_cur&&t.printingStageHistory.push({stage:_.stg_cur,changedAt:l});let o=!!(_.bed_temper||_.bed_target_temper||(a?_.frame_temper:_.chamber_temper)||_.nozzle_temper||_.nozzle_target_temper),m=t.tempLastUpdatedAt.getTime()+5e3<l.getTime();if(o&&m){let s=a?_.frame_temper:_.chamber_temper,u=t.temperatureHistory.at(-1);t.temperatureHistory.push({bedTemp:_.bed_temper??u.bedTemp,bedTargetTemp:_.bed_target_temper??u.bedTargetTemp,interiorTemp:s??u.interiorTemp,nozzleTemp:_.nozzle_temper??u.nozzleTemp,nozzleTargetTemp:_.nozzle_target_temper??u.nozzleTargetTemp,changedAt:l}),t.tempLastUpdatedAt=l}let i={status:_.gcode_state??t.status,stage:_.stg_cur??t.stage,percentDone:_.mc_percent??t.percentDone,layerNumber:_.layer_num??t.layerNumber,remainingTime:_.mc_remaining_time??t.remainingTime,skippedObjects:_.s_obj??t.skippedObjects,errorCodes:_.hms?.map((s,u)=>({attr:s.attr??t.errorCodes[u].attr,code:s.code??t.errorCodes[u].code,thrownAt:l??t.errorCodes[u].thrownAt,layerNumber:_.layer_num??t.errorCodes[u].layerNumber}))??t.errorCodes};this._jobData={...this._jobData,...i}}get data(){return this._jobData}};r(R,"Job");var w=R;import*as ce from"mqtt";import*as ye from"eventemitter3";import{createId as be}from"@paralleldrive/cuid2";function I(e){return new Promise(_=>setTimeout(_,e))}r(I,"sleep");function O(e,_){let t={},l=r((a,o,m)=>{for(let i in o){let s=o[i],u=a[i];if(!(i in a&&JSON.stringify(a[i])===JSON.stringify(o[i]))){if(typeof s=="object"&&s!==null&&u!==null&&!Array.isArray(s)){m[i]=m[i]||{},l(a[i],o[i],m[i]);continue}m[i]=o[i]}}},"checkDifferences");return l(e,_,t),t}r(O,"subtractObject");var D=class D extends ye.EventEmitter{constructor(t){super();n(this,"clientOptions");n(this,"id");n(this,"mqttClient");n(this,"config");n(this,"_printerData");n(this,"_printerStatus");n(this,"jobHistory");n(this,"currentJob");this.clientOptions=t,this.id=be(),this.mqttClient=void 0,this._printerData={modules:[],model:void 0},this._printerStatus="OFFLINE",this.jobHistory=[],this.currentJob=null,this.config={host:t.host,port:t.port??8883,accessToken:t.accessToken,serialNumber:t.serialNumber,throwOnOfflineCommands:t.throwOnOfflineCommands??!1,reconnectInterval:t.reconnectInterval??1e3,connectTimeout:t.connectTimeout??2e3,keepAlive:t.keepAlive??20},this.jobHistory=t.jobHistory??[],this.currentJob=t.currentJob??null}emit(t,...l){return super.emit(t,...l)}off(t,l){return super.off(t,l),this}once(t,l){return super.once(t,l),this}on(t,l){return super.on(t,l),this}async connectMQTT(){return new Promise((t,l)=>{if(this.mqttClient)throw new Error("Can't establish a new connection while running another one!");this.mqttClient=ce.connect(`mqtts://${this.config.host}:${this.config.port}`,{username:"bblp",password:this.config.accessToken,reconnectPeriod:this.clientOptions.reconnectInterval,connectTimeout:this.clientOptions.connectTimeout,keepalive:this.clientOptions.keepAlive,resubscribe:!0,rejectUnauthorized:!1}),this.mqttClient.on("connect",async(...a)=>{await this.onConnect(...a),this.emit("client:connect"),t()}),this.mqttClient.on("disconnect",()=>{this.emit("client:disconnect",!1),this.emit("printer:statusUpdate",this._printerStatus,"OFFLINE"),this._printerStatus="OFFLINE",this.currentJob&&this.emit("job:pause",this.currentJob,!0)}),this.mqttClient.on("offline",()=>{this.emit("client:disconnect",!0),this.emit("printer:statusUpdate",this._printerStatus,"OFFLINE"),this._printerStatus="OFFLINE",this.currentJob&&this.emit("job:pause",this.currentJob,!0)}),this.mqttClient.on("message",(a,o)=>this.emit("rawMessage",a,o)),this.mqttClient.on("error",a=>{this.emit("client:error",a),l(a)})})}async connect(){return await Promise.all([this.connectMQTT()])}async disconnect(t=!1,l){return new Promise(a=>this.mqttClient?.end(t,l,a))}subscribe(t){if(this._printerStatus!=="OFFLINE"&&this.config.throwOnOfflineCommands)throw new Error(`Unable to subscribe to topic "${t}" while disconnected from printer!`);return new Promise((l,a)=>{if(!this.mqttClient)return a("Client not connected.");this.mqttClient.subscribe(t,m=>{if(m)return a(`Error subscribing to topic '${t}': ${m.message}`)});let o=r(async(m,i)=>{m===t&&await this.onMessage(i.toString(),t)},"listener");this.mqttClient.on("message",o),l()})}async executeCommand(t){if(this._printerStatus!=="OFFLINE"&&this.config.throwOnOfflineCommands)throw new Error("Unable to send commands while disconnected from printer!");return new Promise((l,a)=>{t.invoke(this).catch(m=>a(m));let o=setTimeout(()=>{a(new Error("Command execution timed out after 5 seconds."))},5e3);this.on("rawMessage",(m,i)=>{let s=JSON.parse(i.toString()),u=Object.keys(s)[0];if(!(z(s)||g(s)||y(s)))return;let P=s[u];P?.command&&t.ownsResponse(P)&&(clearTimeout(o),l(P))})})}async onConnect(t){await this.subscribe(`device/${this.config.serialNumber}/report`),await I(1e3),await this.executeCommand(new b),(this._printerData.model===p.P1P||this._printerData.model===p.P1S||this._printerData.model===p.A1||this._printerData.model===p.A1M)&&(await this.executeCommand(new c),setInterval(async()=>{await this.executeCommand(new c)},5*60*1e3))}async onMessage(t,l){let a=JSON.parse(t),o=Object.keys(a)[0];if(z(a)||g(a)||y(a)){if(y(a)&&(this.emit("message",l,o,a.print),(h(a.print)||J(a.print))&&(this._printerData={...this._printerData,...a.print},this.emit("printer:dataUpdate",this._printerData,a.print),a.print.gcode_state&&a.print.gcode_state!==this._printerStatus))){let m=this._printerStatus,i=a.print.gcode_state;if(this.emit("printer:statusUpdate",m,i),(i==="PREPARE"||i==="SLICING")&&(i="IDLE"),(m==="PREPARE"||m==="SLICING")&&(m="RUNNING"),(m==="IDLE"||m==="FINISH"||m==="FAILED")&&i==="RUNNING")this.currentJob!==null&&this.jobHistory.push(this.currentJob),this.currentJob=new w(this._printerData,this.id,this._printerData.model),this.emit("job:start",this.currentJob);else if(m==="RUNNING"&&(i==="FINISH"||i==="FAILED"||i==="IDLE"))i==="FINISH"?this.currentJob!==null&&this.emit("job:finish",this.currentJob,"SUCCESS"):i==="FAILED"?this.currentJob!==null&&this.emit("job:finish",this.currentJob,"FAILED"):i==="IDLE"&&this.currentJob!==null&&this.emit("job:finish",this.currentJob,"UNEXPECTED"),this.currentJob!==null&&this.jobHistory.push(this.currentJob),this.currentJob=null;else if(m==="RUNNING"&&i==="PAUSE")this.currentJob&&this.emit("job:pause",this.currentJob,!1);else if(m==="PAUSE"&&i==="RUNNING")this.currentJob&&this.emit("job:unpause",this.currentJob);else if(m==="OFFLINE"&&i==="PAUSE")this.emit("job:offlineRecovery",this.currentJob);else if(!(m==="OFFLINE"&&i==="RUNNING")){if(!((m==="IDLE"||m==="OFFLINE")&&(i==="FINISH"||i==="FAILED"||i==="IDLE"))){if(!((m==="FINISH"||i==="FAILED")&&i==="IDLE")){if(m!==i)throw new Error("Edge case detected while updating printer status!")}}}if(this._printerStatus=a.print.gcode_state,this.currentJob){let s=this.currentJob?.data;this.currentJob.update(a.print),this.emit("job:update",this.currentJob,O(s,this.currentJob.data))}}if(g(a)&&this.emit("message",l,o,a.print),z(a)&&(this.emit("message",l,o,a.info),x(a.info))){let m=[...new Set([...this._printerData.modules,...a.info.module])];this._printerData.modules=m,this.emit("printer:dataUpdate",this._printerData,{modules:m});let i=this._printerData.modules.find(s=>s.name==="ota");if(!i)throw new Error("OTA module version data not found, unable to determine printer model!");if(i.sn.startsWith("00M"))this._printerData.model=p.X1C;else if(i.sn.startsWith("00W"))this._printerData.model=p.X1;else if(i.sn.startsWith("03W"))this._printerData.model=p.X1E;else if(i.sn.startsWith("01S"))this._printerData.model=p.P1P;else if(i.sn.startsWith("01P"))this._printerData.model=p.P1S;else if(i.sn.startsWith("030"))this._printerData.model=p.A1;else if(i.sn.startsWith("039"))this._printerData.model=p.A1M;else throw new Error("Printer model not supported!");this.emit("printer:dataUpdate",this._printerData,{model:this._printerData.model})}}}publish(t){return new Promise((l,a)=>{if(!this.mqttClient)return a("Client not connected.");let o=typeof t=="string"?t:JSON.stringify(t),m=`device/${this.config.serialNumber}/request`;this.mqttClient.publish(m,o,i=>{if(i)return a(`Error publishing to topic '${m}': ${i.message}`);l()})})}get data(){return this._printerData}get status(){return this._printerStatus}};r(D,"BambuClient");var de=D;export{f as AbstractCommand,V as AmsRFIDStatus,de as BambuClient,le as Fan,Q as FilamentType,d as GCodeCommand,ie as GCodeFileCommand,ne as GCodeLineCommand,b as GetVersionCommand,w as Job,W as PrintStage,p as PrinterModel,c as PushAllCommand,X as SpeedLevel,me as UpdateFanCommand,oe as UpdateLightCommand,pe as UpdateSpeedCommand,se as UpdateStateCommand,ue as UpdateTempCommand,K as UpgradeDisplayState,Y as UpgradeErrorCode,Z as UpgradeStatusFinishState,$ as UpgradeStatusProgressState,gt as getCleanPushInfoCommand,Je as getFilamentConfig,j as isGCodeLineCommand,q as isGcodeFileCommand,x as isGetVersionCommand,z as isInfoMessage,g as isMCPrintMessage,y as isPrintMessage,Oe as isProjectFileCommand,h as isPushAllCommand,ht as isPushInfoCommand,J as isPushStatusCommand,ee as isUpdateFanCommand,te as isUpdateLightCommand,_e as isUpdateSpeedCommand,re as isUpdateStateCommand,ae as isUpdateTempCommand};
|
|
16
|
+
+{endif}`],filament_id:"GFL01",filament_flow_ratio:["0.98"],filament_vendor:["Polymaker"]}};var W;(function(e){e[e.PRINTING=0]="PRINTING",e[e.BED_LEVELING=1]="BED_LEVELING",e[e.HEATBED_PREHEATING=2]="HEATBED_PREHEATING",e[e.XY_MECH_MODE_SWEEP=3]="XY_MECH_MODE_SWEEP",e[e.CHANGE_MATERIAL=4]="CHANGE_MATERIAL",e[e.M400_PAUSE=5]="M400_PAUSE",e[e.FILAMENT_RUNOUT_PAUSE=6]="FILAMENT_RUNOUT_PAUSE",e[e.HOTEND_HEATING=7]="HOTEND_HEATING",e[e.EXTRUDE_COMPENSATION_SCAN=8]="EXTRUDE_COMPENSATION_SCAN",e[e.BED_SCAN=9]="BED_SCAN",e[e.FIRST_LAYER_SCAN=10]="FIRST_LAYER_SCAN",e[e.BE_SURFACE_TYPT_IDENTIFICATION=11]="BE_SURFACE_TYPT_IDENTIFICATION",e[e.SCANNER_EXTRINSIC_PARA_CALI=12]="SCANNER_EXTRINSIC_PARA_CALI",e[e.TOOLHEAD_HOMING=13]="TOOLHEAD_HOMING",e[e.NOZZLE_TIP_CLEANING=14]="NOZZLE_TIP_CLEANING",e[e.EXTRUDER_TEMP_PROTECT_CALI=15]="EXTRUDER_TEMP_PROTECT_CALI",e[e.USER_PAUSE=16]="USER_PAUSE",e[e.TOOLHEAD_SHELL_OFF_PAUSE=17]="TOOLHEAD_SHELL_OFF_PAUSE",e[e.SCANNER_LASER_PARA_CALI=18]="SCANNER_LASER_PARA_CALI",e[e.EXTRUDER_ABSOLUTE_FLOW_CALI=19]="EXTRUDER_ABSOLUTE_FLOW_CALI",e[e.HOTEND_TEMPERATURE_ERROR_PAUSE=20]="HOTEND_TEMPERATURE_ERROR_PAUSE",e[e.HEATED_BED_TEMPERATURE_ERROR_PAUSE=21]="HEATED_BED_TEMPERATURE_ERROR_PAUSE",e[e.FILAMENT_UNLOADING=22]="FILAMENT_UNLOADING",e[e.SKIP_STEP_PAUSE=23]="SKIP_STEP_PAUSE",e[e.FILAMENT_LOADING=24]="FILAMENT_LOADING",e[e.MOTOR_NOISE_CALIBRATION=25]="MOTOR_NOISE_CALIBRATION",e[e.AMS_LOST_PAUSE=26]="AMS_LOST_PAUSE",e[e.HEAT_BREAK_FAN_PAUSE=27]="HEAT_BREAK_FAN_PAUSE",e[e.CHAMBER_TEMPERATURE_CONTROL_ERROR_PAUSE=28]="CHAMBER_TEMPERATURE_CONTROL_ERROR_PAUSE",e[e.CHAMBER_COOLING=29]="CHAMBER_COOLING",e[e.USER_INSERT_GCODE_PAUSE=30]="USER_INSERT_GCODE_PAUSE",e[e.MOTOR_NOISE_SHOWOFF=31]="MOTOR_NOISE_SHOWOFF",e[e.NOZZLE_FILAMENT_COVERED_DETECTED_PAUSE=32]="NOZZLE_FILAMENT_COVERED_DETECTED_PAUSE",e[e.CUTTER_ERROR_PAUSE=33]="CUTTER_ERROR_PAUSE",e[e.FIRST_LAYER_ERROR_PAUSE=34]="FIRST_LAYER_ERROR_PAUSE",e[e.NOZZLE_CLOG_PAUSE=35]="NOZZLE_CLOG_PAUSE"})(W||(W={}));var X;(function(e){e.SILENT="1",e.NORMAL="2",e.SPORT="3",e.LUDICROUS="4"})(X||(X={}));var V;(function(e){e[e.IDLE=0]="IDLE",e[e.READING=1]="READING",e[e.GCODE_TRANS=2]="GCODE_TRANS",e[e.GCODE_RUNNING=3]="GCODE_RUNNING",e[e.ASSISTANT=4]="ASSISTANT",e[e.SWITCH_FILAMENT=5]="SWITCH_FILAMENT",e[e.HAS_FILAMENT=6]="HAS_FILAMENT"})(V||(V={}));var $;(function(e){e.DOWNLOADING="DOWNLOADING",e.UPGRADE_REQUEST="UPGRADE_REQUEST",e.PRE_FLASH_START="PRE_FLASH_START",e.PRE_FLASH_SUCCESS="PRE_FLASH_SUCCESS"})($||($={}));var Z;(function(e){e.UPGRADE_SUCCESS="UPGRADE_SUCCESS",e.DOWNLOAD_FAIL="DOWNLOAD_FAIL",e.FLASH_FAIL="FLASH_FAIL",e.PRE_FLASH_FAIL="PRE_FLASH_FAIL",e.UPGRADE_FAIL="UPGRADE_FAIL"})(Z||(Z={}));var Y;(function(e){e[e.UpgradeNoError=0]="UpgradeNoError",e[e.UpgradeDownloadFailed=1]="UpgradeDownloadFailed",e[e.UpgradeVerifyFailed=2]="UpgradeVerifyFailed",e[e.UpgradeFlashFailed=3]="UpgradeFlashFailed",e[e.UpgradePrinting=4]="UpgradePrinting"})(Y||(Y={}));var K;(function(e){e[e.UNAVAILABLE=0]="UNAVAILABLE",e[e.IDLE=1]="IDLE",e[e.UPGRADING=2]="UPGRADING",e[e.UPGRADE_FINISHED=3]="UPGRADE_FINISHED"})(K||(K={}));var Q;(function(e){e.PLA="PLA",e.ABS="ABS",e.TPU="TPU",e.PC="PC",e.ASA="ASA",e.PA_CF="PA-CF",e.PA6_CF="PA6-CF",e.PET_CF="PET-CF",e.PETG="PETG",e.PETG_CF="PETG-CF",e.PLA_AERO="PLA-AERO",e.PLA_CF="PLA-CF",e.PPA_CF="PPA-CF",e.PPA_GF="PPA-GF",e.PA="PA",e.HIPS="HIPS",e.PPS="PPS",e.PPS_CF="PPS-CF",e.PVA="PVA",e.PLA_S="PLA-S",e.PA_S="PLA-S"})(Q||(Q={}));function Je(e){return k[e]??null}r(Je,"getFilamentConfig");function h(e){return e.command==="push_status"&&Object.keys(e).length>=40}r(h,"isPushAllCommand");function ee(e){return e.command==="gcode_line"&&!!e?.param?.startsWith("M106")}r(ee,"isUpdateFanCommand");function te(e){return e.command==="push_status"&&e.hasOwnProperty("lights_report")}r(te,"isUpdateLightCommand");function _e(e){return e.command==="push_status"&&(e.hasOwnProperty("spd_lvl")||e.hasOwnProperty("spd_mag"))}r(_e,"isUpdateSpeedCommand");function re(e){return e.command===e.command}r(re,"isUpdateStateCommand");function ae(e){return e.command==="gcode_line"&&!!(e?.param?.startsWith("M104")||e?.param?.startsWith("M140"))}r(ae,"isUpdateTempCommand");function g(e){return!!e?.mc_print&&!!e?.mc_print?.command&&["push_info"].includes(e.mc_print.command)}r(g,"isMCPrintMessage");function ht(e){return e.command==="push_info"}r(ht,"isPushInfoCommand");function gt(e){let[,_,t,l]=e.param.match(/^\[(.+?)](?:\[(.+?)])?\s*(.+)$/)??[];return{sequenceId:Number(e.sequence_id),category:_,subcategory:t,content:l,rawParam:e.param}}r(gt,"getCleanPushInfoCommand");function x(e){return e.command==="get_version"}r(x,"isGetVersionCommand");function z(e){return!!e?.info&&!!e?.info?.command&&["get_version"].includes(e.info.command)}r(z,"isInfoMessage");var E=class E extends f{constructor({fileName:t}){super({param:t});n(this,"category","print");n(this,"command","gcode_file");n(this,"ownsResponse",q)}};r(E,"GCodeFileCommand");var ie=E;var S=class S extends d{constructor({gcodes:t}){super(t);n(this,"command","gcode_line");n(this,"ownsResponse",j)}};r(S,"GCodeLineCommand");var ne=S;var G=class G extends f{constructor(){super();n(this,"category","info");n(this,"command","get_version");n(this,"ownsResponse",x)}};r(G,"GetVersionCommand");var b=G;var M=class M extends f{constructor(){super();n(this,"category","pushing");n(this,"command","pushall");n(this,"ownsResponse",h)}};r(M,"PushAllCommand");var c=M;var le;(function(e){e[e.PART_COOLING_FAN=1]="PART_COOLING_FAN",e[e.AUXILIARY_FAN=2]="AUXILIARY_FAN",e[e.CHAMBER_FAN=3]="CHAMBER_FAN"})(le||(le={}));var L=class L extends d{constructor({fan:t,speed:l}){super([`M106 P${t.toString()} S${255*l/100}`]);n(this,"command","gcode_line");n(this,"ownsResponse",ee)}};r(L,"UpdateFanCommand");var me=L;var F=class F extends f{constructor({light:t,mode:l,loopOptions:a={led_on_time:0,led_off_time:0,loop_times:0,interval_time:0}}){super({led_node:t,led_mode:l,...a});n(this,"category","system");n(this,"command","ledctrl");n(this,"ownsResponse",te)}};r(F,"UpdateLightCommand");var oe=F;var N=class N extends f{constructor({speed:t}){super({param:t.toString()});n(this,"category","print");n(this,"command","print_speed");n(this,"ownsResponse",_e)}};r(N,"UpdateSpeedCommand");var pe=N;var C=class C extends f{constructor({state:t}){super();n(this,"category","print");n(this,"command");n(this,"ownsResponse",re);this.command=t}};r(C,"UpdateStateCommand");var se=C;var fe;(function(e){e.BED="M140",e.EXTRUDER="M104"})(fe||(fe={}));var T=class T extends d{constructor({part:t,temperature:l}){super([`${t} S${l}`]);n(this,"command","gcode_line");n(this,"ownsResponse",ae)}};r(T,"UpdateTempCommand");var ue=T;var p;(function(e){e.X1C="X1C",e.X1="X1",e.X1E="X1E",e.P1P="P1P",e.P1S="P1S",e.P2S="P2S",e.A1="A1",e.A1M="A1M"})(p||(p={}));import{createId as xe}from"@paralleldrive/cuid2";var ze=new RegExp(/\.[^/.]+$/),R=class R{constructor(_,t,l,a=new Date){n(this,"_jobData");let o=[p.P1P,p.P1S].includes(l);this._jobData={id:xe(),printerId:t,printerModel:l,name:_.subtask_name.replace(ze,""),fileName:_.subtask_name,gcodeName:_.gcode_file,jobOrigin:_.print_type,totalLayerNumber:_.total_layer_num,totalPrintTime:_.mc_remaining_time,status:_.gcode_state,stage:_.stg_cur,percentDone:_.mc_percent,layerNumber:_.layer_num,remainingTime:_.mc_remaining_time,skippedObjects:_.s_obj,statusHistory:[{status:_.gcode_state,changedAt:a}],speedLevelHistory:[{speedLevel:_.spd_lvl,speedMagnitude:_.spd_mag,changedAt:a}],printingStageHistory:[{stage:_.stg_cur,changedAt:a}],temperatureHistory:[{bedTemp:_.bed_temper,bedTargetTemp:_.bed_target_temper,interiorTemp:o?_.frame_temper:_.chamber_temper,nozzleTemp:_.nozzle_temper,nozzleTargetTemp:_.nozzle_target_temper,changedAt:a}],tempLastUpdatedAt:new Date,errorCodes:_.hms.map(m=>({attr:m.attr,code:m.code,thrownAt:a,layerNumber:_.layer_num})),createdAt:a}}update(_){if(!this._jobData)return;let t=this._jobData,l=new Date,a=[p.P1P,p.P1S].includes(this._jobData.printerModel);_.gcode_state&&t.statusHistory.at(-1)?.status!==_.gcode_state&&t.statusHistory.push({status:_.gcode_state,changedAt:l}),_.spd_lvl&&_.spd_mag&&t.speedLevelHistory.at(-1)?.speedLevel!==_.spd_lvl&&t.speedLevelHistory.push({speedLevel:_.spd_lvl,speedMagnitude:_.spd_mag,changedAt:l}),_.stg_cur&&t.printingStageHistory.at(-1)?.stage!==_.stg_cur&&t.printingStageHistory.push({stage:_.stg_cur,changedAt:l});let o=!!(_.bed_temper||_.bed_target_temper||(a?_.frame_temper:_.chamber_temper)||_.nozzle_temper||_.nozzle_target_temper),m=t.tempLastUpdatedAt.getTime()+5e3<l.getTime();if(o&&m){let s=a?_.frame_temper:_.chamber_temper,u=t.temperatureHistory.at(-1);t.temperatureHistory.push({bedTemp:_.bed_temper??u.bedTemp,bedTargetTemp:_.bed_target_temper??u.bedTargetTemp,interiorTemp:s??u.interiorTemp,nozzleTemp:_.nozzle_temper??u.nozzleTemp,nozzleTargetTemp:_.nozzle_target_temper??u.nozzleTargetTemp,changedAt:l}),t.tempLastUpdatedAt=l}let i={status:_.gcode_state??t.status,stage:_.stg_cur??t.stage,percentDone:_.mc_percent??t.percentDone,layerNumber:_.layer_num??t.layerNumber,remainingTime:_.mc_remaining_time??t.remainingTime,skippedObjects:_.s_obj??t.skippedObjects,errorCodes:_.hms?.map((s,u)=>({attr:s.attr??t.errorCodes[u].attr,code:s.code??t.errorCodes[u].code,thrownAt:l??t.errorCodes[u].thrownAt,layerNumber:_.layer_num??t.errorCodes[u].layerNumber}))??t.errorCodes};this._jobData={...this._jobData,...i}}get data(){return this._jobData}};r(R,"Job");var w=R;import*as ce from"mqtt";import*as ye from"eventemitter3";import{createId as be}from"@paralleldrive/cuid2";function I(e){return new Promise(_=>setTimeout(_,e))}r(I,"sleep");function O(e,_){let t={},l=r((a,o,m)=>{for(let i in o){let s=o[i],u=a[i];if(!(i in a&&JSON.stringify(a[i])===JSON.stringify(o[i]))){if(typeof s=="object"&&s!==null&&u!==null&&!Array.isArray(s)){m[i]=m[i]||{},l(a[i],o[i],m[i]);continue}m[i]=o[i]}}},"checkDifferences");return l(e,_,t),t}r(O,"subtractObject");var D=class D extends ye.EventEmitter{constructor(t){super();n(this,"clientOptions");n(this,"id");n(this,"mqttClient");n(this,"config");n(this,"_printerData");n(this,"_printerStatus");n(this,"jobHistory");n(this,"currentJob");this.clientOptions=t,this.id=be(),this.mqttClient=void 0,this._printerData={modules:[],model:void 0},this._printerStatus="OFFLINE",this.jobHistory=[],this.currentJob=null,this.config={host:t.host,port:t.port??8883,accessToken:t.accessToken,serialNumber:t.serialNumber,throwOnOfflineCommands:t.throwOnOfflineCommands??!1,reconnectInterval:t.reconnectInterval??1e3,connectTimeout:t.connectTimeout??2e3,keepAlive:t.keepAlive??20},this.jobHistory=t.jobHistory??[],this.currentJob=t.currentJob??null}emit(t,...l){return super.emit(t,...l)}off(t,l){return super.off(t,l),this}once(t,l){return super.once(t,l),this}on(t,l){return super.on(t,l),this}async connectMQTT(){return new Promise((t,l)=>{if(this.mqttClient)throw new Error("Can't establish a new connection while running another one!");this.mqttClient=ce.connect(`mqtts://${this.config.host}:${this.config.port}`,{username:"bblp",password:this.config.accessToken,reconnectPeriod:this.clientOptions.reconnectInterval,connectTimeout:this.clientOptions.connectTimeout,keepalive:this.clientOptions.keepAlive,resubscribe:!0,rejectUnauthorized:!1}),this.mqttClient.on("connect",async(...a)=>{await this.onConnect(...a),this.emit("client:connect"),t()}),this.mqttClient.on("disconnect",()=>{this.emit("client:disconnect",!1),this.emit("printer:statusUpdate",this._printerStatus,"OFFLINE"),this._printerStatus="OFFLINE",this.currentJob&&this.emit("job:pause",this.currentJob,!0)}),this.mqttClient.on("offline",()=>{this.emit("client:disconnect",!0),this.emit("printer:statusUpdate",this._printerStatus,"OFFLINE"),this._printerStatus="OFFLINE",this.currentJob&&this.emit("job:pause",this.currentJob,!0)}),this.mqttClient.on("message",(a,o)=>this.emit("rawMessage",a,o)),this.mqttClient.on("error",a=>{this.emit("client:error",a),l(a)})})}async connect(){return await Promise.all([this.connectMQTT()])}async disconnect(t=!1,l){return new Promise(a=>this.mqttClient?.end(t,l,a))}subscribe(t){if(this._printerStatus!=="OFFLINE"&&this.config.throwOnOfflineCommands)throw new Error(`Unable to subscribe to topic "${t}" while disconnected from printer!`);return new Promise((l,a)=>{if(!this.mqttClient)return a("Client not connected.");this.mqttClient.subscribe(t,m=>{if(m)return a(`Error subscribing to topic '${t}': ${m.message}`)});let o=r(async(m,i)=>{m===t&&await this.onMessage(i.toString(),t)},"listener");this.mqttClient.on("message",o),l()})}async executeCommand(t){if(this._printerStatus!=="OFFLINE"&&this.config.throwOnOfflineCommands)throw new Error("Unable to send commands while disconnected from printer!");return new Promise((l,a)=>{t.invoke(this).catch(m=>a(m));let o=setTimeout(()=>{a(new Error("Command execution timed out after 5 seconds."))},5e3);this.on("rawMessage",(m,i)=>{let s=JSON.parse(i.toString()),u=Object.keys(s)[0];if(!(z(s)||g(s)||y(s)))return;let P=s[u];P?.command&&t.ownsResponse(P)&&(clearTimeout(o),l(P))})})}async onConnect(t){await this.subscribe(`device/${this.config.serialNumber}/report`),await I(1e3),await this.executeCommand(new b),(this._printerData.model===p.P1P||this._printerData.model===p.P1S||this._printerData.model===p.P2S||this._printerData.model===p.A1||this._printerData.model===p.A1M)&&(await this.executeCommand(new c),setInterval(async()=>{await this.executeCommand(new c)},5*60*1e3))}async onMessage(t,l){let a=JSON.parse(t),o=Object.keys(a)[0];if(z(a)||g(a)||y(a)){if(y(a)&&(this.emit("message",l,o,a.print),(h(a.print)||J(a.print))&&(this._printerData={...this._printerData,...a.print},this.emit("printer:dataUpdate",this._printerData,a.print),a.print.gcode_state&&a.print.gcode_state!==this._printerStatus))){let m=this._printerStatus,i=a.print.gcode_state;if(this.emit("printer:statusUpdate",m,i),(i==="PREPARE"||i==="SLICING")&&(i="IDLE"),(m==="PREPARE"||m==="SLICING")&&(m="RUNNING"),(m==="IDLE"||m==="FINISH"||m==="FAILED")&&i==="RUNNING")this.currentJob!==null&&this.jobHistory.push(this.currentJob),this.currentJob=new w(this._printerData,this.id,this._printerData.model),this.emit("job:start",this.currentJob);else if(m==="RUNNING"&&(i==="FINISH"||i==="FAILED"||i==="IDLE"))i==="FINISH"?this.currentJob!==null&&this.emit("job:finish",this.currentJob,"SUCCESS"):i==="FAILED"?this.currentJob!==null&&this.emit("job:finish",this.currentJob,"FAILED"):i==="IDLE"&&this.currentJob!==null&&this.emit("job:finish",this.currentJob,"UNEXPECTED"),this.currentJob!==null&&this.jobHistory.push(this.currentJob),this.currentJob=null;else if(m==="RUNNING"&&i==="PAUSE")this.currentJob&&this.emit("job:pause",this.currentJob,!1);else if(m==="PAUSE"&&i==="RUNNING")this.currentJob&&this.emit("job:unpause",this.currentJob);else if(m==="OFFLINE"&&i==="PAUSE")this.emit("job:offlineRecovery",this.currentJob);else if(!(m==="OFFLINE"&&i==="RUNNING")){if(!((m==="IDLE"||m==="OFFLINE")&&(i==="FINISH"||i==="FAILED"||i==="IDLE"))){if(!((m==="FINISH"||i==="FAILED")&&i==="IDLE")){if(m!==i)throw new Error("Edge case detected while updating printer status!")}}}if(this._printerStatus=a.print.gcode_state,this.currentJob){let s=this.currentJob?.data;this.currentJob.update(a.print),this.emit("job:update",this.currentJob,O(s,this.currentJob.data))}}if(g(a)&&this.emit("message",l,o,a.print),z(a)&&(this.emit("message",l,o,a.info),x(a.info))){let m=[...new Set([...this._printerData.modules,...a.info.module])];this._printerData.modules=m,this.emit("printer:dataUpdate",this._printerData,{modules:m});let i=this._printerData.modules.find(s=>s.name==="ota");if(!i)throw new Error("OTA module version data not found, unable to determine printer model!");if(i.sn.startsWith("00M"))this._printerData.model=p.X1C;else if(i.sn.startsWith("00W"))this._printerData.model=p.X1;else if(i.sn.startsWith("03W"))this._printerData.model=p.X1E;else if(i.sn.startsWith("01S"))this._printerData.model=p.P1P;else if(i.sn.startsWith("01P"))this._printerData.model=p.P1S;else if(i.sn.startsWith("22E"))this._printerData.model=p.P2S;else if(i.sn.startsWith("030"))this._printerData.model=p.A1;else if(i.sn.startsWith("039"))this._printerData.model=p.A1M;else throw new Error("Printer model not supported!");this.emit("printer:dataUpdate",this._printerData,{model:this._printerData.model})}}}publish(t){return new Promise((l,a)=>{if(!this.mqttClient)return a("Client not connected.");let o=typeof t=="string"?t:JSON.stringify(t),m=`device/${this.config.serialNumber}/request`;this.mqttClient.publish(m,o,i=>{if(i)return a(`Error publishing to topic '${m}': ${i.message}`);l()})})}get data(){return this._printerData}get status(){return this._printerStatus}};r(D,"BambuClient");var de=D;export{f as AbstractCommand,V as AmsRFIDStatus,de as BambuClient,le as Fan,Q as FilamentType,d as GCodeCommand,ie as GCodeFileCommand,ne as GCodeLineCommand,b as GetVersionCommand,w as Job,W as PrintStage,p as PrinterModel,c as PushAllCommand,X as SpeedLevel,me as UpdateFanCommand,oe as UpdateLightCommand,pe as UpdateSpeedCommand,se as UpdateStateCommand,ue as UpdateTempCommand,K as UpgradeDisplayState,Y as UpgradeErrorCode,Z as UpgradeStatusFinishState,$ as UpgradeStatusProgressState,gt as getCleanPushInfoCommand,Je as getFilamentConfig,j as isGCodeLineCommand,q as isGcodeFileCommand,x as isGetVersionCommand,z as isInfoMessage,g as isMCPrintMessage,y as isPrintMessage,Oe as isProjectFileCommand,h as isPushAllCommand,ht as isPushInfoCommand,J as isPushStatusCommand,ee as isUpdateFanCommand,te as isUpdateLightCommand,_e as isUpdateSpeedCommand,re as isUpdateStateCommand,ae as isUpdateTempCommand};
|