@vextrix3d/machine-agent 0.0.0-stage → 2.1.0

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.
@@ -0,0 +1,289 @@
1
+ import { KlippyState } from '../types/index.js';
2
+ import { findMoonrakerChamberObject, MOONRAKER_SUBSCRIBED_OBJECTS, mergeMoonrakerStatus, } from '../utils.js';
3
+ const METHOD_NOT_FOUND = -32601;
4
+ class MoonrakerRequestError extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(`Moonraker error ${code}: ${message}`);
8
+ this.code = code;
9
+ }
10
+ }
11
+ const RECONNECT_DELAYS_MS = [2_000, 5_000, 15_000, 30_000, 60_000];
12
+ export class MoonrakerClient {
13
+ options;
14
+ socket = null;
15
+ requestId = 0;
16
+ pendingRequests = new Map();
17
+ status = {};
18
+ isSubscribed = false;
19
+ subscribing = null;
20
+ isStopped = false;
21
+ hasListedWebcams = false;
22
+ reconnectAttempt = 0;
23
+ reconnectTimer = null;
24
+ heartbeatTimer = null;
25
+ baseUrl;
26
+ constructor(options) {
27
+ this.options = options;
28
+ this.baseUrl = `${options.ip}:${options.port}`;
29
+ void this.connect();
30
+ }
31
+ async stop() {
32
+ this.isStopped = true;
33
+ if (this.reconnectTimer) {
34
+ clearTimeout(this.reconnectTimer);
35
+ }
36
+ this.stopHeartbeat();
37
+ this.socket?.close();
38
+ }
39
+ async getFileEstimatedTime(filename) {
40
+ const metadata = await this.request('server.files.metadata', {
41
+ filename,
42
+ });
43
+ return typeof metadata.estimated_time === 'number' && metadata.estimated_time > 0
44
+ ? metadata.estimated_time
45
+ : null;
46
+ }
47
+ async connect() {
48
+ let url;
49
+ try {
50
+ url = await this.resolveSocketUrl();
51
+ }
52
+ catch (error) {
53
+ this.handleError(error);
54
+ this.scheduleReconnect();
55
+ return;
56
+ }
57
+ const socket = new WebSocket(url);
58
+ this.socket = socket;
59
+ socket.addEventListener('open', () => {
60
+ this.reconnectAttempt = 0;
61
+ void this.initialise();
62
+ });
63
+ socket.addEventListener('message', event => {
64
+ this.handleMessage(event.data);
65
+ });
66
+ socket.addEventListener('error', () => {
67
+ this.handleError(new Error(`WebSocket error on ws://${this.baseUrl}/websocket`));
68
+ });
69
+ socket.addEventListener('close', () => {
70
+ this.handleClose(socket);
71
+ });
72
+ }
73
+ async resolveSocketUrl() {
74
+ const url = `ws://${this.baseUrl}/websocket`;
75
+ if (!this.options.apiKey) {
76
+ return url;
77
+ }
78
+ const response = await fetch(`http://${this.baseUrl}/access/oneshot_token`, {
79
+ headers: {
80
+ 'X-Api-Key': this.options.apiKey,
81
+ },
82
+ signal: AbortSignal.timeout(this.options.requestTimeoutMs ?? 15_000),
83
+ });
84
+ if (!response.ok) {
85
+ throw new Error(`Moonraker refused the API key: ${response.status} ${response.statusText}`);
86
+ }
87
+ const { result } = (await response.json());
88
+ return `${url}?token=${encodeURIComponent(result)}`;
89
+ }
90
+ async initialise() {
91
+ try {
92
+ await this.request('server.connection.identify', {
93
+ client_name: 'vextrix3d-machine-agent',
94
+ version: '1.0.0',
95
+ type: 'other',
96
+ url: 'https://vextrix3d.co.uk',
97
+ });
98
+ this.startHeartbeat();
99
+ void this.listWebcams();
100
+ await this.subscribeOnceKlippyAnswers();
101
+ }
102
+ catch (error) {
103
+ this.handleError(error);
104
+ }
105
+ }
106
+ async listWebcams() {
107
+ let webcams;
108
+ try {
109
+ const result = await this.request('server.webcams.list');
110
+ webcams = Array.isArray(result.webcams) ? result.webcams : [];
111
+ }
112
+ catch (error) {
113
+ if (!(error instanceof MoonrakerRequestError && error.code === METHOD_NOT_FOUND)) {
114
+ this.handleError(error);
115
+ return;
116
+ }
117
+ webcams = null;
118
+ }
119
+ this.passWebcams(webcams);
120
+ }
121
+ passWebcams(webcams) {
122
+ this.hasListedWebcams = true;
123
+ this.options.onWebcams?.(webcams);
124
+ }
125
+ async subscribeOnceKlippyAnswers() {
126
+ const { klippy_state: klippyState } = await this.request('server.info');
127
+ const isAnswering = klippyState === KlippyState.READY ||
128
+ klippyState === KlippyState.SHUTDOWN ||
129
+ klippyState === KlippyState.ERROR;
130
+ if (isAnswering) {
131
+ await this.subscribe();
132
+ }
133
+ }
134
+ subscribe() {
135
+ this.subscribing ??= this.requestSubscription().finally(() => {
136
+ this.subscribing = null;
137
+ });
138
+ return this.subscribing;
139
+ }
140
+ async requestSubscription() {
141
+ const { objects } = await this.request('printer.objects.list');
142
+ const chamberObject = findMoonrakerChamberObject(objects);
143
+ const { status } = await this.request('printer.objects.subscribe', {
144
+ objects: {
145
+ ...MOONRAKER_SUBSCRIBED_OBJECTS,
146
+ ...(chamberObject
147
+ ? {
148
+ [chamberObject]: ['temperature'],
149
+ }
150
+ : {}),
151
+ },
152
+ });
153
+ this.status = status;
154
+ this.isSubscribed = true;
155
+ this.options.onConnect?.();
156
+ this.options.onStatus?.(this.status);
157
+ }
158
+ startHeartbeat() {
159
+ this.stopHeartbeat();
160
+ this.heartbeatTimer = setInterval(() => {
161
+ if (!this.hasListedWebcams) {
162
+ void this.listWebcams();
163
+ }
164
+ if (this.isSubscribed) {
165
+ this.options.onStatus?.(this.status);
166
+ return;
167
+ }
168
+ void this.subscribeOnceKlippyAnswers().catch(error => this.handleError(error));
169
+ }, this.options.heartbeatMs ?? 30_000);
170
+ }
171
+ stopHeartbeat() {
172
+ if (this.heartbeatTimer) {
173
+ clearInterval(this.heartbeatTimer);
174
+ this.heartbeatTimer = null;
175
+ }
176
+ }
177
+ request(method, params) {
178
+ const socket = this.socket;
179
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
180
+ return Promise.reject(new Error(`Moonraker is not connected for ${method}`));
181
+ }
182
+ const id = ++this.requestId;
183
+ return new Promise((resolve, reject) => {
184
+ const timeout = setTimeout(() => {
185
+ this.pendingRequests.delete(id);
186
+ reject(new Error(`Moonraker did not answer ${method}`));
187
+ }, this.options.requestTimeoutMs ?? 15_000);
188
+ this.pendingRequests.set(id, {
189
+ resolve: result => resolve(result),
190
+ reject,
191
+ timeout,
192
+ });
193
+ socket.send(JSON.stringify({
194
+ jsonrpc: '2.0',
195
+ method,
196
+ ...(params
197
+ ? {
198
+ params,
199
+ }
200
+ : {}),
201
+ id,
202
+ }));
203
+ });
204
+ }
205
+ handleMessage(data) {
206
+ let message;
207
+ try {
208
+ message = JSON.parse(String(data));
209
+ }
210
+ catch (error) {
211
+ this.handleError(new Error('Failed to parse a Moonraker message', {
212
+ cause: error,
213
+ }));
214
+ return;
215
+ }
216
+ if (message.id !== undefined) {
217
+ this.settleRequest(message);
218
+ return;
219
+ }
220
+ switch (message.method) {
221
+ case 'notify_status_update': {
222
+ const update = message.params?.[0];
223
+ if (!this.isSubscribed || !update) {
224
+ return;
225
+ }
226
+ this.status = mergeMoonrakerStatus(this.status, update);
227
+ this.options.onStatus?.(this.status);
228
+ break;
229
+ }
230
+ case 'notify_webcams_changed': {
231
+ const { webcams } = (message.params?.[0] ?? {});
232
+ if (Array.isArray(webcams)) {
233
+ this.passWebcams(webcams);
234
+ }
235
+ break;
236
+ }
237
+ case 'notify_klippy_ready':
238
+ void this.subscribe().catch(error => this.handleError(error));
239
+ break;
240
+ case 'notify_klippy_disconnected':
241
+ this.isSubscribed = false;
242
+ break;
243
+ }
244
+ }
245
+ settleRequest(message) {
246
+ const pending = this.pendingRequests.get(message.id);
247
+ if (!pending) {
248
+ return;
249
+ }
250
+ clearTimeout(pending.timeout);
251
+ this.pendingRequests.delete(message.id);
252
+ if (message.error) {
253
+ pending.reject(new MoonrakerRequestError(message.error.code, message.error.message));
254
+ return;
255
+ }
256
+ pending.resolve(message.result);
257
+ }
258
+ handleClose(socket) {
259
+ if (socket !== this.socket) {
260
+ return;
261
+ }
262
+ this.socket = null;
263
+ this.isSubscribed = false;
264
+ this.hasListedWebcams = false;
265
+ this.stopHeartbeat();
266
+ this.pendingRequests.forEach(({ reject, timeout }) => {
267
+ clearTimeout(timeout);
268
+ reject(new Error('The connection to Moonraker closed'));
269
+ });
270
+ this.pendingRequests.clear();
271
+ this.options.onClose?.();
272
+ this.scheduleReconnect();
273
+ }
274
+ scheduleReconnect() {
275
+ if (this.isStopped) {
276
+ return;
277
+ }
278
+ const delay = RECONNECT_DELAYS_MS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS_MS.length - 1)];
279
+ this.reconnectAttempt++;
280
+ this.reconnectTimer = setTimeout(() => {
281
+ this.reconnectTimer = null;
282
+ this.options.onReconnect?.();
283
+ void this.connect();
284
+ }, delay);
285
+ }
286
+ handleError(error) {
287
+ this.options.onError?.(error);
288
+ }
289
+ }
@@ -0,0 +1,121 @@
1
+ import { ApiRequestError } from './ApiRequestError.js';
2
+ var PrintTechnology;
3
+ (function (PrintTechnology) {
4
+ PrintTechnology["FDM"] = "fdm";
5
+ PrintTechnology["SLA"] = "sla";
6
+ })(PrintTechnology || (PrintTechnology = {}));
7
+ var PrinterStatus;
8
+ (function (PrinterStatus) {
9
+ PrinterStatus["ONLINE"] = "online";
10
+ PrinterStatus["OFFLINE"] = "offline";
11
+ })(PrinterStatus || (PrinterStatus = {}));
12
+ export var PrinterConnectionType;
13
+ (function (PrinterConnectionType) {
14
+ PrinterConnectionType["BAMBU_MQTT"] = "bambu-mqtt";
15
+ PrinterConnectionType["MOONRAKER"] = "moonraker";
16
+ })(PrinterConnectionType || (PrinterConnectionType = {}));
17
+ export var MachineAgentStateType;
18
+ (function (MachineAgentStateType) {
19
+ MachineAgentStateType["IDLE"] = "idle";
20
+ MachineAgentStateType["RUNNING"] = "running";
21
+ MachineAgentStateType["PAUSED"] = "paused";
22
+ MachineAgentStateType["FAILED"] = "failed";
23
+ MachineAgentStateType["CANCELLED"] = "cancelled";
24
+ MachineAgentStateType["FINISHED"] = "finished";
25
+ MachineAgentStateType["PREPARING"] = "preparing";
26
+ })(MachineAgentStateType || (MachineAgentStateType = {}));
27
+ export var PrintAttemptFailureReason;
28
+ (function (PrintAttemptFailureReason) {
29
+ PrintAttemptFailureReason["PRINTER_ERROR"] = "printer-error";
30
+ PrintAttemptFailureReason["MATERIAL_ERROR"] = "material-error";
31
+ PrintAttemptFailureReason["MODEL_ERROR"] = "model-error";
32
+ PrintAttemptFailureReason["SUPPORT_REMOVAL_DAMAGE"] = "support-removal-damage";
33
+ PrintAttemptFailureReason["QUALITY_CHECK_FAILED"] = "quality-check-failed";
34
+ PrintAttemptFailureReason["UNKNOWN"] = "unknown";
35
+ })(PrintAttemptFailureReason || (PrintAttemptFailureReason = {}));
36
+ export class Vextrix3dApiClient {
37
+ functionsUrl;
38
+ apiKey;
39
+ agentVersion;
40
+ timeoutMs;
41
+ constructor({ functionsUrl, apiKey, agentVersion, timeoutMs = 15_000, }) {
42
+ this.functionsUrl = functionsUrl.replace(/\/+$/, '');
43
+ this.apiKey = apiKey;
44
+ this.agentVersion = agentVersion;
45
+ this.timeoutMs = timeoutMs;
46
+ }
47
+ getPrinterInfo() {
48
+ return this.request('/getMachineAgentInfo');
49
+ }
50
+ async getCamera() {
51
+ try {
52
+ return await this.request('/getMachineAgentCamera', {
53
+ method: 'POST',
54
+ });
55
+ }
56
+ catch (error) {
57
+ if (error instanceof ApiRequestError && error.status === 404) {
58
+ return {
59
+ tunnelToken: null,
60
+ relayKey: null,
61
+ };
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ reportPrinterState(state) {
67
+ return this.request('/reportMachineAgentState', {
68
+ method: 'POST',
69
+ body: { ...state, agentVersion: this.agentVersion },
70
+ });
71
+ }
72
+ getCurrentPrintAttempt() {
73
+ return this.request('/getMachineAgentPrintAttempt');
74
+ }
75
+ cancelCurrentPrintAttempt() {
76
+ return this.request('/cancelMachineAgentPrintAttempt', {
77
+ method: 'POST',
78
+ });
79
+ }
80
+ failCurrentPrintAttempt(reason) {
81
+ return this.request('/failMachineAgentPrintAttempt', {
82
+ method: 'POST',
83
+ body: {
84
+ reason,
85
+ },
86
+ });
87
+ }
88
+ async request(path, { method = 'GET', body } = {}) {
89
+ const response = await fetch(`${this.functionsUrl}${path}`, {
90
+ method,
91
+ headers: {
92
+ Accept: 'application/json',
93
+ Authorization: `Bearer ${this.apiKey}`,
94
+ ...(body === undefined
95
+ ? {}
96
+ : {
97
+ 'Content-Type': 'application/json',
98
+ }),
99
+ },
100
+ body: body === undefined ? undefined : JSON.stringify(body),
101
+ signal: AbortSignal.timeout(this.timeoutMs),
102
+ });
103
+ const responseText = await response.text();
104
+ if (!response.ok) {
105
+ throw new ApiRequestError(`API request failed: ${method} ${path} - ${response.status} ${response.statusText}${responseText ? ` - ${responseText}` : ''}`, response.status);
106
+ }
107
+ let result;
108
+ if (responseText) {
109
+ try {
110
+ result = JSON.parse(responseText);
111
+ }
112
+ catch {
113
+ throw new Error(`The API returned invalid JSON for ${method} ${path}`);
114
+ }
115
+ }
116
+ if (!result || result.status !== 'ok' || result.data === undefined) {
117
+ throw new Error(`The API returned an invalid response for ${method} ${path}`);
118
+ }
119
+ return result.data;
120
+ }
121
+ }
@@ -0,0 +1,21 @@
1
+ export const createMoonrakerCameraReporter = ({ stateReporter, cameraRelay, readWebcams, hasCameraStream, }) => {
2
+ let webcamReport;
3
+ let lastState = null;
4
+ const reportState = (state) => {
5
+ lastState = state;
6
+ stateReporter.report({
7
+ ...state,
8
+ hasCameraStream: hasCameraStream(webcamReport),
9
+ });
10
+ };
11
+ return {
12
+ reportState,
13
+ noteWebcams: webcams => {
14
+ webcamReport = readWebcams(webcams);
15
+ cameraRelay?.update(webcamReport);
16
+ if (lastState) {
17
+ reportState(lastState);
18
+ }
19
+ },
20
+ };
21
+ };
@@ -0,0 +1,76 @@
1
+ import { PrintAttemptFailureReason, MachineAgentStateType, } from './clients/Vextrix3dApiClient.js';
2
+ export const createPrintAttemptSynchronizer = ({ printerLabel, apiClient, printerGone, }) => {
3
+ let currentAttempt = null;
4
+ let previousState = null;
5
+ let requestPromise = null;
6
+ let loadPromise = null;
7
+ const refreshCurrentAttempt = () => {
8
+ if (printerGone.isGone()) {
9
+ return Promise.resolve();
10
+ }
11
+ loadPromise ??= apiClient
12
+ .getCurrentPrintAttempt()
13
+ .then(attempt => {
14
+ currentAttempt = attempt;
15
+ if (attempt) {
16
+ console.log(`Current print attempt loaded: ${attempt.id}`);
17
+ }
18
+ else {
19
+ console.log(`No active print attempt for "${printerLabel}"`);
20
+ }
21
+ })
22
+ .catch(error => {
23
+ if (!printerGone.notice(error)) {
24
+ console.error(`Failed to load the current print attempt for "${printerLabel}":`, error);
25
+ }
26
+ })
27
+ .finally(() => {
28
+ loadPromise = null;
29
+ });
30
+ return loadPromise;
31
+ };
32
+ const runOnce = (action) => {
33
+ if (requestPromise || printerGone.isGone()) {
34
+ return;
35
+ }
36
+ requestPromise = action()
37
+ .then(() => {
38
+ currentAttempt = null;
39
+ })
40
+ .catch(error => {
41
+ if (!printerGone.notice(error)) {
42
+ console.error(`Failed to update print attempt for "${printerLabel}":`, error);
43
+ }
44
+ })
45
+ .finally(() => {
46
+ requestPromise = null;
47
+ });
48
+ };
49
+ const handleState = (state) => {
50
+ const stateChanged = state !== previousState;
51
+ previousState = state;
52
+ if (!stateChanged) {
53
+ return;
54
+ }
55
+ if (!currentAttempt &&
56
+ (state === MachineAgentStateType.PREPARING || state === MachineAgentStateType.RUNNING)) {
57
+ void refreshCurrentAttempt();
58
+ return;
59
+ }
60
+ if (!currentAttempt) {
61
+ return;
62
+ }
63
+ switch (state) {
64
+ case MachineAgentStateType.CANCELLED:
65
+ runOnce(() => apiClient.cancelCurrentPrintAttempt());
66
+ break;
67
+ case MachineAgentStateType.FAILED:
68
+ runOnce(() => apiClient.failCurrentPrintAttempt(PrintAttemptFailureReason.PRINTER_ERROR));
69
+ break;
70
+ }
71
+ };
72
+ return {
73
+ initialise: refreshCurrentAttempt,
74
+ handleState,
75
+ };
76
+ };
@@ -0,0 +1,26 @@
1
+ import { ApiRequestError } from './clients/ApiRequestError.js';
2
+ export const isPrinterGoneError = (error) => error instanceof ApiRequestError && (error.status === 401 || error.status === 410);
3
+ const defaultKeepAlive = () => {
4
+ setInterval(() => { }, 2 ** 30);
5
+ };
6
+ export const createPrinterGone = ({ stop, keepAlive = defaultKeepAlive, }) => {
7
+ let isGone = false;
8
+ return {
9
+ isGone: () => isGone,
10
+ notice: error => {
11
+ if (!isPrinterGoneError(error)) {
12
+ return false;
13
+ }
14
+ if (isGone) {
15
+ return true;
16
+ }
17
+ isGone = true;
18
+ console.error(`vextrix3d no longer knows ${error.status === 410 ? 'this printer' : "this printer's key"} (${error.status}) - stopped; restart the agent once it has a new key`);
19
+ keepAlive();
20
+ void (async () => stop())().catch(stopError => {
21
+ console.error('Failed to stop cleanly:', stopError);
22
+ });
23
+ return true;
24
+ },
25
+ };
26
+ };
@@ -0,0 +1,93 @@
1
+ import { backoffDelayMs } from './utils.js';
2
+ const STATE_REPORT_INTERVAL_MS = 120_000;
3
+ const PROGRESS_REPORT_STEP_PCT = 5;
4
+ export const createPrinterStateReporter = ({ printerLabel, reportPrinterState, printerGone, }) => {
5
+ let lastReportedState = null;
6
+ let lastReportedAt = 0;
7
+ let pendingState = null;
8
+ let reportingPromise = null;
9
+ let failures = 0;
10
+ let retryTimeoutId;
11
+ let isStopped = false;
12
+ const shouldReport = (state) => {
13
+ if (!lastReportedState) {
14
+ return true;
15
+ }
16
+ if (state.state !== lastReportedState.state ||
17
+ state.hasCameraStream !== lastReportedState.hasCameraStream) {
18
+ return true;
19
+ }
20
+ const previousProgress = lastReportedState.progressPct;
21
+ const currentProgress = state.progressPct;
22
+ if (previousProgress !== null &&
23
+ currentProgress !== null &&
24
+ Math.abs(currentProgress - previousProgress) >= PROGRESS_REPORT_STEP_PCT) {
25
+ return true;
26
+ }
27
+ return Date.now() - lastReportedAt >= STATE_REPORT_INTERVAL_MS;
28
+ };
29
+ const canSend = () => !isStopped && !printerGone.isGone();
30
+ const send = () => {
31
+ if (reportingPromise || !pendingState || !canSend()) {
32
+ return;
33
+ }
34
+ const state = pendingState;
35
+ pendingState = null;
36
+ reportingPromise = reportPrinterState(state)
37
+ .then(() => {
38
+ lastReportedState = state;
39
+ lastReportedAt = Date.now();
40
+ failures = 0;
41
+ if (pendingState && !shouldReport(pendingState)) {
42
+ pendingState = null;
43
+ }
44
+ }, error => {
45
+ if (printerGone.notice(error)) {
46
+ pendingState = null;
47
+ return;
48
+ }
49
+ pendingState ??= state;
50
+ failures++;
51
+ const delayMs = backoffDelayMs(failures, 30_000, 600_000);
52
+ console.error(`Failed to report state for "${printerLabel}"; trying again in ${delayMs / 1000}s:`, error);
53
+ if (canSend()) {
54
+ retryTimeoutId = setTimeout(() => {
55
+ retryTimeoutId = undefined;
56
+ send();
57
+ }, delayMs);
58
+ }
59
+ })
60
+ .finally(() => {
61
+ reportingPromise = null;
62
+ if (retryTimeoutId === undefined) {
63
+ send();
64
+ }
65
+ });
66
+ };
67
+ return {
68
+ report: state => {
69
+ if (!canSend() || (!pendingState && !shouldReport(state))) {
70
+ return;
71
+ }
72
+ pendingState = state;
73
+ if (retryTimeoutId === undefined) {
74
+ send();
75
+ }
76
+ },
77
+ flush: async () => {
78
+ if (!reportingPromise && retryTimeoutId !== undefined) {
79
+ clearTimeout(retryTimeoutId);
80
+ retryTimeoutId = undefined;
81
+ send();
82
+ }
83
+ while (reportingPromise) {
84
+ await reportingPromise;
85
+ }
86
+ },
87
+ stop: () => {
88
+ isStopped = true;
89
+ clearTimeout(retryTimeoutId);
90
+ retryTimeoutId = undefined;
91
+ },
92
+ };
93
+ };
@@ -0,0 +1,10 @@
1
+ export var BambuGcodeState;
2
+ (function (BambuGcodeState) {
3
+ BambuGcodeState["IDLE"] = "IDLE";
4
+ BambuGcodeState["RUNNING"] = "RUNNING";
5
+ BambuGcodeState["PAUSE"] = "PAUSE";
6
+ BambuGcodeState["FAILED"] = "FAILED";
7
+ BambuGcodeState["CANCEL"] = "CANCEL";
8
+ BambuGcodeState["FINISH"] = "FINISH";
9
+ BambuGcodeState["PREPARE"] = "PREPARE";
10
+ })(BambuGcodeState || (BambuGcodeState = {}));
@@ -0,0 +1,2 @@
1
+ export * from './bambu.types.js';
2
+ export * from './moonraker.types.js';
@@ -0,0 +1,16 @@
1
+ export var MoonrakerPrintState;
2
+ (function (MoonrakerPrintState) {
3
+ MoonrakerPrintState["STANDBY"] = "standby";
4
+ MoonrakerPrintState["PRINTING"] = "printing";
5
+ MoonrakerPrintState["PAUSED"] = "paused";
6
+ MoonrakerPrintState["COMPLETE"] = "complete";
7
+ MoonrakerPrintState["CANCELLED"] = "cancelled";
8
+ MoonrakerPrintState["ERROR"] = "error";
9
+ })(MoonrakerPrintState || (MoonrakerPrintState = {}));
10
+ export var KlippyState;
11
+ (function (KlippyState) {
12
+ KlippyState["READY"] = "ready";
13
+ KlippyState["STARTUP"] = "startup";
14
+ KlippyState["SHUTDOWN"] = "shutdown";
15
+ KlippyState["ERROR"] = "error";
16
+ })(KlippyState || (KlippyState = {}));