{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://schemas.continuum.dev/continuum/v1/edge/discovered_printer.schema.json", "title": "DiscoveredPrinter", "description": "A printer a gateway found on its own network or USB ports that is NOT in its fleet file - so it is a candidate for an operator to add, not a printer the gateway can drive. It deliberately carries no credential: a Bambu access code is on the printer's screen and a PrusaLink API key is in its web UI, and no probe can learn either. The point of sending these to the cloud is that an operator adding a printer in the web app should not have to go and read an IP address or a 15-character serial off a machine. Every string here is reported by a device on the customer's network and must be treated as untrusted display text - hence the length caps - because anything that answers an SSDP query or an HTTP probe chooses its own name.", "type": "object", "properties": { "vendor": { "description": "The `vendor` value this candidate would need in the gateway's fleet file. `octoprint` rather than `prusa_link` is the honest answer when a probe saw only the OctoPrint API, which PrusaLink also serves - see `ambiguous_with`.", "type": "string", "enum": ["bambu", "klipper", "prusa_link", "octoprint", "prusa_serial"] }, "address": { "description": "Where the gateway found it: an IP or hostname for a network printer, or a device path like `/dev/ttyACM0` for one on a serial port. Not a URL, and not reachable from the cloud - it is an address on the gateway's network, shown so an operator can recognise which machine this is.", "type": "string", "minLength": 1, "maxLength": 255 }, "serial": { "description": "The printer's serial number where the probe could learn it without a credential - which today means a Bambu printer's TLS certificate CN, or a Marlin board's `UUID`. Absent is normal and not an error.", "type": "string", "maxLength": 64 }, "model": { "description": "The vendor's own model code or machine type, e.g. Bambu's `O1C2` or Marlin's `MACHINE_TYPE`.", "type": "string", "maxLength": 128 }, "name": { "description": "The name the printer reports for itself, which is usually whatever the operator typed into their slicer or set as the host's hostname. Display only.", "type": "string", "maxLength": 128 }, "detail": { "description": "Free-form human detail to recognise the machine by - a firmware version, a USB vendor string, or why a probe could not say more. Never parsed, and never written into a config file.", "type": "string", "maxLength": 256 }, "ambiguous_with": { "description": "A vendor this could equally be, when the probe genuinely cannot tell. Today only `prusa_link` versus `octoprint`: PrusaLink implements OctoPrint's API, so without a credential nothing on the network distinguishes them. Present means the operator has to choose; the web app should offer both rather than picking one.", "type": "string", "enum": ["bambu", "klipper", "prusa_link", "octoprint", "prusa_serial"] }, "source": { "description": "Which probe found it, which bounds how much it can know: `certificate` and `usb_descriptor` are passive reads, `ssdp` is an unsolicited announcement, `http_api` an identity endpoint, and `serial_handshake` an `M115` that reset the board to ask.", "type": "string", "enum": ["ssdp", "certificate", "http_api", "usb_descriptor", "serial_handshake"] }, "driver_ready": { "description": "Whether this gateway build can actually drive this vendor. False means the printer was found and reported but its client is still a stub, so adding it would produce a printer that connects and does nothing - the web app should say so rather than offering it as ready to use.", "type": "boolean" } }, "required": ["vendor", "address", "source", "driver_ready"] }