{
  "openapi": "3.1.0",
  "info": {
    "title": "Remote Control API",
    "version": "1.0.0",
    "description": "REST API of the Superordinate Controller with which the Robot Operator drives the Kiwi Robot remotely. See capabilities 002, 003, and 005."
  },
  "servers": [
    {
      "url": "http://{host}:{port}",
      "variables": {
        "host": { "default": "localhost" },
        "port": { "default": "8081" }
      }
    }
  ],
  "paths": {
    "/v1/drive-commands": {
      "post": {
        "operationId": "sendRemoteDriveCommand",
        "summary": "Translate the robot in a direction at a speed for the Remote Drive Command Timeout",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RemoteDriveCommand" },
              "example": { "direction": 0, "speed": 50 }
            }
          }
        },
        "responses": {
          "202": { "description": "Command accepted; the Drive Command follows asynchronously" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "500": { "$ref": "#/components/responses/InternalServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v1/rotation-commands": {
      "post": {
        "operationId": "sendRemoteRotationCommand",
        "summary": "Rotate the robot around its center at a speed for the Remote Drive Command Timeout",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RemoteRotationCommand" },
              "example": { "speed": 20 }
            }
          }
        },
        "responses": {
          "202": { "description": "Command accepted; the Drive Command follows asynchronously" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "500": { "$ref": "#/components/responses/InternalServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v1/stop-commands": {
      "post": {
        "operationId": "sendStopCommand",
        "summary": "End the active Remote Drive Command and enter the Safe Mode",
        "responses": {
          "202": { "description": "Command accepted; the Neutral Drive Command follows asynchronously" },
          "500": { "$ref": "#/components/responses/InternalServerError" },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" }
        }
      }
    },
    "/v1/telemetry": {
      "get": {
        "operationId": "openTelemetryStream",
        "summary": "Open the Telemetry Stream: a Telemetry Snapshot at once and then one every TELEMETRY_INTERVAL milliseconds",
        "responses": {
          "200": {
            "description": "Server-Sent Events of type telemetry; the data of each is one TelemetrySnapshot (#/components/schemas/TelemetrySnapshot) as JSON",
            "content": {
              "text/event-stream": {
                "schema": { "type": "string" },
                "example": "event: telemetry\ndata: {\"type\":\"telemetry\",\"version\":1,\"timestamp\":\"2026-10-09T12:00:00.000Z\",\"source\":\"superordinate-controller\",\"batteryVoltage\":36.25,\"motorSpeeds\":[50,50,100]}\n\n"
              }
            }
          },
          "500": { "$ref": "#/components/responses/InternalServerError" },
          "503": {
            "description": "Too many Telemetry Streams are open",
            "content": {
              "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } }
            }
          }
        }
      }
    },
    "/healthz/live": {
      "get": {
        "operationId": "checkLiveness",
        "summary": "Report whether the process runs",
        "responses": {
          "200": { "description": "Live" }
        }
      }
    },
    "/healthz/ready": {
      "get": {
        "operationId": "checkReadiness",
        "summary": "Report whether the Superordinate Controller accepts commands",
        "responses": {
          "200": { "description": "Ready" },
          "503": { "description": "Not ready: starting, shutting down, or the CAN Adapter is unavailable" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "RemoteDriveCommand": {
        "type": "object",
        "required": ["direction", "speed"],
        "additionalProperties": false,
        "properties": {
          "direction": {
            "type": "integer",
            "minimum": 0,
            "maximum": 359,
            "description": "Direction of the translation in degrees: 0 forward, 90 right, 180 backward, 270 left, seen from above"
          },
          "speed": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Speed of the translation in percent of the maximum speed"
          }
        }
      },
      "RemoteRotationCommand": {
        "type": "object",
        "required": ["speed"],
        "additionalProperties": false,
        "properties": {
          "speed": {
            "type": "integer",
            "minimum": -100,
            "maximum": 100,
            "description": "Speed of the rotation in percent of the maximum speed: positive clockwise, negative counterclockwise, seen from above"
          }
        }
      },
      "TelemetrySnapshot": {
        "type": "object",
        "description": "Data of one event of the Telemetry Stream",
        "required": ["type", "version", "timestamp", "source", "batteryVoltage", "motorSpeeds"],
        "additionalProperties": false,
        "properties": {
          "type": { "const": "telemetry" },
          "version": { "const": 1 },
          "timestamp": { "type": "string", "format": "date-time", "description": "Time of the snapshot, UTC with milliseconds" },
          "source": { "const": "superordinate-controller" },
          "batteryVoltage": {
            "type": ["number", "null"],
            "description": "Battery Voltage in volts, two decimals; null while it is not current"
          },
          "motorSpeeds": {
            "type": "array",
            "minItems": 3,
            "maxItems": 3,
            "description": "Motor Speed of motors 1 to 3 in percent; an entry is null while it is not current",
            "items": { "type": ["integer", "null"], "minimum": -100, "maximum": 100 }
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "Problem detail of RFC 9457",
        "required": ["type", "title", "status"],
        "properties": {
          "type": { "type": "string", "format": "uri-reference", "default": "about:blank" },
          "title": { "type": "string" },
          "status": { "type": "integer" },
          "detail": { "type": "string" },
          "instance": { "type": "string", "format": "uri-reference" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Functional error: malformed JSON, missing or unknown field, wrong type, or value out of range",
        "content": {
          "application/problem+json": {
            "schema": { "$ref": "#/components/schemas/Problem" },
            "example": { "type": "about:blank", "title": "Bad Request", "status": 400, "detail": "speed must be between 0 and 100" }
          }
        }
      },
      "InternalServerError": {
        "description": "Technical error",
        "content": {
          "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } }
        }
      },
      "ServiceUnavailable": {
        "description": "The CAN Adapter is unavailable",
        "content": {
          "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } }
        }
      }
    }
  }
}
