Nothing subscribed to channel:print_alerts and nothing watched a printer fail, so an operator away from the screen found out when they came back to a ruined plate. Alerts are now rows in printer_alerts, listed at GET /alerts (open=true for what still needs a person) and cleared with POST /alerts/:id/acknowledge, which is operator-only and keeps the first acknowledger on the record. Two sources. Telemetry: a change into error is a failed print if the printer was printing or paused, otherwise a printer error; a print the printer paused itself (how an AMS fault looks) is announced too. Transitions, not levels - a printer sitting in error is one alert, not one per frame - and a connection's first sight of a printer raises nothing, so a gateway that reconnects does not re-announce known faults. AI: the worker's Valkey publications are stored when confidence is at least ALERT_MIN_CONFIDENCE (default 0.6); the worker deliberately does not threshold, so the policy lives here. A bad message costs one message, not the listener. e2e-authz.sh gains the scoping/ack cases; new e2e-alerts.sh drives a stand-in gateway over the real /ws/edge/v1 and the real Valkey channel (checked to fail when the derivation is broken).
37 lines
1.3 KiB
TypeScript
37 lines
1.3 KiB
TypeScript
/**
|
|
* A stand-in gateway for scripts/e2e-alerts.sh: connects to /ws/edge/v1 with
|
|
* an edge JWT, says hello, then reports one printer through a sequence of
|
|
* states, one telemetry frame each, and waits for the backend to take them.
|
|
*
|
|
* Usage: bun scripts/fake-gateway.ts <ws-url> <edge-jwt> <printer-id> STATE[,STATE...]
|
|
* A state is a Bambu gcode_state word (IDLE, RUNNING, PAUSE, FAILED).
|
|
*/
|
|
const [url, jwt, printerId, states] = process.argv.slice(2);
|
|
if (!url || !jwt || !printerId || !states) {
|
|
console.error("usage: fake-gateway.ts <ws-url> <edge-jwt> <printer-id> STATE[,STATE...]");
|
|
process.exit(2);
|
|
}
|
|
|
|
const ws = new WebSocket(url, { headers: { authorization: `Bearer ${jwt}` } } as never);
|
|
ws.onerror = () => {
|
|
console.error("socket error");
|
|
process.exit(1);
|
|
};
|
|
ws.onopen = async () => {
|
|
ws.send(JSON.stringify({ type: "hello", gateway_id: "fake-gw" }));
|
|
for (const state of states.split(",")) {
|
|
ws.send(
|
|
JSON.stringify({
|
|
type: "telemetry",
|
|
printers: [{ printer_id: printerId, name: "Fake", state, percent: 10, print_error: 0 }],
|
|
}),
|
|
);
|
|
// Telemetry is persisted in order per socket; a pause keeps the frames
|
|
// from being coalesced into one observation.
|
|
await Bun.sleep(400);
|
|
}
|
|
await Bun.sleep(500);
|
|
ws.close();
|
|
process.exit(0);
|
|
};
|