Files
Gabriel Vidal b2308d415c feat(ha): phone_service — hand every notify to a voice leg through HA
The HA integration now covers both destinations of a notification instead of
only the screen: an optional `phone_service: <domain>.<service>` is called
with `{spoken, type, title, message, audio}` for each notify, non-blocking and
best-effort, so a slow phone can neither delay the push nor fail the hub's
webhook. The homelab points it at `script.ai_agent_phone_notify`, which rings
the Yealink desk phone — replacing the hub's second, direct-to-bridge webhook.

Also fires `ai_agent_notify` / `ai_agent_ask` on the HA event bus so
automations can react to agent activity. Asks keep no voice leg (answering one
means tapping a button).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 03:40:14 +02:00

81 lines
3.5 KiB
Markdown

# `ai_agent` — Home Assistant custom integration
The Home Assistant side of the ai-agent notification hub. It registers a
webhook (`/api/webhook/ai_agent`) that the ai-agent backend forwards its
`notify` and `ask` events to, and turns them into Android companion-app
notifications:
- **notify** — a normal push on the per-type channel (`Claude · Deploy`,
`Claude · Error`, …) with the same icon/color/vibration map the notify-done
skill used to apply client-side. Explicit payload fields (`icon`, `channel`,
`vibrationPattern`, …) always win over the type defaults.
- **ask** — a *persistent* notification on the dedicated **`Claude · Ask`**
channel with the ". .. .._" morse vibration pattern
(`0, 100, 350, 100, 120, 100, 350, 100, 120, 100, 120, 450`) and one action
button per option. When a button is tapped the integration POSTs
`{"index": n}` back to the backend (`POST /api/ask/{id}/answer`) and clears
the notification from the phone. Whatever is long-polling
`GET /api/ask/{id}?waitSecs=…` (normally `ask.sh`) then unblocks with the
chosen label.
Android locks a channel's importance/vibration when the channel is first
created — to re-tune the ask buzz, rename `ASK_CHANNEL` in `const.py` (or
clear the companion app's storage).
## The voice leg (`phone_service`)
A notify has two destinations in the homelab: the screen (the Pixel push above)
and the **AI desk phone** (`services/phone`, a Yealink that reads updates aloud
over an auto-answer call). Both go through this one webhook: when
`phone_service: <domain>.<service>` is configured, every `notify` is also
handed to that HA service with the phone-relevant fields —
```python
{"spoken": , "type": , "title": , "message": , "audio": }
```
— non-blocking and best-effort, so a ringing (or absent) phone never delays the
push nor fails the hub's webhook. The homelab points it at
`script.ai_agent_phone_notify` (`services/home-assistant/config/packages/ai_agent_phone.yaml`),
which POSTs to the phone bridge; that script is where HA-side routing policy
belongs. Leave `phone_service` unset for screen-only notifications.
Asks have **no** voice leg on purpose — answering one means tapping a button.
## Events on the bus
Every handled payload is also fired on the HA event bus as `ai_agent_notify` /
`ai_agent_ask` (the raw webhook JSON as event data), so automations can react
to agent activity — flash a light on `type: error`, count deploys, mirror asks
to another device — without re-implementing the webhook.
## Install
```bash
./install.sh --restart # copy component + config block, restart HA
```
`HA_CONFIG` overrides the target config dir (default:
`services/home-assistant/config`). The config block it adds:
```yaml
ai_agent:
webhook_id: ai_agent # -> POST /api/webhook/ai_agent
notify_target: mobile_app_pixel_9 # notify.<target>
callback_url: http://127.0.0.1:8096 # ai-agent backend (host-published port)
phone_service: script.ai_agent_phone_notify # voice leg (see below)
```
HA runs on the host network, so `127.0.0.1:8096` reaches the ai-agent
container's published port; the backend's trusted-caller gate accepts the
connection because it arrives from the docker gateway.
## Verify
```bash
docker logs home-assistant 2>&1 | grep ai_agent # "ai_agent ready: …"
curl -s -X POST http://127.0.0.1:8123/api/webhook/ai_agent \
-H 'Content-Type: application/json' \
-d '{"event":"notify","title":"test","message":"hello","type":"success"}'
```