Spencer a074a42d40 Bump to v2.0.0: daily emote rotation + expanded sound library
- Emote face is now stable all day; /wake picks a fresh random face each
  morning instead of rotating every 5 minutes. Removes EMOTE_ROTATION_INTERVAL
  and last_emote_change entirely.
- Added 10 new synthesized sounds for /notify: doorbell, knock, ding, blip,
  siren, tada, ping, bubble, fanfare, and alarm (loops until tapped or TTL
  expires). stopAlarm() wired into tap handler and handleStateChange().
- openapi.yaml: new sound names added to enum.
- CLAUDE.md / README.md updated to reflect both changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 16:36:23 -06:00

Kao

A minimalist system status monitor that uses ASCII emotes to display server health on an old phone.

Status: Optimal

Why?

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.

Features

  • OLED-optimized — Pure black background, saves battery
  • Glanceable — Know your server's status from across the room
  • Extensible — Add custom detectors for any metric
  • Personality — Rotating expressions, celebration animations, sleep mode
  • Sound effects — Optional audio cues for state changes (tap to enable)
  • Home Assistant ready — Webhook endpoints for notifications and automation

Quick Start

# Clone and setup
git clone https://github.com/yourusername/kao.git
cd kao
python -m venv venv
source venv/bin/activate  # Windows: .\venv\Scripts\activate
pip install -r requirements.txt

# Run everything
python kao.py

Open http://localhost:5100 on your phone (use Fully Kiosk Browser for best results).

Status Faces

State Emote Meaning
Optimal ( ^_^) All systems healthy
Warning ( o_o) Something needs attention
Critical ( x_x) Immediate action required
Notify ( 'o') Transient notification
Sleeping ( -_-)zzZ Sleep mode active
Disconnected ( ?.?) Can't reach server

Built-in Detectors

Detector Monitors
disk_space Disk usage on all drives
cpu CPU utilization
memory RAM usage
service Whether processes are running
network Host reachability (ping)
docker Container health and restart loops

Configuration

Edit config.json to enable/disable detectors and set thresholds:

{
	"aggregator_url": "http://localhost:5100",
	"detectors": [
		{
			"name": "disk_space",
			"enabled": true,
			"script": "detectors/disk_space.py",
			"env": {
				"CHECK_INTERVAL": "300",
				"THRESHOLD_WARNING": "85",
				"THRESHOLD_CRITICAL": "95"
			}
		}
	]
}

Custom Detectors

Create your own detector by POSTing events to the aggregator:

curl -X POST http://localhost:5100/event \
  -H "Content-Type: application/json" \
  -d '{"id": "my_check", "priority": 2, "message": "Something is wrong", "ttl": 120}'
  • id — Unique identifier for this event
  • priority — 1 (critical), 2 (warning), 3 (notify), 4 (optimal)
  • message — What to display
  • ttl — Auto-expire after N seconds (for heartbeat pattern)

Clear an event:

curl -X POST http://localhost:5100/clear \
  -d '{"id": "my_check"}'

Home Assistant Integration

Add REST commands to your configuration.yaml:

rest_command:
  kao_notify:
    url: "http://YOUR_SERVER:5100/notify"
    method: POST
    content_type: "application/json"
    payload: >
      {
        "message": "{{ message | default('') }}",
        "duration": {{ duration | default(5) }},
        "emote": "{{ emote | default('') }}",
        "color": "{{ color | default('') }}",
        "animation": "{{ animation | default('') }}",
        "sound": "{{ sound | default('') }}"
      }

  kao_sleep:
    url: "http://YOUR_SERVER:5100/sleep"
    method: POST

  kao_wake:
    url: "http://YOUR_SERVER:5100/wake"
    method: POST

Use in automations:

automation:
  - 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
        emote: "( °o°)"
        color: "#FF9900"
        sound: "chime"

  - alias: "Timer Complete"
    trigger:
      platform: event
      event_type: timer.finished
    action:
      service: rest_command.kao_notify
      data:
        message: "Timer done!"
        emote: "\\(^o^)/"
        animation: "celebrating"
        sound: "success"

  - alias: "Kao Sleep at Bedtime"
    trigger:
      platform: time
      at: "23:00:00"
    action:
      service: rest_command.kao_sleep

  - alias: "Kao Wake in Morning"
    trigger:
      platform: time
      at: "07:00:00"
    action:
      service: rest_command.kao_wake

Notify options:

  • emote: Any ASCII emote (e.g., ( °o°), \\(^o^)/)
  • color: Hex color (e.g., #FF9900)
  • animation: breathing, shaking, popping, celebrating, floating, bouncing, swaying
  • sound: chime, alert, warning, critical, success, notify, doorbell, knock, ding, blip, siren, tada, ping, bubble, fanfare, alarm, none

API Reference

Endpoint Method Description
/ GET Web UI
/status GET Current state as JSON
/events GET List all active events
/event POST Register an event
/clear POST Clear an event by ID
/clear-all POST Clear all active events
/notify POST Simple notification {"message": "", "duration": 5}
/sleep POST Enter sleep mode
/wake POST Exit sleep mode
/docs GET Interactive API documentation (Swagger UI)

Full API documentation available at /docs or in openapi.yaml.

Personality

The emote has personality! In optimal state it:

  • Shows a stable face all day — set fresh each morning when /wake is called
  • Occasionally winks ( -_^) or blinks ( ᵕ.ᵕ) for a second or two on wake
  • Celebrates \(^o^)/ when recovering from warnings
  • Each face has its own animation (floating, bouncing, swaying)
  • Reacts when tapped ( °o°), shows version info, and dismisses active alerts

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
  • 10 additional synthesized sounds for /notify: doorbell, knock, ding, blip, siren, tada, ping, bubble, fanfare, alarm (alarm loops until tapped)

License

MIT

Description
No description provided
Readme 333 KiB
Languages
Python 73.6%
HTML 19.4%
Shell 7%