All checks were successful
CI / changes (push) Successful in 19s
CI / test (push) Successful in 24s
CI / build-ai-gateway (push) Successful in 1m6s
CI / build-ha-gateway (push) Successful in 1m3s
CI / build-discord-bot (push) Successful in 1m4s
CI / build-alexa-bridge (push) Successful in 1m15s
CI / build-tts-gateway (push) Successful in 1m5s
CI / build-tts-sidecar (push) Has been skipped
CI / build-tts-model (push) Has been skipped
- Implemented SetTemperature in ClimateService for setting an absolute target temperature. - Updated ClimateServiceClient and ClimateServiceServer interfaces to include SetTemperature. - Added corresponding handler and tests for SetTemperature in ClimateGRPC. - Modified ClimateApp to handle SetTemperature requests without clamping. - Updated climate.proto to define SetTemperatureRequest message. - Adjusted Dockerfiles to include alexa-bridge dependencies.
94 lines
4.3 KiB
Markdown
94 lines
4.3 KiB
Markdown
# alexa-bridge
|
|
|
|
`alexa-bridge` is a self-hosted Alexa Custom Skill backend. It verifies that
|
|
incoming HTTPS requests actually came from Alexa, resolves the spoken device
|
|
name against entities discovered from `ha-gateway`, and executes the
|
|
resulting action over gRPC. Unlike the other three services in this repo, it
|
|
is internet-facing — Alexa's servers call directly into it — so it is the
|
|
one service whose own inbound edge is untrusted by default and needs its own
|
|
request verification, on top of the mTLS it uses (like `ai-gateway` and
|
|
`discord-bot`) to call `ha-gateway`.
|
|
|
|
See [plan.md](plan.md) for the full design, the action-table mapping from
|
|
Alexa intents to `ha-gateway` RPCs, and the resolved "Decisions on open
|
|
questions" section documenting scope choices (single-setpoint-only climate
|
|
control, exact-match-only entity name resolution, no remote/SwitchBot
|
|
support, etc.).
|
|
|
|
## Runtime Flow
|
|
|
|
1. The service loads `.env`, configures logging and telemetry, blocking-fetches
|
|
the initial entity list from `ha-gateway`'s `EntityService` (failing
|
|
startup if that fails — nothing can resolve a device name without it),
|
|
then starts serving HTTP on `HTTP_PORT`.
|
|
2. A background goroutine refreshes the entity list every
|
|
`ENTITY_REFRESH_INTERVAL`; a failed periodic refresh only logs and keeps
|
|
serving the last-known-good list.
|
|
3. Each incoming Alexa request is verified: `SignatureCertChainUrl`/
|
|
`Signature` headers (cert chain fetched from Amazon's S3 host and cached,
|
|
RSA-SHA1 signature over the raw body), request timestamp (150s replay
|
|
tolerance), and the request envelope's application ID against
|
|
`ALEXA_SKILL_ID`.
|
|
4. Verified `IntentRequest`s are routed by intent name; the `Device` slot is
|
|
resolved (exact match, normalized) against the cached entity list, and the
|
|
resulting `(entity_id, action, params)` triple is executed via
|
|
`ha-gateway`'s `Light`/`Switch`/`Climate` services over mTLS.
|
|
5. Resolution and execution failures produce a spoken failure response
|
|
rather than an HTTP error — a Dispatch-layer error would surface as
|
|
Alexa's own generic failure speech instead of a message this skill
|
|
controls.
|
|
|
|
## Configuration
|
|
|
|
Environment variables:
|
|
|
|
| Variable | Default | Description |
|
|
| --- | --- | --- |
|
|
| `HTTP_PORT` | `8080` | HTTP listen port |
|
|
| `ALEXA_SKILL_ID` | — (required) | Verified against the request envelope's application ID |
|
|
| `HA_GATEWAY_ADDR` | `ha-gateway.home-services.svc.cluster.local:50051` | gRPC address for `ha-gateway` |
|
|
| `HA_GATEWAY_SERVER_NAME` | `ha-gateway.home-services.svc.cluster.local` | Expected server name for mTLS |
|
|
| `TLS_DIR` | `/tls` | mTLS client cert dir for the `ha-gateway` call. Unlike `ai-gateway`/`discord-bot`, this defaults **on** rather than empty — alexa-bridge is internet-facing, so mTLS to `ha-gateway` should be on by default rather than opt-in. Set empty to disable for local plaintext dev. |
|
|
| `ENTITY_REFRESH_INTERVAL` | `5m` | Periodic entity list refresh cadence |
|
|
| `OTEL_ENDPOINT` | empty | OTLP gRPC collector endpoint; empty disables telemetry |
|
|
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
|
|
| `LOG_FORMAT` | `json` | `json` or `text` |
|
|
|
|
Example env file: [.env.example](.env.example)
|
|
|
|
When `TLS_DIR` is set, the directory must contain `tls.crt`, `tls.key`, and
|
|
`ca.crt`.
|
|
|
|
## Local Run
|
|
|
|
Start `ha-gateway` first, then run:
|
|
|
|
```bash
|
|
cd alexa-bridge
|
|
cp .env.example .env
|
|
# edit .env: set ALEXA_SKILL_ID, leave TLS_DIR empty for plaintext local dev
|
|
go run ./cmd/bridge
|
|
```
|
|
|
|
`alexa-bridge` doesn't register with Alexa on its own — during development,
|
|
point Alexa's request signature verification at a tunneled local endpoint
|
|
(e.g. `ngrok`) and set that HTTPS URL as the skill's endpoint in the Alexa
|
|
developer console. `/healthz` returns `200 OK` for liveness/readiness probes.
|
|
|
|
## HTTP API
|
|
|
|
There is no gRPC surface here (unlike the other three services) — this is a
|
|
plain HTTP server:
|
|
|
|
- `POST /`: the Alexa Custom Skill endpoint. Expects the standard Alexa
|
|
request envelope as JSON, with `SignatureCertChainUrl` and `Signature`
|
|
headers.
|
|
- `GET /healthz`: unauthenticated liveness/readiness check, always `200 OK`.
|
|
|
|
## Deployment
|
|
|
|
Kubernetes manifests (Deployment/Service, the `alexa-bridge-tls` mTLS client
|
|
cert, and the public HTTPS ingress) live in the separate `homelab` repo, not
|
|
here — see [plan.md](plan.md)'s mTLS section and "Decisions on open
|
|
questions" #2 for what's drafted there.
|