Add /notify endpoint for Home Assistant integration

- New /notify endpoint: simple {"message": "", "duration": 5} API
- Uses Priority 3 (Notify) with auto-expiring TTL
- Updated CLAUDE.md with HA integration examples
- Updated README.md with new features and endpoints
- Added Docker detector to documentation
- Removed completed TODO items

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 21:15:32 -06:00
parent 66c9790d2b
commit d149580387
3 changed files with 125 additions and 34 deletions

View File

@@ -61,6 +61,7 @@ Edit `config.json` to configure detectors:
| Memory | `detectors/memory.py` | — | | Memory | `detectors/memory.py` | — |
| Service | `detectors/service.py` | `SERVICES` (comma-separated process names) | | Service | `detectors/service.py` | `SERVICES` (comma-separated process names) |
| Network | `detectors/network.py` | `HOSTS` (comma-separated hostnames/IPs) | | Network | `detectors/network.py` | `HOSTS` (comma-separated hostnames/IPs) |
| Docker | `detectors/docker.py` | `CONTAINERS` (optional, monitors all if empty) |
All detectors support: `AGGREGATOR_URL`, `CHECK_INTERVAL`, `THRESHOLD_WARNING`, `THRESHOLD_CRITICAL` All detectors support: `AGGREGATOR_URL`, `CHECK_INTERVAL`, `THRESHOLD_WARNING`, `THRESHOLD_CRITICAL`
@@ -70,7 +71,8 @@ All detectors support: `AGGREGATOR_URL`, `CHECK_INTERVAL`, `THRESHOLD_WARNING`,
|----------|--------|-------------| |----------|--------|-------------|
| `/event` | POST | Register event: `{"id": "name", "priority": 1-4, "message": "optional", "ttl": seconds}` | | `/event` | POST | Register event: `{"id": "name", "priority": 1-4, "message": "optional", "ttl": seconds}` |
| `/clear` | POST | Clear event: `{"id": "name"}` | | `/clear` | POST | Clear event: `{"id": "name"}` |
| `/sleep` | POST | Enter sleep mode (for Home Assistant) | | `/notify` | POST | Simple notification: `{"message": "text", "duration": 5}` |
| `/sleep` | POST | Enter sleep mode |
| `/wake` | POST | Exit sleep mode | | `/wake` | POST | Exit sleep mode |
| `/status` | GET | Current state JSON | | `/status` | GET | Current state JSON |
| `/events` | GET | List active events | | `/events` | GET | List active events |
@@ -93,7 +95,7 @@ The optimal state cycles through emotes with paired animations every 5 minutes:
| Emote | Animation | Vibe | | Emote | Animation | Vibe |
|-------|-----------|------| |-------|-----------|------|
| `( ^_^)` | breathing | calm | | `( ^_^)` | breathing | calm |
| `( ᵔᴥᵔ)` | floating | dreamy | | `( ˙▿˙)` | floating | content |
| `(◕‿◕)` | bouncing | cheerful | | `(◕‿◕)` | bouncing | cheerful |
| `( ・ω・)` | swaying | curious | | `( ・ω・)` | swaying | curious |
| `( ˘▽˘)` | breathing | cozy | | `( ˘▽˘)` | breathing | cozy |
@@ -104,6 +106,43 @@ Additional states:
- **Connection lost**: `( ?.?)` gray, searching animation - **Connection lost**: `( ?.?)` gray, searching animation
- **Sleep mode**: `( -_-)zzZ` dim, very slow breathing - **Sleep mode**: `( -_-)zzZ` dim, very slow breathing
## Home Assistant Integration
Add REST commands to `configuration.yaml`:
```yaml
rest_command:
kao_notify:
url: "http://kao-host:5100/notify"
method: POST
content_type: "application/json"
payload: '{"message": "{{ message }}", "duration": {{ duration | default(5) }}}'
kao_sleep:
url: "http://kao-host:5100/sleep"
method: POST
kao_wake:
url: "http://kao-host:5100/wake"
method: POST
```
Use in automations:
```yaml
# Doorbell notification
- service: rest_command.kao_notify
data:
message: "Someone at the door"
duration: 10
# Bedtime routine
- service: rest_command.kao_sleep
# Morning routine
- service: rest_command.kao_wake
```
## File Structure ## File Structure
``` ```
@@ -116,7 +155,8 @@ Additional states:
│ ├── cpu.py │ ├── cpu.py
│ ├── memory.py │ ├── memory.py
│ ├── service.py │ ├── service.py
── network.py ── network.py
│ └── docker.py
├── requirements.txt ├── requirements.txt
└── SPEC.md # Original project specification └── SPEC.md # Original project specification
``` ```

View File

