| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- openapi: 3.1.0
- info:
- title: Getränkeautomat Monitor API
- version: 1.0.0
- summary: HTTP API for ingesting sensor readings and retrieving vending machine status.
- description: |
- Handwritten OpenAPI specification for the Getränkeautomat Monitor application.
- The API currently exposes two endpoints:
- - `POST /api/v1/readings.php` for ingesting one sensor measurement
- - `GET /api/v1/status.php` for retrieving the aggregated application state
- The readings endpoint uses Bearer token authentication. The status endpoint is
- intentionally public so the dashboard can poll it without a login.
- servers:
- - url: ./
- description: Same-origin deployment
- tags:
- - name: Readings
- description: Receive one sensor reading and update the persisted slot state.
- - name: Status
- description: Retrieve the current aggregated machine, slot, and alert status.
- paths:
- /api/v1/readings.php:
- post:
- tags:
- - Readings
- summary: Submit one reading
- description: |
- Accepts exactly one sensor measurement for one configured machine slot.
- Processing behavior:
- - Requires a Bearer token in the `Authorization` header
- - Accepts JSON request bodies
- - Updates `data/state.json` on success
- - Triggers alerts only when the slot state changes from `ok` to `critical`
- or from `critical` to `ok`
- operationId: submitReading
- security:
- - bearerAuth: []
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ReadingRequest'
- examples:
- lobbySlot:
- summary: Reading for the A1 slot in the lobby machine
- value:
- machine_id: automat-lobby
- sensor_id: fach-a1
- distance_mm: 184
- measured_at: '2026-04-15T19:20:00Z'
- responses:
- '200':
- description: Reading processed successfully.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ReadingSuccessResponse'
- examples:
- ok:
- value:
- ok: true
- machine_id: automat-lobby
- sensor_id: fach-a1
- slot_label: A1
- units_estimated: 4
- fill_percent: 63
- state: ok
- '400':
- description: Invalid JSON body or malformed request payload.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- invalidJson:
- value:
- ok: false
- error: Ungültiger JSON-Body.
- '401':
- description: Missing or invalid Bearer token.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- unauthorized:
- value:
- ok: false
- error: Nicht autorisiert.
- '404':
- description: The referenced machine or sensor is not configured.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- unknownMachineOrSensor:
- value:
- ok: false
- error: Unbekannter Automat oder Sensor.
- '405':
- description: Only POST is supported for this endpoint.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- wrongMethod:
- value:
- ok: false
- error: Nur POST ist erlaubt.
- '422':
- description: Semantic validation failed.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- missingIdentifiers:
- value:
- ok: false
- error: machine_id und sensor_id sind erforderlich.
- nonNumericDistance:
- value:
- ok: false
- error: distance_mm muss numerisch sein.
- invalidTimestamp:
- value:
- ok: false
- error: measured_at ist kein gültiger ISO-Zeitstempel.
- '500':
- description: Unexpected internal server error.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- internalError:
- value:
- ok: false
- error: Interner Fehler.
- options:
- tags:
- - Readings
- summary: CORS preflight for readings
- description: |
- Preflight handler for browser-based clients. The endpoint responds with
- `204 No Content` and emits `Access-Control-Allow-Methods` and
- `Access-Control-Allow-Headers`.
- operationId: readingsPreflight
- responses:
- '204':
- description: Preflight accepted without a response body.
- headers:
- Access-Control-Allow-Methods:
- description: Allowed methods for this endpoint.
- schema:
- type: string
- example: POST, OPTIONS
- Access-Control-Allow-Headers:
- description: Allowed request headers for this endpoint.
- schema:
- type: string
- example: Authorization, Content-Type
- /api/v1/status.php:
- get:
- tags:
- - Status
- summary: Retrieve the current application status
- description: |
- Returns the aggregated state for the dashboard and admin panel.
- The response contains:
- - app metadata
- - machine and slot status
- - a summary section
- - the most recent alert log entries
- operationId: getStatus
- responses:
- '200':
- description: Aggregated status generated successfully.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/StatusResponse'
- examples:
- dashboard:
- value:
- ok: true
- generated_at: '2026-04-15T20:10:00+00:00'
- app:
- name: Getränkeautomat Monitor
- dashboard_refresh_seconds: 15
- summary:
- machine_count: 2
- slot_count: 3
- critical_count: 1
- machines:
- - id: automat-lobby
- name: Lobby Automat
- location: Erdgeschoss
- slots:
- - machine_id: automat-lobby
- machine_name: Lobby Automat
- sensor_id: fach-a1
- slot_label: A1
- product_name: Cola 0,5l
- fill_percent: 63
- units_estimated: 4
- max_units: 7
- distance_mm: 184
- state: ok
- measured_at: '2026-04-15T19:20:00+00:00'
- updated_at: '2026-04-15T19:20:02+00:00'
- alert_below_units: 2
- alerts:
- - id: alert_680004979d8512.07480974
- created_at: '2026-04-15T19:20:02+00:00'
- payload:
- event: critical
- machine_id: automat-lobby
- machine_name: Lobby Automat
- sensor_id: fach-a1
- slot_label: A1
- product_name: Cola 0,5l
- distance_mm: 320
- units_estimated: 1
- max_units: 7
- fill_percent: 14
- state: critical
- previous_state: ok
- measured_at: '2026-04-15T19:19:59+00:00'
- deliveries:
- webhooks:
- - label: lager-webhook
- success: false
- message: Webhook nicht gefunden oder deaktiviert.
- emails:
- - label: lager-team
- success: true
- message: Email versendet.
- '405':
- description: Only GET is supported for this endpoint.
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- examples:
- wrongMethod:
- value:
- ok: false
- error: Nur GET ist erlaubt.
- components:
- securitySchemes:
- bearerAuth:
- type: http
- scheme: bearer
- bearerFormat: opaque token
- description: Bearer token stored in config.json under api.bearer_token.
- schemas:
- ReadingRequest:
- type: object
- additionalProperties: false
- required:
- - machine_id
- - sensor_id
- - distance_mm
- properties:
- machine_id:
- type: string
- description: Configured machine identifier from config.json.
- minLength: 1
- example: automat-lobby
- sensor_id:
- type: string
- description: Slot or sensor identifier within the machine.
- minLength: 1
- example: fach-a1
- distance_mm:
- type: number
- description: Measured distance in millimeters.
- example: 184
- measured_at:
- type:
- - string
- - 'null'
- description: |
- Optional measurement timestamp. When omitted or empty, the server uses
- the current time. Values are parsed with PHP `strtotime()` and returned
- as an ISO-8601 timestamp.
- format: date-time
- example: '2026-04-15T19:20:00Z'
- ReadingSuccessResponse:
- type: object
- additionalProperties: false
- required:
- - ok
- - machine_id
- - sensor_id
- - slot_label
- - units_estimated
- - fill_percent
- - state
- properties:
- ok:
- type: boolean
- const: true
- machine_id:
- type: string
- example: automat-lobby
- sensor_id:
- type: string
- example: fach-a1
- slot_label:
- type: string
- example: A1
- units_estimated:
- type: integer
- example: 4
- fill_percent:
- type: integer
- example: 63
- state:
- $ref: '#/components/schemas/SlotState'
- ErrorResponse:
- type: object
- additionalProperties: false
- required:
- - ok
- - error
- properties:
- ok:
- type: boolean
- const: false
- error:
- type: string
- example: Nicht autorisiert.
- StatusResponse:
- type: object
- additionalProperties: false
- required:
- - ok
- - generated_at
- - app
- - summary
- - machines
- - alerts
- properties:
- ok:
- type: boolean
- const: true
- generated_at:
- type: string
- format: date-time
- description: Timestamp when the response was generated.
- app:
- $ref: '#/components/schemas/AppStatus'
- summary:
- $ref: '#/components/schemas/StatusSummary'
- machines:
- type: array
- items:
- $ref: '#/components/schemas/MachineStatus'
- alerts:
- type: array
- items:
- $ref: '#/components/schemas/AlertEvent'
- AppStatus:
- type: object
- additionalProperties: false
- required:
- - name
- - dashboard_refresh_seconds
- properties:
- name:
- type: string
- example: Getränkeautomat Monitor
- dashboard_refresh_seconds:
- type: integer
- minimum: 1
- example: 15
- StatusSummary:
- type: object
- additionalProperties: false
- required:
- - machine_count
- - slot_count
- - critical_count
- properties:
- machine_count:
- type: integer
- minimum: 0
- example: 2
- slot_count:
- type: integer
- minimum: 0
- example: 3
- critical_count:
- type: integer
- minimum: 0
- example: 1
- MachineStatus:
- type: object
- additionalProperties: false
- required:
- - id
- - name
- - location
- - slots
- properties:
- id:
- type: string
- example: automat-lobby
- name:
- type: string
- example: Lobby Automat
- location:
- type: string
- example: Erdgeschoss
- slots:
- type: array
- items:
- $ref: '#/components/schemas/SlotStatus'
- SlotStatus:
- type: object
- additionalProperties: false
- required:
- - machine_id
- - machine_name
- - sensor_id
- - slot_label
- - product_name
- - fill_percent
- - units_estimated
- - max_units
- - distance_mm
- - state
- - measured_at
- - updated_at
- - alert_below_units
- properties:
- machine_id:
- type: string
- example: automat-lobby
- machine_name:
- type: string
- example: Lobby Automat
- sensor_id:
- type: string
- example: fach-a1
- slot_label:
- type: string
- example: A1
- product_name:
- type: string
- example: Cola 0,5l
- fill_percent:
- type:
- - integer
- - 'null'
- minimum: 0
- maximum: 100
- description: Null until a first reading has been received.
- example: 63
- units_estimated:
- type:
- - integer
- - 'null'
- minimum: 0
- description: Null until a first reading has been received.
- example: 4
- max_units:
- type: integer
- minimum: 0
- example: 7
- distance_mm:
- type:
- - number
- - 'null'
- description: Last measured distance in millimeters.
- example: 184
- state:
- $ref: '#/components/schemas/SlotState'
- measured_at:
- type:
- - string
- - 'null'
- format: date-time
- example: '2026-04-15T19:20:00+00:00'
- updated_at:
- type:
- - string
- - 'null'
- format: date-time
- example: '2026-04-15T19:20:02+00:00'
- alert_below_units:
- type: integer
- minimum: 0
- example: 2
- SlotState:
- type: string
- enum:
- - ok
- - critical
- - unknown
- example: ok
- AlertEvent:
- type: object
- additionalProperties: false
- required:
- - id
- - created_at
- - payload
- - deliveries
- properties:
- id:
- type: string
- description: Unique alert log entry ID generated with PHP uniqid().
- example: alert_680004979d8512.07480974
- created_at:
- type: string
- format: date-time
- example: '2026-04-15T19:20:02+00:00'
- payload:
- $ref: '#/components/schemas/AlertPayload'
- deliveries:
- $ref: '#/components/schemas/AlertDeliveries'
- AlertPayload:
- type: object
- additionalProperties: false
- required:
- - event
- - machine_id
- - machine_name
- - sensor_id
- - slot_label
- - product_name
- - distance_mm
- - units_estimated
- - max_units
- - fill_percent
- - state
- - previous_state
- - measured_at
- properties:
- event:
- type: string
- enum:
- - critical
- - recovered
- example: critical
- machine_id:
- type: string
- example: automat-lobby
- machine_name:
- type: string
- example: Lobby Automat
- sensor_id:
- type: string
- example: fach-a1
- slot_label:
- type: string
- example: A1
- product_name:
- type: string
- example: Cola 0,5l
- distance_mm:
- type:
- - number
- - 'null'
- example: 320
- units_estimated:
- type:
- - integer
- - 'null'
- example: 1
- max_units:
- type:
- - integer
- - 'null'
- example: 7
- fill_percent:
- type:
- - integer
- - 'null'
- example: 14
- state:
- $ref: '#/components/schemas/SlotState'
- previous_state:
- type:
- - string
- - 'null'
- description: Previous slot state before the transition.
- example: ok
- measured_at:
- type:
- - string
- - 'null'
- format: date-time
- example: '2026-04-15T19:19:59+00:00'
- AlertDeliveries:
- type: object
- additionalProperties: false
- required:
- - webhooks
- - emails
- properties:
- webhooks:
- type: array
- items:
- $ref: '#/components/schemas/DeliveryResult'
- emails:
- type: array
- items:
- $ref: '#/components/schemas/DeliveryResult'
- DeliveryResult:
- type: object
- additionalProperties: false
- required:
- - id
- - success
- - message
- properties:
- id:
- type: string
- example: lager-team
- success:
- type: boolean
- example: true
- message:
- type: string
- example: Email versendet.
|