{
  "components": {
    "schemas": {
      "Axis": {
        "additionalProperties": false,
        "properties": {
          "continuous": {
            "default": false,
            "description": "Rotary axes only: turns without limit, so min and max are not enforced.",
            "title": "Continuous",
            "type": "boolean"
          },
          "home": {
            "default": 0.0,
            "description": "Position after homing. May lie outside the soft limits: the firmware homes against a switch that moves can then not return to.",
            "title": "Home",
            "type": "number"
          },
          "kind": {
            "default": "linear",
            "enum": [
              "linear",
              "rotary"
            ],
            "title": "Kind",
            "type": "string"
          },
          "max": {
            "description": "Soft limit. Ignored for continuous axes.",
            "title": "Max",
            "type": "number"
          },
          "max_speed": {
            "description": "Units per second (mm/s or deg/s).",
            "exclusiveMinimum": 0.0,
            "title": "Max Speed",
            "type": "number"
          },
          "min": {
            "description": "Soft limit. Ignored for continuous axes.",
            "title": "Min",
            "type": "number"
          },
          "name": {
            "maxLength": 8,
            "minLength": 1,
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name",
          "kind",
          "min",
          "max",
          "home",
          "max_speed",
          "continuous"
        ],
        "title": "Axis",
        "type": "object"
      },
      "Camera": {
        "additionalProperties": false,
        "properties": {
          "field_of_view": {
            "$ref": "#/components/schemas/Point",
            "description": "Width (x) and height (y) of the view in mm."
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "light_output": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Light Output"
          },
          "looking": {
            "enum": [
              "down",
              "up"
            ],
            "title": "Looking",
            "type": "string"
          },
          "mount": {
            "enum": [
              "head",
              "fixed"
            ],
            "title": "Mount",
            "type": "string"
          },
          "position": {
            "$ref": "#/components/schemas/Point",
            "description": "Head cameras: offset from the head. Fixed cameras: machine coordinates."
          },
          "settle_s": {
            "default": 0.25,
            "description": "Wait this long after a move before using a frame (vibration, latency).",
            "minimum": 0.0,
            "title": "Settle S",
            "type": "number"
          }
        },
        "required": [
          "id",
          "label",
          "mount",
          "position",
          "looking",
          "field_of_view",
          "light_output",
          "settle_s"
        ],
        "title": "Camera",
        "type": "object"
      },
      "CameraInfo": {
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the camera is not delivering frames, if so.",
            "title": "Error"
          },
          "height": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Height"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "looking": {
            "enum": [
              "down",
              "up"
            ],
            "title": "Looking",
            "type": "string"
          },
          "source": {
            "enum": [
              "synthetic",
              "device",
              "none"
            ],
            "title": "Source",
            "type": "string"
          },
          "streaming": {
            "description": "Frames are being captured for at least one viewer.",
            "title": "Streaming",
            "type": "boolean"
          },
          "width": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Native frame width, when the camera is on.",
            "title": "Width"
          }
        },
        "required": [
          "id",
          "label",
          "looking",
          "source",
          "width",
          "height",
          "streaming",
          "error"
        ],
        "title": "CameraInfo",
        "type": "object"
      },
      "ClientInfo": {
        "properties": {
          "address": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Network address the client connected from.",
            "title": "Address"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "name": {
            "description": "Name the client gave when connecting, such as 'Studio'.",
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "address"
        ],
        "title": "ClientInfo",
        "type": "object"
      },
      "ClientMessage": {
        "description": "Heartbeat. Clients send one every couple of seconds; the server answers with pong.",
        "properties": {
          "type": {
            "const": "ping",
            "default": "ping",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "PingMessage",
        "type": "object"
      },
      "ErrorEvent": {
        "properties": {
          "message": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "const": "error",
            "default": "error",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "message"
        ],
        "title": "ErrorEvent",
        "type": "object"
      },
      "ErrorResponse": {
        "properties": {
          "code": {
            "title": "Code",
            "type": "string"
          },
          "detail": {
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "detail",
          "code"
        ],
        "title": "ErrorResponse",
        "type": "object"
      },
      "FeederBank": {
        "additionalProperties": false,
        "description": "A row of evenly spaced feeder slots, such as one LumenPnP feeder rail.",
        "properties": {
          "count": {
            "minimum": 1.0,
            "title": "Count",
            "type": "integer"
          },
          "first_pick": {
            "$ref": "#/components/schemas/Point",
            "description": "Pick location of the first slot."
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "length": {
            "description": "Feeder length along the tape, in mm.",
            "exclusiveMinimum": 0.0,
            "title": "Length",
            "type": "number"
          },
          "pitch": {
            "$ref": "#/components/schemas/Point",
            "description": "Step from one slot's pick location to the next."
          },
          "rotation": {
            "default": 0,
            "description": "Tape direction in degrees; 90 means parts feed toward +Y.",
            "title": "Rotation",
            "type": "number"
          },
          "width": {
            "description": "Feeder width across the tape, in mm.",
            "exclusiveMinimum": 0.0,
            "title": "Width",
            "type": "number"
          }
        },
        "required": [
          "id",
          "label",
          "first_pick",
          "pitch",
          "count",
          "width",
          "length",
          "rotation"
        ],
        "title": "FeederBank",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "HealthResponse": {
        "properties": {
          "protocol": {
            "title": "Protocol",
            "type": "integer"
          },
          "status": {
            "const": "ok",
            "default": "ok",
            "title": "Status",
            "type": "string"
          },
          "version": {
            "title": "Version",
            "type": "string"
          }
        },
        "required": [
          "status",
          "version",
          "protocol"
        ],
        "title": "HealthResponse",
        "type": "object"
      },
      "HelloEvent": {
        "description": "First message on every connection.",
        "properties": {
          "client_id": {
            "title": "Client Id",
            "type": "string"
          },
          "lease": {
            "$ref": "#/components/schemas/LeaseState"
          },
          "machine": {
            "$ref": "#/components/schemas/MachineState"
          },
          "presence": {
            "$ref": "#/components/schemas/PresenceState"
          },
          "protocol": {
            "title": "Protocol",
            "type": "integer"
          },
          "server_version": {
            "title": "Server Version",
            "type": "string"
          },
          "type": {
            "const": "hello",
            "default": "hello",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "protocol",
          "server_version",
          "client_id",
          "machine",
          "presence",
          "lease"
        ],
        "title": "HelloEvent",
        "type": "object"
      },
      "Input": {
        "additionalProperties": false,
        "properties": {
          "axis": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Home sensors: the axis they home.",
            "title": "Axis"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "enum": [
              "digital",
              "analog"
            ],
            "title": "Kind",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "range": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ValueRange"
              },
              {
                "type": "null"
              }
            ],
            "description": "Analog inputs: the display range."
          },
          "role": {
            "enum": [
              "home",
              "safe_z",
              "vacuum",
              "other"
            ],
            "title": "Role",
            "type": "string"
          },
          "threshold": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Vacuum inputs: readings at or past this value, going toward range.max, mean a part is held.",
            "title": "Threshold"
          },
          "unit": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Unit"
          }
        },
        "required": [
          "id",
          "label",
          "kind",
          "role",
          "axis",
          "unit",
          "range",
          "threshold"
        ],
        "title": "Input",
        "type": "object"
      },
      "JogRequest": {
        "properties": {
          "axis": {
            "title": "Axis",
            "type": "string"
          },
          "distance": {
            "description": "Relative distance, mm or degrees. Clamped to limits.",
            "title": "Distance",
            "type": "number"
          },
          "speed": {
            "anyOf": [
              {
                "exclusiveMinimum": 0.0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Speed"
          }
        },
        "required": [
          "axis",
          "distance"
        ],
        "title": "JogRequest",
        "type": "object"
      },
      "KeepOut": {
        "additionalProperties": false,
        "description": "An area where boards cannot go, such as the bottom camera or a fiducial mount.",
        "properties": {
          "area": {
            "$ref": "#/components/schemas/Rect"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "area"
        ],
        "title": "KeepOut",
        "type": "object"
      },
      "LeaseEvent": {
        "properties": {
          "lease": {
            "$ref": "#/components/schemas/LeaseState"
          },
          "type": {
            "const": "lease",
            "default": "lease",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "lease"
        ],
        "title": "LeaseEvent",
        "type": "object"
      },
      "LeaseRequest": {
        "properties": {
          "force": {
            "default": false,
            "description": "Take the lease even if another client holds it. Ask the user first.",
            "title": "Force",
            "type": "boolean"
          }
        },
        "title": "LeaseRequest",
        "type": "object"
      },
      "LeaseState": {
        "properties": {
          "holder": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ClientInfo"
              },
              {
                "type": "null"
              }
            ],
            "description": "The client allowed to move the machine, or null if no one holds it."
          }
        },
        "required": [
          "holder"
        ],
        "title": "LeaseState",
        "type": "object"
      },
      "Location": {
        "additionalProperties": false,
        "properties": {
          "diameter": {
            "anyOf": [
              {
                "exclusiveMinimum": 0.0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Fiducials: the round mark's diameter in mm.",
            "title": "Diameter"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "enum": [
              "homing_fiducial",
              "fiducial",
              "tip_holder",
              "discard",
              "park"
            ],
            "title": "Kind",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "position": {
            "$ref": "#/components/schemas/Point"
          },
          "style": {
            "default": "dot",
            "description": "How the simulator draws a fiducial: a plain dot, or the LumenPnP datum board (a 1 mm copper dot in a reticle, with rings, a 5 mm grid, and corner fiducials).",
            "enum": [
              "dot",
              "lumenpnp-datum"
            ],
            "title": "Style",
            "type": "string"
          },
          "z": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Height of the surface, where it matters (fiducials).",
            "title": "Z"
          }
        },
        "required": [
          "id",
          "label",
          "kind",
          "position",
          "z",
          "diameter",
          "style"
        ],
        "title": "Location",
        "type": "object"
      },
      "MachineProfile": {
        "additionalProperties": false,
        "properties": {
          "axes": {
            "items": {
              "$ref": "#/components/schemas/Axis"
            },
            "minItems": 1,
            "title": "Axes",
            "type": "array"
          },
          "bed": {
            "$ref": "#/components/schemas/Rect",
            "description": "Outline of the machine bed, for drawing."
          },
          "cameras": {
            "items": {
              "$ref": "#/components/schemas/Camera"
            },
            "title": "Cameras",
            "type": "array"
          },
          "feeder_banks": {
            "items": {
              "$ref": "#/components/schemas/FeederBank"
            },
            "title": "Feeder Banks",
            "type": "array"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "inputs": {
            "items": {
              "$ref": "#/components/schemas/Input"
            },
            "title": "Inputs",
            "type": "array"
          },
          "keep_outs": {
            "description": "Areas inside the placement area where boards cannot be mounted.",
            "items": {
              "$ref": "#/components/schemas/KeepOut"
            },
            "title": "Keep Outs",
            "type": "array"
          },
          "locations": {
            "items": {
              "$ref": "#/components/schemas/Location"
            },
            "title": "Locations",
            "type": "array"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "nozzle_reach": {
            "additionalProperties": {
              "$ref": "#/components/schemas/Rect"
            },
            "description": "Where each nozzle can reach: the head's X/Y travel shifted by its offset.",
            "readOnly": true,
            "title": "Nozzle Reach",
            "type": "object"
          },
          "nozzles": {
            "items": {
              "$ref": "#/components/schemas/Nozzle"
            },
            "title": "Nozzles",
            "type": "array"
          },
          "outputs": {
            "items": {
              "$ref": "#/components/schemas/Output"
            },
            "title": "Outputs",
            "type": "array"
          },
          "placement_area": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Rect"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where boards can go so that every nozzle can place anywhere on them: the\nnozzles' reach, all of them, within the staging plates (or the work area). Boards\nmust also stay clear of the keep-outs inside it.",
            "readOnly": true
          },
          "provisional": {
            "default": false,
            "description": "Dimensions are estimates that have not been measured on a real machine.",
            "title": "Provisional",
            "type": "boolean"
          },
          "safe_z": {
            "description": "A nozzle at or above this height (on its own Z scale) is safe for travel.",
            "title": "Safe Z",
            "type": "number"
          },
          "safe_z_input": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "A digital input that reads true when every nozzle is at safe height.",
            "title": "Safe Z Input"
          },
          "staging_plates": {
            "items": {
              "$ref": "#/components/schemas/StagingPlate"
            },
            "title": "Staging Plates",
            "type": "array"
          },
          "visual_homing": {
            "default": false,
            "description": "After homing, center the top camera on the homing fiducial and correct the X/Y coordinates to its known position.",
            "title": "Visual Homing",
            "type": "boolean"
          },
          "work_area": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Rect"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where boards go."
          },
          "x_points": {
            "default": "right",
            "description": "Which way +X points for an operator facing the machine.",
            "enum": [
              "right",
              "left"
            ],
            "title": "X Points",
            "type": "string"
          },
          "y_points": {
            "default": "back",
            "description": "Which way +Y points for an operator facing the machine.",
            "enum": [
              "back",
              "front"
            ],
            "title": "Y Points",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "provisional",
          "bed",
          "work_area",
          "axes",
          "safe_z",
          "x_points",
          "y_points",
          "visual_homing",
          "safe_z_input",
          "nozzles",
          "cameras",
          "locations",
          "feeder_banks",
          "staging_plates",
          "keep_outs",
          "inputs",
          "outputs",
          "nozzle_reach",
          "placement_area"
        ],
        "title": "MachineProfile",
        "type": "object"
      },
      "MachineState": {
        "properties": {
          "axes": {
            "items": {
              "type": "string"
            },
            "title": "Axes",
            "type": "array"
          },
          "busy": {
            "description": "A motion operation is in progress.",
            "title": "Busy",
            "type": "boolean"
          },
          "connected": {
            "title": "Connected",
            "type": "boolean"
          },
          "connection_error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the last attempt to connect to the machine failed, if it did.",
            "title": "Connection Error"
          },
          "driver": {
            "title": "Driver",
            "type": "string"
          },
          "estop": {
            "description": "An emergency stop is latched; reset before moving.",
            "title": "Estop",
            "type": "boolean"
          },
          "firmware": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Firmware name and version, when known.",
            "title": "Firmware"
          },
          "homed": {
            "title": "Homed",
            "type": "boolean"
          },
          "inputs": {
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "number"
                }
              ]
            },
            "description": "Reading per profile input: bool for digital, number for analog.",
            "title": "Inputs",
            "type": "object"
          },
          "motion_enabled": {
            "description": "Motion commands are currently allowed (connected, client present, no E-stop).",
            "title": "Motion Enabled",
            "type": "boolean"
          },
          "outputs": {
            "additionalProperties": {
              "type": "boolean"
            },
            "description": "State per profile output.",
            "title": "Outputs",
            "type": "object"
          },
          "position": {
            "additionalProperties": {
              "type": "number"
            },
            "description": "Current position per axis, mm or degrees.",
            "title": "Position",
            "type": "object"
          },
          "speed_scale": {
            "description": "Moves run at no more than this fraction of full speed (bring-up mode).",
            "title": "Speed Scale",
            "type": "number"
          },
          "warnings": {
            "description": "Problems worth showing the operator, such as a firmware mismatch.",
            "items": {
              "type": "string"
            },
            "title": "Warnings",
            "type": "array"
          },
          "z_safe": {
            "description": "Every nozzle is at safe travel height. From the safe-Z input when the profile has one, otherwise computed from the Z position.",
            "title": "Z Safe",
            "type": "boolean"
          }
        },
        "required": [
          "driver",
          "firmware",
          "connected",
          "connection_error",
          "homed",
          "estop",
          "busy",
          "motion_enabled",
          "axes",
          "position",
          "inputs",
          "outputs",
          "speed_scale",
          "warnings",
          "z_safe"
        ],
        "title": "MachineState",
        "type": "object"
      },
      "MachineStateEvent": {
        "properties": {
          "machine": {
            "$ref": "#/components/schemas/MachineState"
          },
          "type": {
            "const": "machine.state",
            "default": "machine.state",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "machine"
        ],
        "title": "MachineStateEvent",
        "type": "object"
      },
      "MarlinInput": {
        "additionalProperties": false,
        "properties": {
          "pattern": {
            "description": "Regular expression with a 'value' group, matched against the reply.",
            "title": "Pattern",
            "type": "string"
          },
          "read": {
            "description": "Commands to send; the value is in the last one's reply.",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "title": "Read",
            "type": "array"
          },
          "scale": {
            "default": 1.0,
            "description": "Multiply the (signed) value by this.",
            "title": "Scale",
            "type": "number"
          },
          "sign_bits": {
            "anyOf": [
              {
                "maximum": 64.0,
                "minimum": 2.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Treat the raw value as a two's-complement number of this many bits.",
            "title": "Sign Bits"
          }
        },
        "required": [
          "read",
          "pattern"
        ],
        "title": "MarlinInput",
        "type": "object"
      },
      "MarlinInputOverride": {
        "additionalProperties": false,
        "properties": {
          "clear_sign_bits": {
            "default": false,
            "description": "Remove the base input's sign_bits instead of setting it.",
            "title": "Clear Sign Bits",
            "type": "boolean"
          },
          "pattern": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pattern"
          },
          "read": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Read"
          },
          "scale": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Scale"
          },
          "sign_bits": {
            "anyOf": [
              {
                "maximum": 64.0,
                "minimum": 2.0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sign Bits"
          }
        },
        "title": "MarlinInputOverride",
        "type": "object"
      },
      "MarlinOutput": {
        "additionalProperties": false,
        "properties": {
          "off": {
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "title": "Off",
            "type": "array"
          },
          "on": {
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "title": "On",
            "type": "array"
          }
        },
        "required": [
          "on",
          "off"
        ],
        "title": "MarlinOutput",
        "type": "object"
      },
      "MarlinSettings": {
        "additionalProperties": false,
        "properties": {
          "connect": {
            "description": "Sent after the firmware answers M115.",
            "items": {
              "type": "string"
            },
            "title": "Connect",
            "type": "array"
          },
          "default_variant": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Default Variant"
          },
          "estop": {
            "default": "M112",
            "description": "Immediate stop; firmware halts.",
            "title": "Estop",
            "type": "string"
          },
          "expected_machine_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "MACHINE_TYPE that M115 should report; others get a warning.",
            "title": "Expected Machine Type"
          },
          "home": {
            "items": {
              "type": "string"
            },
            "title": "Home",
            "type": "array"
          },
          "inputs": {
            "additionalProperties": {
              "$ref": "#/components/schemas/MarlinInput"
            },
            "title": "Inputs",
            "type": "object"
          },
          "outputs": {
            "additionalProperties": {
              "$ref": "#/components/schemas/MarlinOutput"
            },
            "title": "Outputs",
            "type": "object"
          },
          "stop": {
            "default": "M410",
            "description": "Quick stop: end motion, stay running.",
            "title": "Stop",
            "type": "string"
          },
          "variants": {
            "additionalProperties": {
              "$ref": "#/components/schemas/MarlinVariant"
            },
            "title": "Variants",
            "type": "object"
          }
        },
        "title": "MarlinSettings",
        "type": "object"
      },
      "MarlinVariant": {
        "additionalProperties": false,
        "properties": {
          "description": {
            "title": "Description",
            "type": "string"
          },
          "inputs": {
            "additionalProperties": {
              "$ref": "#/components/schemas/MarlinInputOverride"
            },
            "title": "Inputs",
            "type": "object"
          }
        },
        "required": [
          "description"
        ],
        "title": "MarlinVariant",
        "type": "object"
      },
      "MoveRequest": {
        "properties": {
          "position": {
            "additionalProperties": {
              "type": "number"
            },
            "description": "Target per axis. Axes left out do not move.",
            "minProperties": 1,
            "title": "Position",
            "type": "object"
          },
          "speed": {
            "anyOf": [
              {
                "exclusiveMinimum": 0.0,
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Speed limit in units per second. Defaults to axis max.",
            "title": "Speed"
          }
        },
        "required": [
          "position"
        ],
        "title": "MoveRequest",
        "type": "object"
      },
      "Nozzle": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "offset": {
            "$ref": "#/components/schemas/Point",
            "description": "Position relative to the head (top camera center)."
          },
          "pump_output": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Pump Output"
          },
          "rotation_axis": {
            "title": "Rotation Axis",
            "type": "string"
          },
          "vacuum_input": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Vacuum Input"
          },
          "valve_output": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Valve Output"
          },
          "z_axis": {
            "title": "Z Axis",
            "type": "string"
          },
          "z_sign": {
            "default": 1,
            "description": "Direction of this nozzle on its Z axis. Two nozzles sharing one Z motor move in opposite directions: one has 1, the other -1.",
            "enum": [
              1,
              -1
            ],
            "title": "Z Sign",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "label",
          "offset",
          "z_axis",
          "z_sign",
          "rotation_axis",
          "vacuum_input",
          "pump_output",
          "valve_output"
        ],
        "title": "Nozzle",
        "type": "object"
      },
      "Operation": {
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Error"
          },
          "finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Finished At"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "title": "Kind",
            "type": "string"
          },
          "message": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "What the operation found or did, for people.",
            "title": "Message"
          },
          "started_at": {
            "format": "date-time",
            "title": "Started At",
            "type": "string"
          },
          "status": {
            "enum": [
              "running",
              "succeeded",
              "failed",
              "cancelled"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "id",
          "kind",
          "status",
          "error",
          "message",
          "started_at",
          "finished_at"
        ],
        "title": "Operation",
        "type": "object"
      },
      "OperationEvent": {
        "properties": {
          "operation": {
            "$ref": "#/components/schemas/Operation"
          },
          "type": {
            "const": "operation",
            "default": "operation",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "operation"
        ],
        "title": "OperationEvent",
        "type": "object"
      },
      "Output": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "role": {
            "enum": [
              "pump",
              "valve",
              "light",
              "other"
            ],
            "title": "Role",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "role"
        ],
        "title": "Output",
        "type": "object"
      },
      "OutputRequest": {
        "properties": {
          "on": {
            "title": "On",
            "type": "boolean"
          }
        },
        "required": [
          "on"
        ],
        "title": "OutputRequest",
        "type": "object"
      },
      "Point": {
        "additionalProperties": false,
        "properties": {
          "x": {
            "title": "X",
            "type": "number"
          },
          "y": {
            "title": "Y",
            "type": "number"
          }
        },
        "required": [
          "x",
          "y"
        ],
        "title": "Point",
        "type": "object"
      },
      "PongEvent": {
        "properties": {
          "server_time": {
            "format": "date-time",
            "title": "Server Time",
            "type": "string"
          },
          "type": {
            "const": "pong",
            "default": "pong",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "server_time"
        ],
        "title": "PongEvent",
        "type": "object"
      },
      "PresenceEvent": {
        "properties": {
          "presence": {
            "$ref": "#/components/schemas/PresenceState"
          },
          "type": {
            "const": "presence",
            "default": "presence",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "presence"
        ],
        "title": "PresenceEvent",
        "type": "object"
      },
      "PresenceLostEvent": {
        "description": "No client has been connected for the grace period. Running jobs pause.",
        "properties": {
          "grace_period_s": {
            "title": "Grace Period S",
            "type": "number"
          },
          "type": {
            "const": "presence.lost",
            "default": "presence.lost",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "grace_period_s"
        ],
        "title": "PresenceLostEvent",
        "type": "object"
      },
      "PresenceState": {
        "properties": {
          "clients": {
            "description": "Clients with a live event-socket connection.",
            "title": "Clients",
            "type": "integer"
          },
          "present": {
            "title": "Present",
            "type": "boolean"
          }
        },
        "required": [
          "clients",
          "present"
        ],
        "title": "PresenceState",
        "type": "object"
      },
      "Rect": {
        "additionalProperties": false,
        "properties": {
          "x_max": {
            "title": "X Max",
            "type": "number"
          },
          "x_min": {
            "title": "X Min",
            "type": "number"
          },
          "y_max": {
            "title": "Y Max",
            "type": "number"
          },
          "y_min": {
            "title": "Y Min",
            "type": "number"
          }
        },
        "required": [
          "x_min",
          "y_min",
          "x_max",
          "y_max"
        ],
        "title": "Rect",
        "type": "object"
      },
      "ServerEvent": {
        "discriminator": {
          "mapping": {
            "error": "#/components/schemas/ErrorEvent",
            "hello": "#/components/schemas/HelloEvent",
            "lease": "#/components/schemas/LeaseEvent",
            "machine.state": "#/components/schemas/MachineStateEvent",
            "operation": "#/components/schemas/OperationEvent",
            "pong": "#/components/schemas/PongEvent",
            "presence": "#/components/schemas/PresenceEvent",
            "presence.lost": "#/components/schemas/PresenceLostEvent"
          },
          "propertyName": "type"
        },
        "oneOf": [
          {
            "$ref": "#/components/schemas/HelloEvent"
          },
          {
            "$ref": "#/components/schemas/MachineStateEvent"
          },
          {
            "$ref": "#/components/schemas/PresenceEvent"
          },
          {
            "$ref": "#/components/schemas/PresenceLostEvent"
          },
          {
            "$ref": "#/components/schemas/LeaseEvent"
          },
          {
            "$ref": "#/components/schemas/OperationEvent"
          },
          {
            "$ref": "#/components/schemas/PongEvent"
          },
          {
            "$ref": "#/components/schemas/ErrorEvent"
          }
        ]
      },
      "StagingPlate": {
        "additionalProperties": false,
        "description": "A plate that boards and trays mount on, with a grid of tapped holes.",
        "properties": {
          "center": {
            "$ref": "#/components/schemas/Point"
          },
          "checkerboard": {
            "default": true,
            "description": "Holes only where column + row (from the center) is even.",
            "title": "Checkerboard",
            "type": "boolean"
          },
          "corner_hole_diameter": {
            "default": 5.3,
            "title": "Corner Hole Diameter",
            "type": "number"
          },
          "corner_holes": {
            "description": "Large mounting holes, relative to the center; grid holes near them are left out.",
            "items": {
              "$ref": "#/components/schemas/Point"
            },
            "title": "Corner Holes",
            "type": "array"
          },
          "hole_columns": {
            "description": "Columns of the grid, centered on the plate.",
            "minimum": 1.0,
            "title": "Hole Columns",
            "type": "integer"
          },
          "hole_diameter": {
            "exclusiveMinimum": 0.0,
            "title": "Hole Diameter",
            "type": "number"
          },
          "hole_pitch": {
            "description": "Spacing of the hole grid, in mm.",
            "exclusiveMinimum": 0.0,
            "title": "Hole Pitch",
            "type": "number"
          },
          "hole_rows": {
            "minimum": 1.0,
            "title": "Hole Rows",
            "type": "integer"
          },
          "holes": {
            "description": "Grid hole centers, in machine coordinates.",
            "items": {
              "$ref": "#/components/schemas/Point"
            },
            "readOnly": true,
            "title": "Holes",
            "type": "array"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "knockout_diameter": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "A round opening at the center (for a bottom camera); grid holes inside are left out.",
            "title": "Knockout Diameter"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "size": {
            "$ref": "#/components/schemas/Point"
          }
        },
        "required": [
          "id",
          "label",
          "center",
          "size",
          "hole_pitch",
          "hole_diameter",
          "hole_columns",
          "hole_rows",
          "checkerboard",
          "corner_holes",
          "corner_hole_diameter",
          "knockout_diameter",
          "holes"
        ],
        "title": "StagingPlate",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      },
      "ValueRange": {
        "additionalProperties": false,
        "description": "A display range. ``max`` may be below ``min`` for readings that grow more negative,\nsuch as raw vacuum sensor counts.",
        "properties": {
          "max": {
            "title": "Max",
            "type": "number"
          },
          "min": {
            "title": "Min",
            "type": "number"
          }
        },
        "required": [
          "min",
          "max"
        ],
        "title": "ValueRange",
        "type": "object"
      }
    },
    "securitySchemes": {
      "HTTPBearer": {
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "REST API for PicnicPlace Core. Live state streams over the WebSocket at `/api/v1/events`; its messages are described by the `ServerEvent` and `ClientMessage` schemas.",
    "title": "PicnicPlace Core API",
    "version": "0.1.0.dev0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/cameras": {
      "get": {
        "operationId": "list_cameras_api_v1_cameras_get",
        "parameters": [
          {
            "description": "API token, for clients that cannot send headers.",
            "in": "query",
            "name": "token",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "API token, for clients that cannot send headers.",
              "title": "Token"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/CameraInfo"
                  },
                  "title": "Response List Cameras Api V1 Cameras Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "List Cameras",
        "tags": [
          "cameras"
        ]
      }
    },
    "/api/v1/cameras/{camera_id}/snapshot.jpg": {
      "get": {
        "description": "The latest frame as a JPEG. The ``X-Timestamp`` header says when it was captured.",
        "operationId": "snapshot_api_v1_cameras__camera_id__snapshot_jpg_get",
        "parameters": [
          {
            "in": "path",
            "name": "camera_id",
            "required": true,
            "schema": {
              "title": "Camera Id",
              "type": "string"
            }
          },
          {
            "description": "Scale frames down to this width.",
            "in": "query",
            "name": "width",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maximum": 3840,
                  "minimum": 16,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Scale frames down to this width.",
              "title": "Width"
            }
          },
          {
            "description": "JPEG quality.",
            "in": "query",
            "name": "quality",
            "required": false,
            "schema": {
              "default": 85,
              "description": "JPEG quality.",
              "maximum": 95,
              "minimum": 10,
              "title": "Quality",
              "type": "integer"
            }
          },
          {
            "description": "API token, for clients that cannot send headers.",
            "in": "query",
            "name": "token",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "API token, for clients that cannot send headers.",
              "title": "Token"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "image/jpeg": {}
            },
            "description": "One frame."
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No such camera."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The camera is off or failing."
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Snapshot",
        "tags": [
          "cameras"
        ]
      }
    },
    "/api/v1/cameras/{camera_id}/stream.mjpg": {
      "get": {
        "description": "MJPEG for ``<img>`` tags, scripts, and small displays (``?width=320&fps=5``).",
        "operationId": "stream_api_v1_cameras__camera_id__stream_mjpg_get",
        "parameters": [
          {
            "in": "path",
            "name": "camera_id",
            "required": true,
            "schema": {
              "title": "Camera Id",
              "type": "string"
            }
          },
          {
            "description": "Scale frames down to this width.",
            "in": "query",
            "name": "width",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maximum": 3840,
                  "minimum": 16,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Scale frames down to this width.",
              "title": "Width"
            }
          },
          {
            "description": "JPEG quality.",
            "in": "query",
            "name": "quality",
            "required": false,
            "schema": {
              "default": 75,
              "description": "JPEG quality.",
              "maximum": 95,
              "minimum": 10,
              "title": "Quality",
              "type": "integer"
            }
          },
          {
            "description": "Maximum frame rate.",
            "in": "query",
            "name": "fps",
            "required": false,
            "schema": {
              "default": 15,
              "description": "Maximum frame rate.",
              "exclusiveMinimum": 0,
              "maximum": 60,
              "title": "Fps",
              "type": "number"
            }
          },
          {
            "description": "Stop after this many frames.",
            "in": "query",
            "name": "frames",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "minimum": 1,
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Stop after this many frames.",
              "title": "Frames"
            }
          },
          {
            "description": "API token, for clients that cannot send headers.",
            "in": "query",
            "name": "token",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "API token, for clients that cannot send headers.",
              "title": "Token"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "multipart/x-mixed-replace; boundary=picnicplace-frame": {}
            },
            "description": "An endless MJPEG stream. Each part carries X-Timestamp (capture time, Unix seconds) and X-Frame (frame number) headers."
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No such camera."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The camera is off or failing."
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Stream",
        "tags": [
          "cameras"
        ]
      }
    },
    "/api/v1/health": {
      "get": {
        "description": "Liveness check. Does not require authentication.",
        "operationId": "health_api_v1_health_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Health",
        "tags": [
          "system"
        ]
      }
    },
    "/api/v1/lease": {
      "delete": {
        "description": "Give up control of the machine, if this client holds it.",
        "operationId": "release_lease_api_v1_lease_delete",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeaseState"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Release Lease",
        "tags": [
          "lease"
        ]
      },
      "get": {
        "operationId": "get_lease_api_v1_lease_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeaseState"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Lease",
        "tags": [
          "lease"
        ]
      },
      "post": {
        "description": "Take control of the machine. With ``force``, take it from another client.",
        "operationId": "acquire_lease_api_v1_lease_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LeaseRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeaseState"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Acquire Lease",
        "tags": [
          "lease"
        ]
      }
    },
    "/api/v1/machine": {
      "get": {
        "operationId": "get_machine_api_v1_machine_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineState"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Machine",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/camera-scale": {
      "post": {
        "description": "Measure the top camera's millimeters per pixel by moving over the homing fiducial.\nThe result is reported in the operation's message; it is not saved yet.",
        "operationId": "measure_camera_scale_api_v1_machine_camera_scale_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Measure Camera Scale",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/connect": {
      "post": {
        "description": "Connect to the machine if it is not connected (for example, after it was off).",
        "operationId": "connect_machine_api_v1_machine_connect_post",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineState"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Connect Machine",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/description": {
      "get": {
        "description": "The machine profile: geometry, nozzles, cameras, feeders, and the list of I/O.",
        "operationId": "get_machine_description_api_v1_machine_description_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineProfile"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Machine Description",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/estop": {
      "post": {
        "description": "Emergency stop. Allowed for any authenticated client, in any state.",
        "operationId": "estop_api_v1_machine_estop_post",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineState"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Estop",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/home": {
      "post": {
        "description": "Start homing. Returns an operation; follow it over the event socket.",
        "operationId": "home_api_v1_machine_home_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Home",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/jog": {
      "post": {
        "description": "Move one axis by a relative distance, clamped to its limits. X and Y jogs are\nrefused while a nozzle is below safe Z.",
        "operationId": "jog_api_v1_machine_jog_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JogRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The axis is unknown."
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Jog",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/move": {
      "post": {
        "description": "Start an absolute move. Returns an operation; follow it over the event socket.",
        "operationId": "move_api_v1_machine_move_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The target is invalid or out of limits."
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Move",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/outputs/{output_id}": {
      "put": {
        "description": "Switch an output on or off. Turning one on requires motion to be enabled.",
        "operationId": "set_output_api_v1_machine_outputs__output_id__put",
        "parameters": [
          {
            "in": "path",
            "name": "output_id",
            "required": true,
            "schema": {
              "title": "Output Id",
              "type": "string"
            }
          },
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OutputRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineState"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No output with this ID."
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Set Output",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/reset": {
      "post": {
        "description": "Clear a latched emergency stop. The machine must be homed again afterward.",
        "operationId": "reset_api_v1_machine_reset_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MachineState"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Reset",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/machine/visual-home": {
      "post": {
        "description": "Find the homing fiducial with the top camera and correct X and Y. Homing does\nthis by itself when the profile enables visual homing; this runs it again.",
        "operationId": "visual_home_api_v1_machine_visual_home_post",
        "parameters": [
          {
            "description": "Client ID from the event socket's hello message.",
            "in": "header",
            "name": "X-PicnicPlace-Client",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Client ID from the event socket's hello message.",
              "title": "X-Picnicplace-Client"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            },
            "description": "Successful Response"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "The machine cannot do this right now, or this client does not hold the control lease."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Visual Home",
        "tags": [
          "machine"
        ]
      }
    },
    "/api/v1/operations/{operation_id}": {
      "get": {
        "operationId": "get_operation_api_v1_operations__operation_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "operation_id",
            "required": true,
            "schema": {
              "title": "Operation Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            },
            "description": "Successful Response"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            },
            "description": "No operation with this ID."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Operation",
        "tags": [
          "operations"
        ]
      }
    },
    "/api/v1/presence": {
      "get": {
        "operationId": "get_presence_api_v1_presence_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresenceState"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "summary": "Get Presence",
        "tags": [
          "system"
        ]
      }
    }
  }
}
