openapi: 3.1.0
info:
  title: EDC Acoustics Device API (PROPOSAL)
  version: 0.1.0-draft
  description: >
    Draft. Endpoint shapes are proposals; transport, port and authentication are placeholders
    until EDC confirms them. Served by the loudspeaker or by an EDC gateway.
    Canonical documentation: https://www.edcacoustics.com/developers/api/
  license: { name: CC-BY-4.0 (proposed), url: https://creativecommons.org/licenses/by/4.0/ }
servers:
  - url: http://{host}:{port}/v1
    variables:
      host: { default: "edc-<serial>.local" }
      port: { default: "<PORT_TBD>" }
security:
  - bearerAuth: []
paths:
  /devices:
    get:
      summary: Discover devices on the local network
      responses:
        "200":
          description: Devices
          content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/Device" } } } }
  /devices/{id}:
    get: { summary: Device identity, model, firmware, network, parameters: [ { $ref: "#/components/parameters/id" } ], responses: { "200": { description: Device, content: { application/json: { schema: { $ref: "#/components/schemas/Device" } } } } } }
  /devices/{id}/status:
    get: { summary: Telemetry snapshot (per-transducer health, thermal, amp faults, input levels), parameters: [ { $ref: "#/components/parameters/id" } ], responses: { "200": { description: Status, content: { application/json: { schema: { $ref: "#/components/schemas/Status" } } } } } }
  /devices/{id}/beams/{n}:
    get: { summary: Read beam n (1..4), parameters: [ { $ref: "#/components/parameters/id" }, { $ref: "#/components/parameters/n" } ], responses: { "200": { description: Beam, content: { application/json: { schema: { $ref: "#/components/schemas/Beam" } } } } } }
    put:
      summary: Set beam aim/shape/level/delay (applied immediately or staged — see ?apply)
      parameters: [ { $ref: "#/components/parameters/id" }, { $ref: "#/components/parameters/n" }, { name: apply, in: query, schema: { type: boolean, default: true } } ]
      requestBody: { required: true, content: { application/json: { schema: { $ref: "#/components/schemas/Beam" } } } }
      responses: { "200": { description: Updated beam, content: { application/json: { schema: { $ref: "#/components/schemas/Beam" } } } } }
  /devices/{id}/inputs/{n}:
    put:
      summary: Input gain / mute / 8-band EQ / compressor (inputs 1..3)
      parameters: [ { $ref: "#/components/parameters/id" }, { $ref: "#/components/parameters/n" } ]
      requestBody: { required: true, content: { application/json: { schema: { $ref: "#/components/schemas/Input" } } } }
      responses: { "200": { description: Updated input } }
  /presets:
    get: { summary: List presets / coverage zones (project-level, may span an array), responses: { "200": { description: Presets } } }
  /presets/{presetId}/recall:
    post: { summary: Recall a preset on one device or a whole array, parameters: [ { name: presetId, in: path, required: true, schema: { type: string } } ], responses: { "202": { description: Recall accepted } } }
  /devices/{id}/firmware:
    get: { summary: Installed/available firmware, parameters: [ { $ref: "#/components/parameters/id" } ], responses: { "200": { description: Firmware } } }
  /events:
    get: { summary: WebSocket upgrade — telemetry + state-change stream (see asyncapi.yaml), responses: { "101": { description: Switching protocols } } }
components:
  parameters:
    id: { name: id, in: path, required: true, schema: { type: string }, description: "serial or MAC <TBD>" }
    n: { name: n, in: path, required: true, schema: { type: integer, minimum: 1, maximum: 4 } }
  securitySchemes:
    bearerAuth: { type: http, scheme: bearer, description: "<auth model TBD: token, or none on an isolated LAN>" }
  schemas:
    Device:
      type: object
      properties:
        id: { type: string, description: "serial or MAC <TBD>" }
        model: { type: string, enum: [SQ-90, SC-30, SC-60, SC-90, SL-30, SL-60, SS-1, SS-3] }
        firmware: { type: string }
        ip: { type: string }
        arrayId: { type: [string, "null"] }
    Beam:
      type: object
      properties:
        enabled: { type: boolean }
        pan_deg:   { type: number, minimum: -60, maximum: 60, description: "per A&E spec ±60°" }
        tilt_deg:  { type: number, minimum: -60, maximum: 60 }
        h_width_deg: { type: number, minimum: 2, maximum: 120, description: "A&E spec 2°–120°; datasheet 1° — reconcile" }
        v_width_deg: { type: number, minimum: 2, maximum: 120 }
        h_skew_db_per_deg: { type: number }
        v_skew_db_per_deg: { type: number }
        level_db: { type: number }
        delay_ms: { type: number }
        input: { type: integer, minimum: 1, maximum: 3 }
    Input:
      type: object
      properties:
        gain_db: { type: number }
        mute: { type: boolean }
        eq:
          type: array
          maxItems: 8
          items: { type: object, properties: { type: { type: string }, bypass: { type: boolean }, freq_hz: { type: number }, gain_db: { type: number }, q: { type: number } } }
        compressor: { type: object, properties: { attack_ms: { type: number }, threshold_db: { type: number }, ratio: { type: number }, release_ms: { type: number }, post_gain_db: { type: number } } }
    Status:
      type: object
      properties:
        online: { type: boolean }
        temperature_c: { type: number }
        faults: { type: array, items: { type: object, properties: { transducer: { type: integer }, code: { type: string }, since: { type: string, format: date-time } } } }
        amp_faults: { type: array, items: { type: string } }
        input_levels_dbfs: { type: array, items: { type: number } }
