diff --git a/CLAUDE.md b/CLAUDE.md index 2e9ebd5..7d1eccd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -172,10 +172,11 @@ Use in automations: ## File Structure ``` -├── kao.py # Unified entry point +├── kao.py # Unified entry point ├── aggregator.py # Event broker/API server ├── index.html # OLED-optimized frontend ├── config.json # Runtime configuration +├── openapi.yaml # API documentation (OpenAPI 3.0) ├── detectors/ │ ├── disk_space.py │ ├── cpu.py diff --git a/README.md b/README.md index f233e73..0717eba 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,8 @@ automation: | `/sleep` | POST | Enter sleep mode | | `/wake` | POST | Exit sleep mode | +Full API documentation available in [openapi.yaml](openapi.yaml) (OpenAPI 3.0 spec). + ## Personality The emote has personality! In optimal state it: diff --git a/index.html b/index.html index d96c900..edb4cae 100644 --- a/index.html +++ b/index.html @@ -217,7 +217,7 @@ const emoteEl = document.getElementById("emote"); const messageEl = document.getElementById("message"); const POLL_INTERVAL = 2000; - const VERSION = "v1.3.0"; + const VERSION = "v1.3.1"; // Sound system let audioCtx = null; diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..bd86cfd --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,400 @@ +openapi: 3.0.3 +info: + title: Kao API + description: | + Kao is a minimalist system status monitor that uses ASCII emotes to represent system health. + + ## Priority System + Events use a priority system where lower numbers indicate higher priority: + - **1 (Critical)**: System failure - red, shaking + - **2 (Warning)**: Degraded state - yellow, breathing + - **3 (Notify)**: Informational alert - blue, popping + - **4 (Optimal)**: All systems healthy - green, varies + + ## TTL/Heartbeat Pattern + Events can have a TTL (time-to-live) that auto-expires them. Detectors typically send + heartbeat events that expire if not refreshed, indicating loss of communication. + version: 1.3.0 + license: + name: MIT + +servers: + - url: http://localhost:5100 + description: Local development server + +paths: + /event: + post: + summary: Register an event + description: | + Register or update a system event. Events are identified by their ID and will + overwrite any existing event with the same ID. Use TTL for heartbeat-style + monitoring where absence of updates indicates a problem. + operationId: postEvent + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EventRequest' + examples: + critical: + summary: Critical event + value: + id: "disk_root" + priority: 1 + message: "Root disk 98% full" + heartbeat: + summary: Heartbeat with TTL + value: + id: "cpu_monitor" + priority: 4 + message: "CPU nominal" + ttl: 60 + responses: + '200': + description: Event registered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/EventResponse' + '400': + description: Invalid request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /clear: + post: + summary: Clear an event + description: Remove an event by its ID. Use this when a condition has resolved. + operationId: clearEvent + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - id + properties: + id: + type: string + description: Event identifier to clear + example: "disk_root" + responses: + '200': + description: Event cleared successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ClearResponse' + '400': + description: Missing required field + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Event not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /notify: + post: + summary: Send a notification + description: | + Send a temporary notification with optional custom display properties. + Designed for Home Assistant integration. Notifications auto-expire after + the specified duration. + operationId: notify + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyRequest' + examples: + simple: + summary: Simple notification + value: + message: "Doorbell rang" + duration: 10 + custom: + summary: Custom notification + value: + message: "Welcome home!" + duration: 10 + emote: "(`-´)ゞ" + color: "#00FF88" + animation: "celebrating" + sound: "chime" + responses: + '200': + description: Notification sent + content: + application/json: + schema: + $ref: '#/components/schemas/NotifyResponse' + + /sleep: + post: + summary: Enter sleep mode + description: | + Put Kao into sleep mode. The display will show a sleeping emote with + dimmed colors. All events are preserved but not displayed until wake. + operationId: sleep + responses: + '200': + description: Entered sleep mode + content: + application/json: + schema: + $ref: '#/components/schemas/SleepResponse' + + /wake: + post: + summary: Exit sleep mode + description: Wake Kao from sleep mode and resume normal status display. + operationId: wake + responses: + '200': + description: Exited sleep mode + content: + application/json: + schema: + $ref: '#/components/schemas/WakeResponse' + + /status: + get: + summary: Get current status + description: | + Returns the current display state including the active emote, color, + animation, and list of active events. The frontend polls this endpoint. + operationId: getStatus + responses: + '200': + description: Current status + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + + /events: + get: + summary: List all active events + description: Returns all currently active events with their full details. + operationId: listEvents + responses: + '200': + description: List of active events + content: + application/json: + schema: + $ref: '#/components/schemas/EventList' + +components: + schemas: + EventRequest: + type: object + required: + - id + - priority + properties: + id: + type: string + description: Unique identifier for this event + example: "cpu_high" + priority: + type: integer + minimum: 1 + maximum: 4 + description: | + Event priority (1=Critical, 2=Warning, 3=Notify, 4=Optimal) + example: 2 + message: + type: string + description: Optional message to display + example: "CPU at 90%" + ttl: + type: integer + description: Time-to-live in seconds. Event auto-expires after this duration. + example: 60 + + EventResponse: + type: object + properties: + status: + type: string + example: "ok" + current_state: + $ref: '#/components/schemas/Status' + + ClearResponse: + type: object + properties: + status: + type: string + example: "cleared" + current_state: + $ref: '#/components/schemas/Status' + + NotifyRequest: + type: object + properties: + message: + type: string + description: Text to display below the emote + example: "Someone at the door" + duration: + type: integer + description: Seconds before auto-expire + default: 5 + example: 10 + emote: + type: string + description: Custom emote to display (overrides default) + example: "( °o°)" + color: + type: string + description: Custom color in hex format + pattern: '^#[0-9A-Fa-f]{6}$' + example: "#FF9900" + animation: + type: string + description: Animation style + enum: + - breathing + - shaking + - popping + - celebrating + - floating + - bouncing + - swaying + example: "popping" + sound: + type: string + description: Sound effect to play + enum: + - chime + - alert + - warning + - critical + - success + - notify + - none + example: "chime" + + NotifyResponse: + type: object + properties: + status: + type: string + example: "ok" + id: + type: string + description: Generated event ID + example: "ha_notify_1704067200000" + current_state: + $ref: '#/components/schemas/Status' + + SleepResponse: + type: object + properties: + status: + type: string + example: "sleeping" + current_state: + $ref: '#/components/schemas/Status' + + WakeResponse: + type: object + properties: + status: + type: string + example: "awake" + current_state: + $ref: '#/components/schemas/Status' + + Status: + type: object + properties: + current_state: + type: string + description: Current state name + enum: + - critical + - warning + - notify + - optimal + - sleeping + example: "optimal" + active_emote: + type: string + description: ASCII emote to display + example: "( ^_^)" + color: + type: string + description: Display color in hex + example: "#00FF00" + animation: + type: string + description: Current animation + example: "breathing" + message: + type: string + description: Status message + example: "Optimal" + sound: + type: string + description: Sound to play (only present when triggered) + example: "chime" + active_events: + type: array + items: + $ref: '#/components/schemas/ActiveEvent' + last_updated: + type: string + format: date-time + description: ISO 8601 timestamp + example: "2024-01-01T12:00:00" + + ActiveEvent: + type: object + properties: + id: + type: string + example: "cpu_monitor" + priority: + type: integer + example: 4 + message: + type: string + example: "CPU nominal" + + EventList: + type: object + properties: + events: + type: object + additionalProperties: + type: object + properties: + priority: + type: integer + message: + type: string + timestamp: + type: number + ttl: + type: number + + Error: + type: object + properties: + error: + type: string + example: "Missing required fields: id, priority"