{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://git.octoturge.com/Continuum/continuum-schemas/raw/branch/main/schemas/continuum/v1/edge/printer_command.schema.json", "title": "PrinterCommand", "description": "The closed set of things the cloud may ask a printer to do. Deliberately small. It now includes start_print, which was held back until there was a job path to drive it; the thing to understand about that variant is that its acceptance says almost nothing - see its own description. Every variant is closed (additionalProperties: false), unlike printer_telemetry which is deliberately open: telemetry is written by gateways in the field and read liberally, while a command is written by the backend, is a handful of fields, and moves a physical machine - an unrecognised field there is a bug on the sending side, and silently dropping it would let a caller misspell a field and watch its command succeed while doing something else.", "oneOf": [ { "type": "object", "title": "StartPrint", "description": "Print a job's file. The gateway downloads it from `url`, transfers it to the printer, and starts it. Three things about this variant that a caller must not get wrong. (1) Its result means the gateway accepted the job, not that anything is printing: a real end-to-end run on a P1P (2026-10-15) was accepted, uploaded byte-perfect, reached RUNNING, and then the printer paused itself on a physical filament fault. Job state has to be driven by the telemetry that follows, never by this reply. (2) There is no filament or AMS field on purpose. The gateway reads the mapping out of the project archive's own Metadata/slice_info.config, because the archive is the only thing that knows what each plate was sliced for - guessing it instead is what made that same printer heat to 250C and pause itself at 1%. (3) `url` is short-lived and must be presigned at dispatch time, not at job creation: a queued job may wait days, and a URL minted when it was queued would be expired by the time anything sent it.", "properties": { "action": { "const": "start_print" }, "job_id": { "type": "string", "format": "uuid", "description": "The cloud's print_jobs.id. The gateway does not need it to do the work - it is here so that a gateway's logs can be lined up against a job without reading its own telemetry back out of the cloud." }, "file_name": { "type": "string", "minLength": 1, "maxLength": 500, "description": "What to call the file on the printer's own storage. A `.3mf` is treated as a sliced project (filament mapping derived from inside it); anything else is treated as bare gcode, which carries no mapping and prints with whatever its own tool changes select." }, "url": { "type": "string", "format": "uri", "description": "A presigned, expiring GET for the job file. Deliberately a URL rather than the bytes: the file is tens of megabytes, this is a JSON control channel, and handing the gateway a URL keeps object-store credentials out of every gateway on every farm. Expect it to be valid for minutes, and note that an upload to a printer is slow - 0.2 MiB/s measured over FTPS to a P1P - so the download must finish before the transfer starts, not stream through it." } }, "required": [ "action", "job_id", "file_name", "url" ], "additionalProperties": false }, { "type": "object", "title": "SetFanSpeed", "properties": { "action": { "const": "set_fan_speed" }, "percent": { "type": "integer", "minimum": 0, "maximum": 100, "description": "A percentage on this wire regardless of what the vendor wants. Each gateway converts: for Bambu that means an M106 PWM value over MQTT, which the printer then reports back as one of 16 fan gears - so a value set here does not read back identically." } }, "required": [ "action", "percent" ], "additionalProperties": false }, { "type": "object", "title": "ControlPrint", "description": "Pause, resume or stop the print the printer is currently running. Confirmed live on a P1P (2026-10-11): all three are accepted and a stop during PREPARE/RUNNING moved the printer to FAILED, which is how Bambu reports an operator-cancelled print. Note what the printer's acceptance does not mean - all three answered success while it was IDLE with nothing to pause, so a caller that needs to know the state actually changed must read telemetry back rather than trust the command result.", "properties": { "action": { "const": "control_print" }, "control": { "enum": [ "pause", "resume", "stop" ], "description": "Stop is a cancel, not a pause: it ends the print and it cannot be undone. A UI should treat it accordingly." } }, "required": [ "action", "control" ], "additionalProperties": false }, { "type": "object", "title": "SendGcodeLine", "properties": { "action": { "const": "send_gcode_line" }, "line": { "type": "string", "description": "A single raw gcode line. By far the most powerful variant, and the one to be most careful about ever exposing to a UI." } }, "required": [ "action", "line" ], "additionalProperties": false } ] }