@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.
- package/LICENSE +74 -0
- package/README.md +530 -2
- package/certs/bambu-ca.pem +218 -0
- package/dist/bootstrap.js +252 -0
- package/dist/camera-bambu.js +92 -0
- package/dist/camera-binaries.js +185 -0
- package/dist/camera-frame-bridge.js +102 -0
- package/dist/camera-gateway.js +201 -0
- package/dist/camera-jpeg-bridge.js +19 -0
- package/dist/camera-jpeg-connection.js +121 -0
- package/dist/camera-relay.js +542 -0
- package/dist/camera-stream-bridge.js +58 -0
- package/dist/camera-stream-connection.js +84 -0
- package/dist/camera-tunnel.js +80 -0
- package/dist/camera-webcam-connection.js +186 -0
- package/dist/camera-webcam.js +156 -0
- package/dist/clients/ApiRequestError.js +7 -0
- package/dist/clients/BambuMqttClient.js +108 -0
- package/dist/clients/MoonrakerClient.js +289 -0
- package/dist/clients/Vextrix3dApiClient.js +121 -0
- package/dist/moonraker-camera-reporter.js +21 -0
- package/dist/print-attempt-synchronizer.js +76 -0
- package/dist/printer-gone.js +26 -0
- package/dist/printer-state-reporter.js +93 -0
- package/dist/types/bambu.types.js +10 -0
- package/dist/types/index.js +2 -0
- package/dist/types/moonraker.types.js +16 -0
- package/dist/utils.js +238 -0
- package/package.json +70 -4
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Bambu's CAs - BBL CA, and BBL CA2 in RSA and ECC, each of those also as BBL CA signed it -
|
|
2
|
+
# as OpenBambuAPI collects them:
|
|
3
|
+
# https://github.com/Doridian/OpenBambuAPI/blob/main/examples/ca_cert.pem
|
|
4
|
+
-----BEGIN CERTIFICATE-----
|
|
5
|
+
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
|
|
34
|
+
haGNe/KFNvJBMOIuIUzknRRmiQ==
|
|
35
|
+
-----END CERTIFICATE-----
|
|
36
|
+
-----BEGIN CERTIFICATE-----
|
|
37
|
+
MIIB8zCCAZmgAwIBAgIUe61jGQ4RzIC8k+sNuqbI/CaNqPIwCgYIKoZIzj0EAwIw
|
|
38
|
+
RjELMAkGA1UEBhMCQ04xITAfBgNVBAoMGEJCTCBUZWNobm9sb2dpZXMgQ28uIEx0
|
|
39
|
+
ZDEUMBIGA1UEAwwLQkJMIENBMiBFQ0MwIBcNMjUwNjE3MDEzODM1WhgPMjA1MDA2
|
|
40
|
+
MTcwMTM4MzVaMEYxCzAJBgNVBAYTAkNOMSEwHwYDVQQKDBhCQkwgVGVjaG5vbG9n
|
|
41
|
+
aWVzIENvLiBMdGQxFDASBgNVBAMMC0JCTCBDQTIgRUNDMFkwEwYHKoZIzj0CAQYI
|
|
42
|
+
KoZIzj0DAQcDQgAEpKTF7wRSty4DXpGJzgCPwRh8ghLlxUC3qJbyEgLqTvJgbiwY
|
|
43
|
+
APPHK7kVbVmerkqhHOT4QeWRlTG3dOQGLA2VpaNjMGEwHQYDVR0OBBYEFKuRpsjY
|
|
44
|
+
REOyIKH7HwOE6jhGBd6NMB8GA1UdIwQYMBaAFKuRpsjYREOyIKH7HwOE6jhGBd6N
|
|
45
|
+
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0gA
|
|
46
|
+
MEUCIErBiUm3VdtP3rz4kb8aLpI5p+BzL7M9vElBGWWJxpHMAiEA3r5tJWVGwuxi
|
|
47
|
+
YCrB1c40KYFRFyahGrhOJZAj/YhRdnU=
|
|
48
|
+
-----END CERTIFICATE-----
|
|
49
|
+
-----BEGIN CERTIFICATE-----
|
|
50
|
+
MIIEeTCCA2GgAwIBAgIUOq+lNIaC2xsswkFqj5JPyVBl45cwDQYJKoZIhvcNAQEL
|
|
51
|
+
BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
|
|
52
|
+
LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yNTA2MTcwMjAxMjdaFw0zNTA2MTUw
|
|
53
|
+
MjAxMjdaMEYxCzAJBgNVBAYTAkNOMSEwHwYDVQQKDBhCQkwgVGVjaG5vbG9naWVz
|
|
54
|
+
IENvLiBMdGQxFDASBgNVBAMMC0JCTCBDQTIgUlNBMIICIjANBgkqhkiG9w0BAQEF
|
|
55
|
+
AAOCAg8AMIICCgKCAgEAo4550G4c42gTKzQqixwKT089RizIdZpyOcGA679rPaOd
|
|
56
|
+
WsMqVwnYPP2FpMqXKkjFbedE+SpGloi2NKCuiPNVRbq9PHOOZwTs7YLobOwf53FJ
|
|
57
|
+
uO6vRFpzFfX1tlc9zlFqJvZnYO9NgHpMysidocWcgrDN/SIDywgPB5CVbYg3Vvzu
|
|
58
|
+
a9fwZx9e5KT9xd5IpTqdTrWS47jQOVKLhdQCbJFIlMrblOwLBAx+fHokwqh6tkI6
|
|
59
|
+
Ktuyyjw8Dysebi1ndWjKtZ2mW47r8xZ/J+z3EZqcyJMY6MRtx/zb1jBFuHtkjrb5
|
|
60
|
+
Kv1DMzSKlkaNJIbvC+Mk+hI97W+SjLSRuIdC7+oJUzWaSzgu9cjXCVfmq8t4IL/3
|
|
61
|
+
5hP69PK95LgLectIrP96CYAT/aVMG19FrFW0QWEyfT+kzG4jkumfPbHqY2nNkEN0
|
|
62
|
+
+tjj3h4WdzrWgQEojK/lhfcRFVkts74+aZoMpQP+vmL17CKmSzXk5o/eK21xgxJd
|
|
63
|
+
zMbdztfTpibiXk0abfOpN+1VR+3NYa+bROAKNyGaReEGsyW2bjcjNx515Vqzj3SV
|
|
64
|
+
xhMSp5vfF9E4A1jE99M/l9jQDM6RzkT0lMccGAd5tUSdNvDlrqtQaQiKv/ZsXPgX
|
|
65
|
+
LTWfOpvaLNEgwdMgZMuhjpkwvAZyoYfeF9kyydjDh7bvrX//cz/VopAUlxUtQtMC
|
|
66
|
+
AwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
|
|
67
|
+
BBYEFNVJgQad1sNTN0jxVkwbJ/XM1an1MB8GA1UdIwQYMBaAFI80QmjcZ06PxCKe
|
|
68
|
+
xXxJ5avdRL4eMA0GCSqGSIb3DQEBCwUAA4IBAQAvS8tyfagaGsFf9YncA2ko/Na5
|
|
69
|
+
9BVF+8TlUo+32oznwIVpS1AhSgLP6rNVekXNFKbuP5htudLQ17ZRBJI/UMVyYEDq
|
|
70
|
+
IN7xv7Zj+zJwF6W6haYrjb2Vk8igw1XvNULZfvVNNKIkvJUiVqEslWrC+k74crk/
|
|
71
|
+
Wv8ChVf+zqvfIN6LV3esaGRL02J3AprQGb7DDhR1EefQMScDkNpGJMUmvCmfknrl
|
|
72
|
+
iK8qgvQN1SWO7JRf6fNKHsN1ZQvyP0pgLWxpT3V0/0/WttqX3cMGuJF+jVUzm/Nh
|
|
73
|
+
xYhFewG8vc3KzTjnwQApMA6CW554FOJWFyOD2jn5yJLT3Vue+aYDQRp4bKMx
|
|
74
|
+
-----END CERTIFICATE-----
|
|
75
|
+
-----BEGIN CERTIFICATE-----
|
|
76
|
+
MIICrjCCAZagAwIBAgIUOq+lNIaC2xsswkFqj5JPyVBl45gwDQYJKoZIhvcNAQEL
|
|
77
|
+
BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
|
|
78
|
+
LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yNTA2MTcwMjAxNDdaFw0zNTA2MTUw
|
|
79
|
+
MjAxNDdaMEYxCzAJBgNVBAYTAkNOMSEwHwYDVQQKDBhCQkwgVGVjaG5vbG9naWVz
|
|
80
|
+
IENvLiBMdGQxFDASBgNVBAMMC0JCTCBDQTIgRUNDMFkwEwYHKoZIzj0CAQYIKoZI
|
|
81
|
+
zj0DAQcDQgAEpKTF7wRSty4DXpGJzgCPwRh8ghLlxUC3qJbyEgLqTvJgbiwYAPPH
|
|
82
|
+
K7kVbVmerkqhHOT4QeWRlTG3dOQGLA2VpaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAO
|
|
83
|
+
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFKuRpsjYREOyIKH7HwOE6jhGBd6NMB8G
|
|
84
|
+
A1UdIwQYMBaAFI80QmjcZ06PxCKexXxJ5avdRL4eMA0GCSqGSIb3DQEBCwUAA4IB
|
|
85
|
+
AQCg6PjUSSZV+4bvejcVMvgXmKzfD95osWn0ctnoMBxPDa+m+Gg+BcLT2IlFAe3E
|
|
86
|
+
KYMvu4T295WQc92rjKYqW6cirFppng9uEFW2mZLimxaSmutsTftE3sbMVMJ/SLYN
|
|
87
|
+
PV7TFv6mcBSIFWXwmBOIpbh4BUcVfONTvdSfIqfyAVxsq4xzc2nc6hPBpAm21Ayj
|
|
88
|
+
ToC1ev/TbDJ8VllFZiEVmWWlIP3aNzAm8S2mOpxPB2WnanaZHSrvXLFhstyzwrjD
|
|
89
|
+
yO1/isOZ7wtr7rcuTJdEvvvCimOZlkfRhaDoTew9tQ0E2FVpzzSinw02qmQ1xIE9
|
|
90
|
+
5/H5ZzJSPkpeAHWEPnKkxg0v
|
|
91
|
+
-----END CERTIFICATE-----
|
|
92
|
+
-----BEGIN CERTIFICATE-----
|
|
93
|
+
MIIDZTCCAk2gAwIBAgIUV1FckwXElyek1onFnQ9kL7Bk4N8wDQYJKoZIhvcNAQEL
|
|
94
|
+
BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
|
|
95
|
+
LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yMjA0MDQwMzQyMTFaFw0zMjA0MDEw
|
|
96
|
+
MzQyMTFaMEIxCzAJBgNVBAYTAkNOMSIwIAYDVQQKDBlCQkwgVGVjaG5vbG9naWVz
|
|
97
|
+
IENvLiwgTHRkMQ8wDQYDVQQDDAZCQkwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
|
98
|
+
DwAwggEKAoIBAQDL3pnDdxGOk5Z6vugiT4dpM0ju+3Xatxz09UY7mbj4tkIdby4H
|
|
99
|
+
oeEdiYSZjc5LJngJuCHwtEbBJt1BriRdSVrF6M9D2UaBDyamEo0dxwSaVxZiDVWC
|
|
100
|
+
eeCPdELpFZdEhSNTaT4O7zgvcnFsfHMa/0vMAkvE7i0qp3mjEzYLfz60axcDoJLk
|
|
101
|
+
p7n6xKXI+cJbA4IlToFjpSldPmC+ynOo7YAOsXt7AYKY6Glz0BwUVzSJxU+/+VFy
|
|
102
|
+
/QrmYGNwlrQtdREHeRi0SNK32x1+bOndfJP0sojuIrDjKsdCLye5CSZIvqnbowwW
|
|
103
|
+
1jRwZgTBR29Zp2nzCoxJYcU9TSQp/4KZuWNVAgMBAAGjUzBRMB0GA1UdDgQWBBSP
|
|
104
|
+
NEJo3GdOj8QinsV8SeWr3US+HjAfBgNVHSMEGDAWgBSPNEJo3GdOj8QinsV8SeWr
|
|
105
|
+
3US+HjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQABlBIT5ZeG
|
|
106
|
+
fgcK1LOh1CN9sTzxMCLbtTPFF1NGGA13mApu6j1h5YELbSKcUqfXzMnVeAb06Htu
|
|
107
|
+
3CoCoe+wj7LONTFO++vBm2/if6Jt/DUw1CAEcNyqeh6ES0NX8LJRVSe0qdTxPJuA
|
|
108
|
+
BdOoo96iX89rRPoxeed1cpq5hZwbeka3+CJGV76itWp35Up5rmmUqrlyQOr/Wax6
|
|
109
|
+
itosIzG0MfhgUzU51A2P/hSnD3NDMXv+wUY/AvqgIL7u7fbDKnku1GzEKIkfH8hm
|
|
110
|
+
Rs6d8SCU89xyrwzQ0PR853irHas3WrHVqab3P+qNwR0YirL0Qk7Xt/q3O1griNg2
|
|
111
|
+
Blbjg3obpHo9
|
|
112
|
+
-----END CERTIFICATE-----
|
|
113
|
+
|
|
114
|
+
# The device CAs some models' certificates are issued through, each issued by BBL CA2 RSA, as the
|
|
115
|
+
# Home Assistant integration ships them:
|
|
116
|
+
# https://github.com/greghesp/ha-bambulab/tree/main/custom_components/bambu_lab/pybambu/certs
|
|
117
|
+
|
|
118
|
+
# BBL Device CA N7-V2 - some P2S units
|
|
119
|
+
-----BEGIN CERTIFICATE-----
|
|
120
|
+
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
|
|
149
|
+
v09+U4m1fOSvF0We4MPCYt+z/E/br+uZ1pIBcg==
|
|
150
|
+
-----END CERTIFICATE-----
|
|
151
|
+
|
|
152
|
+
# BBL Device CA O1C2-V2 - H2C
|
|
153
|
+
-----BEGIN CERTIFICATE-----
|
|
154
|
+
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
|
|
184
|
+
-----END CERTIFICATE-----
|
|
185
|
+
|
|
186
|
+
# BBL Device CA N6-V2 - X2C
|
|
187
|
+
-----BEGIN CERTIFICATE-----
|
|
188
|
+
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
|
|
217
|
+
kvkHW8vmTffkFHMWC/YoXRXklNlv79Z4+Rywrw==
|
|
218
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { loadEnvFile } from 'node:process';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { createBambuCameraSource, readCameraStream } from './camera-bambu.js';
|
|
7
|
+
import { readJpegCamera } from './camera-jpeg-connection.js';
|
|
8
|
+
import { resolveCameraRelay } from './camera-relay.js';
|
|
9
|
+
import { createWebcamCameraSource, createWebcamReader } from './camera-webcam.js';
|
|
10
|
+
import { BambuMqttClient } from './clients/BambuMqttClient.js';
|
|
11
|
+
import { MoonrakerClient } from './clients/MoonrakerClient.js';
|
|
12
|
+
import { PrinterConnectionType, Vextrix3dApiClient, } from './clients/Vextrix3dApiClient.js';
|
|
13
|
+
import { createMoonrakerCameraReporter } from './moonraker-camera-reporter.js';
|
|
14
|
+
import { createPrintAttemptSynchronizer } from './print-attempt-synchronizer.js';
|
|
15
|
+
import { createPrinterGone } from './printer-gone.js';
|
|
16
|
+
import { createPrinterStateReporter } from './printer-state-reporter.js';
|
|
17
|
+
import { assert, buildMoonrakerAgentState, createBambuStateResolver, formatSubtaskName, once, readBambuTemperature, requireEnvVar, } from './utils.js';
|
|
18
|
+
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
|
+
const workDir = process.cwd();
|
|
20
|
+
const { version: AGENT_VERSION } = JSON.parse(readFileSync(resolve(packageDir, 'package.json'), 'utf8'));
|
|
21
|
+
if (process.argv.includes('--version')) {
|
|
22
|
+
console.log(AGENT_VERSION);
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
const envFile = process.env.NODE_ENV === 'production' ? '.env.prod' : '.env.local';
|
|
26
|
+
const envPath = resolve(workDir, envFile);
|
|
27
|
+
if (!existsSync(envPath)) {
|
|
28
|
+
console.error(`No ${envFile} in ${workDir}: run the agent in the printer's folder`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
loadEnvFile(envPath);
|
|
32
|
+
console.info(`Loaded environment from ${envPath}`);
|
|
33
|
+
const fetchesCameraBinaries = process.env.VEXTRIX3D_CAMERA_BINARIES?.trim() !== 'manual';
|
|
34
|
+
const apiClient = new Vextrix3dApiClient({
|
|
35
|
+
functionsUrl: requireEnvVar('VEXTRIX3D_FUNCTIONS_URL'),
|
|
36
|
+
apiKey: requireEnvVar('VEXTRIX3D_MACHINE_AGENT_API_KEY'),
|
|
37
|
+
agentVersion: AGENT_VERSION,
|
|
38
|
+
});
|
|
39
|
+
const parseWifiSignalDbm = (value) => {
|
|
40
|
+
if (typeof value !== 'string') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const parsed = Number.parseInt(value, 10);
|
|
44
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
45
|
+
};
|
|
46
|
+
const startPrinterReporting = (printer, cameraSource, apiClient, printerGone) => {
|
|
47
|
+
const cameraRelay = resolveCameraRelay({
|
|
48
|
+
workDir,
|
|
49
|
+
printerId: printer.id,
|
|
50
|
+
printerLabel: printer.label,
|
|
51
|
+
source: cameraSource,
|
|
52
|
+
fetchesBinaries: fetchesCameraBinaries,
|
|
53
|
+
getCameraAccess: () => apiClient.getCamera(),
|
|
54
|
+
printerGone,
|
|
55
|
+
});
|
|
56
|
+
const stateReporter = createPrinterStateReporter({
|
|
57
|
+
printerLabel: printer.label,
|
|
58
|
+
reportPrinterState: async (state) => {
|
|
59
|
+
const answer = await apiClient.reportPrinterState(state);
|
|
60
|
+
cameraRelay?.noteRelayKeyFingerprint(answer.cameraRelayKeyFingerprint);
|
|
61
|
+
return answer;
|
|
62
|
+
},
|
|
63
|
+
printerGone,
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
cameraRelay,
|
|
67
|
+
stateReporter,
|
|
68
|
+
hasCameraStream: report => (report === undefined ? undefined : cameraSource.hasStream(report)),
|
|
69
|
+
stop: async () => {
|
|
70
|
+
await cameraRelay?.stop();
|
|
71
|
+
await stateReporter.flush();
|
|
72
|
+
stateReporter.stop();
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const connectToBambuPrinter = (printer, connection, apiClient, printerGone) => {
|
|
77
|
+
const { ip, serialNumber, accessCode } = connection;
|
|
78
|
+
const cameraSource = createBambuCameraSource({
|
|
79
|
+
printerLabel: printer.label,
|
|
80
|
+
accessCode,
|
|
81
|
+
serialNumber,
|
|
82
|
+
caFile: resolve(packageDir, 'certs', 'bambu-ca.pem'),
|
|
83
|
+
jpegCamera: readJpegCamera(serialNumber, ip),
|
|
84
|
+
});
|
|
85
|
+
const { cameraRelay, stateReporter, hasCameraStream, stop: stopReporting, } = startPrinterReporting(printer, cameraSource, apiClient, printerGone);
|
|
86
|
+
const printAttemptSynchronizer = createPrintAttemptSynchronizer({
|
|
87
|
+
printerLabel: printer.label,
|
|
88
|
+
apiClient,
|
|
89
|
+
printerGone,
|
|
90
|
+
});
|
|
91
|
+
const resolveState = createBambuStateResolver();
|
|
92
|
+
const mqttClient = new BambuMqttClient({
|
|
93
|
+
ip,
|
|
94
|
+
serialNumber,
|
|
95
|
+
accessCode,
|
|
96
|
+
onConnect: () => {
|
|
97
|
+
console.log(`Connected to "${printer.label}" (${serialNumber})`);
|
|
98
|
+
void printAttemptSynchronizer.initialise();
|
|
99
|
+
},
|
|
100
|
+
onStatus: print => {
|
|
101
|
+
const state = resolveState(print);
|
|
102
|
+
const cameraStream = readCameraStream(print.ipcam);
|
|
103
|
+
stateReporter.report({
|
|
104
|
+
state,
|
|
105
|
+
subtaskName: formatSubtaskName(print.subtask_name),
|
|
106
|
+
progressPct: print.mc_percent ?? null,
|
|
107
|
+
remainingMinutes: print.mc_remaining_time ?? null,
|
|
108
|
+
nozzleTemperature: print.nozzle_temper ?? null,
|
|
109
|
+
bedTemperature: print.bed_temper ?? null,
|
|
110
|
+
chamberTemperature: readBambuTemperature(print.device?.ctc?.info?.temp),
|
|
111
|
+
wifiSignalDbm: parseWifiSignalDbm(print.wifi_signal),
|
|
112
|
+
hasCameraStream: hasCameraStream(cameraStream),
|
|
113
|
+
});
|
|
114
|
+
printAttemptSynchronizer.handleState(state);
|
|
115
|
+
cameraRelay?.update(cameraStream);
|
|
116
|
+
},
|
|
117
|
+
onReconnect: () => {
|
|
118
|
+
console.log(`Reconnecting to "${printer.label}"...`);
|
|
119
|
+
},
|
|
120
|
+
onClose: () => {
|
|
121
|
+
console.log(`Connection to "${printer.label}" closed`);
|
|
122
|
+
},
|
|
123
|
+
onError: error => {
|
|
124
|
+
console.error(`MQTT error for "${printer.label}":`, error);
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
stop: once(async () => {
|
|
129
|
+
await stopReporting();
|
|
130
|
+
await mqttClient.stop();
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
const connectToMoonrakerPrinter = (printer, connection, apiClient, printerGone) => {
|
|
135
|
+
const { ip, port, apiKey } = connection;
|
|
136
|
+
const cameraSource = createWebcamCameraSource({
|
|
137
|
+
printerLabel: printer.label,
|
|
138
|
+
});
|
|
139
|
+
const { cameraRelay, stateReporter, hasCameraStream, stop: stopReporting, } = startPrinterReporting(printer, cameraSource, apiClient, printerGone);
|
|
140
|
+
const cameraReporter = createMoonrakerCameraReporter({
|
|
141
|
+
stateReporter,
|
|
142
|
+
cameraRelay,
|
|
143
|
+
readWebcams: createWebcamReader(ip, port),
|
|
144
|
+
hasCameraStream,
|
|
145
|
+
});
|
|
146
|
+
const printAttemptSynchronizer = createPrintAttemptSynchronizer({
|
|
147
|
+
printerLabel: printer.label,
|
|
148
|
+
apiClient,
|
|
149
|
+
printerGone,
|
|
150
|
+
});
|
|
151
|
+
let estimatedFilename = null;
|
|
152
|
+
let estimatedTimeS = null;
|
|
153
|
+
const loadEstimatedTime = (filename) => {
|
|
154
|
+
if (filename === estimatedFilename) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
estimatedFilename = filename;
|
|
158
|
+
estimatedTimeS = null;
|
|
159
|
+
if (!filename) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
client
|
|
163
|
+
.getFileEstimatedTime(filename)
|
|
164
|
+
.then(seconds => {
|
|
165
|
+
if (estimatedFilename === filename) {
|
|
166
|
+
estimatedTimeS = seconds;
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
.catch(error => {
|
|
170
|
+
console.error(`Failed to read the estimate of "${filename}" on "${printer.label}":`, error);
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const client = new MoonrakerClient({
|
|
174
|
+
ip,
|
|
175
|
+
port,
|
|
176
|
+
apiKey,
|
|
177
|
+
onConnect: () => {
|
|
178
|
+
console.log(`Connected to "${printer.label}" (${ip}:${port})`);
|
|
179
|
+
void printAttemptSynchronizer.initialise();
|
|
180
|
+
},
|
|
181
|
+
onStatus: status => {
|
|
182
|
+
const filename = status.print_stats?.filename;
|
|
183
|
+
loadEstimatedTime(typeof filename === 'string' ? filename : '');
|
|
184
|
+
const agentState = buildMoonrakerAgentState(status, estimatedTimeS);
|
|
185
|
+
cameraReporter.reportState(agentState);
|
|
186
|
+
printAttemptSynchronizer.handleState(agentState.state);
|
|
187
|
+
},
|
|
188
|
+
onWebcams: webcams => cameraReporter.noteWebcams(webcams),
|
|
189
|
+
onReconnect: () => {
|
|
190
|
+
console.log(`Reconnecting to "${printer.label}"...`);
|
|
191
|
+
},
|
|
192
|
+
onClose: () => {
|
|
193
|
+
console.log(`Connection to "${printer.label}" closed`);
|
|
194
|
+
},
|
|
195
|
+
onError: error => {
|
|
196
|
+
console.error(`Moonraker error for "${printer.label}":`, error);
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
stop: once(async () => {
|
|
201
|
+
await stopReporting();
|
|
202
|
+
await client.stop();
|
|
203
|
+
}),
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
const connectToPrinter = (printer, apiClient, printerGone) => {
|
|
207
|
+
const { connection } = printer;
|
|
208
|
+
switch (connection.type) {
|
|
209
|
+
case PrinterConnectionType.BAMBU_MQTT:
|
|
210
|
+
return connectToBambuPrinter(printer, connection, apiClient, printerGone);
|
|
211
|
+
case PrinterConnectionType.MOONRAKER:
|
|
212
|
+
return connectToMoonrakerPrinter(printer, connection, apiClient, printerGone);
|
|
213
|
+
default:
|
|
214
|
+
throw new Error(`Unsupported printer connection type: ${connection.type}`);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
let printerConnection = null;
|
|
218
|
+
const printerGone = createPrinterGone({
|
|
219
|
+
stop: () => printerConnection?.stop(),
|
|
220
|
+
});
|
|
221
|
+
const stop = once(async () => {
|
|
222
|
+
console.log('Stopping machine agent');
|
|
223
|
+
try {
|
|
224
|
+
await printerConnection?.stop();
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
console.error('Failed to stop cleanly:', error);
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
process.once('SIGINT', () => {
|
|
234
|
+
void stop();
|
|
235
|
+
});
|
|
236
|
+
process.once('SIGTERM', () => {
|
|
237
|
+
void stop();
|
|
238
|
+
});
|
|
239
|
+
const bootstrap = async () => {
|
|
240
|
+
console.log(`Starting machine agent ${AGENT_VERSION}`);
|
|
241
|
+
const printer = await apiClient.getPrinterInfo();
|
|
242
|
+
console.log(`Loaded printer "${printer.label}" (${printer.id})`);
|
|
243
|
+
assert(printer.isEnabled, `Printer "${printer.label}" is disabled`);
|
|
244
|
+
printerConnection = connectToPrinter(printer, apiClient, printerGone);
|
|
245
|
+
};
|
|
246
|
+
bootstrap().catch(error => {
|
|
247
|
+
if (printerGone.notice(error)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
console.error('Failed to start:', error);
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createCameraJpegBridge } from './camera-jpeg-bridge.js';
|
|
2
|
+
import { createCameraStreamBridge, } from './camera-stream-bridge.js';
|
|
3
|
+
export const readCameraStream = (ipcam) => ipcam === undefined ? undefined : (ipcam.rtsp_url ?? null);
|
|
4
|
+
const parseCameraStreamUrl = (rtspUrl) => {
|
|
5
|
+
if (!rtspUrl) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
let url;
|
|
9
|
+
try {
|
|
10
|
+
url = new URL(rtspUrl);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return url.protocol === 'rtsps:' || url.protocol === 'rtsp:' ? url : null;
|
|
16
|
+
};
|
|
17
|
+
export const hasCameraStream = (rtspUrl, jpegCamera = null) => (jpegCamera ? rtspUrl !== undefined : parseCameraStreamUrl(rtspUrl) !== null);
|
|
18
|
+
export const readCameraStreamTarget = (rtspUrl) => {
|
|
19
|
+
const url = parseCameraStreamUrl(rtspUrl);
|
|
20
|
+
return url?.protocol === 'rtsps:'
|
|
21
|
+
? {
|
|
22
|
+
host: url.hostname,
|
|
23
|
+
port: Number(url.port) || 322,
|
|
24
|
+
}
|
|
25
|
+
: null;
|
|
26
|
+
};
|
|
27
|
+
export const buildCameraStreamUrl = (rtspUrl, accessCode, bridgePort) => {
|
|
28
|
+
const url = parseCameraStreamUrl(rtspUrl);
|
|
29
|
+
if (!url) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const streamUrl = url.protocol === 'rtsps:'
|
|
33
|
+
? new URL(`rtsp://127.0.0.1:${bridgePort}${url.pathname}${url.search}`)
|
|
34
|
+
: url;
|
|
35
|
+
streamUrl.username = 'bblp';
|
|
36
|
+
streamUrl.password = accessCode;
|
|
37
|
+
return streamUrl.toString();
|
|
38
|
+
};
|
|
39
|
+
const defaultBambuDependencies = {
|
|
40
|
+
createStreamBridge: options => createCameraStreamBridge(options),
|
|
41
|
+
createJpegBridge: options => createCameraJpegBridge(options),
|
|
42
|
+
};
|
|
43
|
+
export const createBambuCameraSource = ({ printerLabel, accessCode, serialNumber, caFile, jpegCamera }, dependencies = defaultBambuDependencies) => ({
|
|
44
|
+
hasStream: rtspUrl => hasCameraStream(rtspUrl, jpegCamera),
|
|
45
|
+
encodedCamera: jpegCamera ? `a Bambu ${jpegCamera.model}'s camera` : null,
|
|
46
|
+
describeStream: () => jpegCamera
|
|
47
|
+
? `"${printerLabel}" is a Bambu ${jpegCamera.model}, whose camera has no RTSP: its JPEG frames are read from ${jpegCamera.host}:${jpegCamera.port} and encoded to H.264 while somebody watches`
|
|
48
|
+
: null,
|
|
49
|
+
describeNoStream: rtspUrl => `"${printerLabel}" reports no camera stream (${rtspUrl || 'none'}) - its LAN live view is off, or its camera is of a model the agent does not read - so its camera is not relayed until it names one`,
|
|
50
|
+
openInput: () => {
|
|
51
|
+
if (jpegCamera) {
|
|
52
|
+
const bridge = dependencies.createJpegBridge({
|
|
53
|
+
host: jpegCamera.host,
|
|
54
|
+
port: jpegCamera.port,
|
|
55
|
+
serialNumber,
|
|
56
|
+
accessCode,
|
|
57
|
+
caFile,
|
|
58
|
+
printerLabel,
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
resolveTarget: async () => ({
|
|
62
|
+
kind: 'jpeg',
|
|
63
|
+
streamUrl: await bridge.streamUrl,
|
|
64
|
+
orientation: null,
|
|
65
|
+
}),
|
|
66
|
+
close: bridge.close,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const bridge = dependencies.createStreamBridge({
|
|
70
|
+
serialNumber,
|
|
71
|
+
caFile,
|
|
72
|
+
printerLabel,
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
resolveTarget: async (rtspUrl) => {
|
|
76
|
+
const target = readCameraStreamTarget(rtspUrl);
|
|
77
|
+
if (target) {
|
|
78
|
+
bridge.setTarget(target.host, target.port);
|
|
79
|
+
}
|
|
80
|
+
const streamUrl = buildCameraStreamUrl(rtspUrl, accessCode, await bridge.port);
|
|
81
|
+
return streamUrl
|
|
82
|
+
? {
|
|
83
|
+
kind: 'rtsp',
|
|
84
|
+
streamUrl,
|
|
85
|
+
orientation: null,
|
|
86
|
+
}
|
|
87
|
+
: null;
|
|
88
|
+
},
|
|
89
|
+
close: bridge.close,
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
});
|