@@ -8,18 +8,14 @@ A minimalist system status monitor that uses ASCII emotes to display server heal
Turn an old phone (with its OLED screen) into a glanceable ambient display for your home server. Instead of graphs and numbers, see a happy face `( ^_^)` when things are good, and a worried face `( o_o)` when they're not. Turn an old phone (with its OLED screen) into a glanceable ambient display for your home server. Instead of graphs and numbers, see a happy face `( ^_^)` when things are good, and a worried face `( o_o)` when they're not.
## TODO
- Figure out way to update installation in /opt/kao when git updates. Maybe run a git pull on service startup?
- Think about ways of implementing noises.
## Features ## Features
- **OLED-optimized** — Pure black background, saves battery - **OLED-optimized** — Pure black background, saves battery
- **Glanceable** — Know your server's status from across the room - **Glanceable** — Know your server's status from across the room
- **Extensible** — Add custom detectors for any metric - **Extensible** — Add custom detectors for any metric
- **Personality** — Rotating expressions, celebration animations, sleep mode - **Personality** — Rotating expressions, celebration animations, sleep mode
- **Home Assistant ready** — Webhook endpoints for automation - **Sound effects** — Optional audio cues for state changes (tap to enable)
- **Home Assistant ready** — Webhook endpoints for notifications and automation
## Quick Start ## Quick Start
@@ -50,13 +46,14 @@ Open http://localhost:5100 on your phone (use Fully Kiosk Browser for best resul
## Built-in Detectors ## Built-in Detectors
| Detector | Monitors | | Detector | Monitors |
| -------------- | ----------------------------- | | -------------- | ------------------------------------- |
| **disk_space** | Disk usage on all drives | | **disk_space** | Disk usage on all drives |
| **cpu** | CPU utilization | | **cpu** | CPU utilization |
| **memory** | RAM usage | | **memory** | RAM usage |
| **service** | Whether processes are running | | **service** | Whether processes are running |
| **network** | Host reachability (ping) | | **network** | Host reachability (ping) |
| **docker** | Container health and restart loops |
## Configuration ## Configuration
@@ -104,57 +101,83 @@ curl -X POST http://localhost:5100/clear \
## Home Assistant Integration ## Home Assistant Integration
Add webhook commands to your Home Assistant config: Add REST commands to your `configuration.yaml`:
```yaml ```yaml
rest_command: rest_command:
sentry_sleep: kao_notify:
url: "http://YOUR_SERVER:5100/notify"
method: POST
content_type: "application/json"
payload: '{"message": "{{ message }}", "duration": {{ duration | default(5) }}}'
kao_sleep:
url: "http://YOUR_SERVER:5100/sleep" url: "http://YOUR_SERVER:5100/sleep"
method: POST method: POST
sentry_wake:
kao_wake:
url: "http://YOUR_SERVER:5100/wake" url: "http://YOUR_SERVER:5100/wake"
method: POST method: POST
``` ```
Trigger from automations: Use in automations:
```yaml ```yaml
automation: automation:
- alias: "Sentry Sleep at Bedtime" - alias: "Doorbell Notification"
trigger:
platform: state
entity_id: binary_sensor.doorbell
to: "on"
action:
service: rest_command.kao_notify
data:
message: "Someone at the door"
duration: 10
- alias: "Kao Sleep at Bedtime"
trigger: trigger:
platform: time platform: time
at: "23:00:00" at: "23:00:00"
action: action:
service: rest_command.sentry_sleep service: rest_command.kao_sleep
- alias: "Sentry Wake in Morning" - alias: "Kao Wake in Morning"
trigger: trigger:
platform: time platform: time
at: "07:00:00" at: "07:00:00"
action: action:
service: rest_command.sentry_wake service: rest_command.kao_wake
``` ```
## API Reference ## API Reference
| Endpoint | Method | Description | | Endpoint | Method | Description |
| --------- | ------ | ---------------------- | | --------- | ------ | ------------------------------------------------ |
| `/` | GET | Web UI | | `/` | GET | Web UI |
| `/status` | GET | Current state as JSON | | `/status` | GET | Current state as JSON |
| `/events` | GET | List all active events | | `/events` | GET | List all active events |
| `/event` | POST | Register an event | | `/event` | POST | Register an event |
| `/clear` | POST | Clear an event by ID | | `/clear` | POST | Clear an event by ID |
| `/sleep` | POST | Enter sleep mode | | `/notify` | POST | Simple notification `{"message": "", "duration": 5}` |
| `/wake` | POST | Exit sleep mode | | `/sleep` | POST | Enter sleep mode |
| `/wake` | POST | Exit sleep mode |
## Personality ## Personality
The emote has personality! In optimal state it: The emote has personality! In optimal state it:
- Rotates through happy faces every 5 minutes - Rotates through happy faces every 5 minutes
- Occasionally winks `( -_^)` or blinks `( ᵕ.ᵕ)` - Occasionally winks `( -_^)` or blinks `( ᵕ.ᵕ)` for a second or two
- Celebrates `\(^o^)/` when recovering from warnings - Celebrates `\(^o^)/` when recovering from warnings
- Each face has its own animation (floating, bouncing, swaying) - Each face has its own animation (floating, bouncing, swaying)
- Reacts when tapped `( °o°)` and shows version info
**Sound effects** (tap screen to enable, or use `?sound=on`):
- Warning: soft double-beep
- Critical: urgent descending tone
- Notify: gentle ping
- Recovery: happy ascending chirp
## License ## License

View File

@@ -223,6 +223,34 @@ def clear_event():
return jsonify({"error": "Event not found"}), 404 return jsonify({"error": "Event not found"}), 404
@app.route("/notify", methods=["POST"])
def notify():
"""
Simple notification endpoint for Home Assistant.
JSON: {"message": "text", "duration": 5}
Shows the Notify emote with message, auto-expires after duration.
"""
data = request.get_json(force=True) if request.data else {}
message = data.get("message", "")
duration = int(data.get("duration", 5))
# Generate unique ID to avoid conflicts
event_id = f"ha_notify_{int(time.time() * 1000)}"
event = {
"priority": 3, # Notify priority
"message": message,
"timestamp": time.time(),
"ttl": time.time() + duration,
}
with events_lock:
active_events[event_id] = event
state = write_status()
return jsonify({"status": "ok", "id": event_id, "current_state": state}), 200
@app.route("/sleep", methods=["POST"]) @app.route("/sleep", methods=["POST"])
def sleep_mode(): def sleep_mode():
"""Enter sleep mode. For Home Assistant webhook.""" """Enter sleep mode. For Home Assistant webhook."""