camera_snapshot_request (cloud to gateway) and camera_snapshot (gateway to cloud) carry one JPEG frame at a time; telemetry gains camera_snapshot and the app view gains cameraSnapshot.
211 lines
7.6 KiB
JSON
211 lines
7.6 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://git.octoturge.com/Continuum/continuum-schemas/raw/branch/main/schemas/continuum/v1/edge/server_message.schema.json",
|
|
"title": "EdgeServerMessage",
|
|
"description": "What continuum-backend sends back down the same WebSocket. Discriminated on 'type'; snake_case for the same reason as EdgeClientMessage.",
|
|
"oneOf": [
|
|
{
|
|
"type": "object",
|
|
"title": "HelloAck",
|
|
"properties": {
|
|
"type": {
|
|
"const": "hello_ack"
|
|
},
|
|
"session_id": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"session_id"
|
|
]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "HelloError",
|
|
"description": "The handshake was rejected. The connection closes immediately after this.",
|
|
"properties": {
|
|
"type": {
|
|
"const": "hello_error"
|
|
},
|
|
"message": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"message"
|
|
]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "HeartbeatAck",
|
|
"properties": {
|
|
"type": {
|
|
"const": "heartbeat_ack"
|
|
},
|
|
"seq": {
|
|
"type": "integer",
|
|
"minimum": 0,
|
|
"description": "Echoes the seq from the heartbeat being acknowledged."
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"seq"
|
|
]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "Command",
|
|
"description": "Do something to one printer on this gateway. Answered with a client command_result carrying the same command_id.",
|
|
"properties": {
|
|
"type": {
|
|
"const": "command"
|
|
},
|
|
"command_id": {
|
|
"type": "string"
|
|
},
|
|
"printer_id": {
|
|
"type": "string",
|
|
"description": "The gateway's own printer id, not the backend UUID - the backend translates on the way out, so a gateway never has to know its printers' cloud identities."
|
|
},
|
|
"command": {
|
|
"$ref": "./printer_command.schema.json"
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"command_id",
|
|
"printer_id",
|
|
"command"
|
|
]
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "WebrtcOffer",
|
|
"description": "Relay a browser's WebRTC offer to this gateway's go2rtc and send the answer back. SIGNALLING ONLY - no video crosses this socket. The gateway POSTs the offer to its own go2rtc over loopback (which go2rtc exempts from its API password, so no credential is needed or sent) and returns the answer as a WebrtcAnswer, after which the browser and go2rtc negotiate media directly over ICE. This exists because a browser cannot signal to a gateway itself: measured against go2rtc 1.9.14, it sends no CORS headers on anything and checks its password before CORS, so the unauthenticated preflight a browser always sends gets a 401. Relaying only the signalling keeps the media peer-to-peer - on a workshop LAN it never leaves the building, and go2rtc also offers a server-reflexive candidate, so a remote viewer can often hole-punch straight to the gateway. A TURN server in go2rtc's ice_servers is the fallback for the NATs where that fails; it is configuration, not a change to this protocol.",
|
|
"properties": {
|
|
"type": {
|
|
"const": "webrtc_offer"
|
|
},
|
|
"session_id": {
|
|
"type": "string",
|
|
"description": "Correlates the answer with this offer. One negotiation, not a lasting subscription."
|
|
},
|
|
"camera_stream": {
|
|
"type": "string",
|
|
"description": "The go2rtc stream name, which is what PrinterTelemetry.camera_stream reported. Deliberately not called a printer id: the two are equal today, but one is the gateway's own naming and the other is the cloud's."
|
|
},
|
|
"sdp": {
|
|
"type": "string",
|
|
"description": "The browser's offer SDP, passed through verbatim."
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"session_id",
|
|
"camera_stream",
|
|
"sdp"
|
|
],
|
|
"additionalProperties": false
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "CameraSnapshotRequest",
|
|
"description": "Read one JPEG frame from a printer's camera and send it back as a CameraSnapshot. For the printers whose camera is not RTSP (Bambu P1/A1, telemetry camera_snapshot true). Unlike WebrtcOffer this DOES carry the picture through the cloud, which is acceptable only because the frames are small, low-rate and requested one at a time.",
|
|
"properties": {
|
|
"type": { "const": "camera_snapshot_request" },
|
|
"request_id": { "type": "string" },
|
|
"printer_id": { "type": "string", "description": "The gateway's own printer id." }
|
|
},
|
|
"required": ["type", "request_id", "printer_id"],
|
|
"additionalProperties": false
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "ProvisionPrinter",
|
|
"description": "Add a printer to this gateway. Sent by the console through the cloud, which relays it and keeps nothing: it carries the printer's LAN credential (access_code or api_key), and the platform's rule is that those never rest in the cloud. The gateway checks them by connecting to the printer, saves them to its own local store, and answers with a client command_result whose command_id equals request_id. NEVER LOG THIS MESSAGE. Deliberately has no ca_cert_path or serial port: those name things on the gateway's own disk and USB bus, which the cloud does not get to choose.",
|
|
"properties": {
|
|
"type": {
|
|
"const": "provision_printer"
|
|
},
|
|
"request_id": {
|
|
"type": "string",
|
|
"description": "Echoed as command_id in the command_result."
|
|
},
|
|
"printer": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"pattern": "^[A-Za-z0-9_-]{1,64}$",
|
|
"description": "The gateway's own printer id, chosen by the cloud so that telemetry is reported under an id the cloud already knows."
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 200
|
|
},
|
|
"vendor": {
|
|
"type": "string",
|
|
"description": "A fleet vendor name: bambu, bambu_v1, bambu_v2, prusa_link, octoprint, klipper."
|
|
},
|
|
"host": {
|
|
"type": "string",
|
|
"maxLength": 255
|
|
},
|
|
"access_code": {
|
|
"type": "string",
|
|
"maxLength": 128
|
|
},
|
|
"sn": {
|
|
"type": "string",
|
|
"maxLength": 64
|
|
},
|
|
"api_key": {
|
|
"type": "string",
|
|
"maxLength": 256
|
|
}
|
|
},
|
|
"required": [
|
|
"id",
|
|
"name",
|
|
"vendor"
|
|
],
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"request_id",
|
|
"printer"
|
|
],
|
|
"additionalProperties": false
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "RemovePrinter",
|
|
"description": "Forget a printer on this gateway: drop it from the running fleet and delete its stored credential. Answered with a command_result whose command_id equals request_id.",
|
|
"properties": {
|
|
"type": {
|
|
"const": "remove_printer"
|
|
},
|
|
"request_id": {
|
|
"type": "string"
|
|
},
|
|
"printer_id": {
|
|
"type": "string",
|
|
"description": "The gateway's own printer id."
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"request_id",
|
|
"printer_id"
|
|
],
|
|
"additionalProperties": false
|
|
}
|
|
]
|
|
}
|