- 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.
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 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
- The service loads
.env, configures logging and telemetry, blocking-fetches the initial entity list fromha-gateway'sEntityService(failing startup if that fails — nothing can resolve a device name without it), then starts serving HTTP onHTTP_PORT. - 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. - Each incoming Alexa request is verified:
SignatureCertChainUrl/Signatureheaders (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 againstALEXA_SKILL_ID. - Verified
IntentRequests are routed by intent name; theDeviceslot is resolved (exact match, normalized) against the cached entity list, and the resulting(entity_id, action, params)triple is executed viaha-gateway'sLight/Switch/Climateservices over mTLS. - 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
When TLS_DIR is set, the directory must contain tls.crt, tls.key, and
ca.crt.
Local Run
Start ha-gateway first, then run:
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, withSignatureCertChainUrlandSignatureheaders.GET /healthz: unauthenticated liveness/readiness check, always200 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's mTLS section and "Decisions on open
questions" #2 for what's drafted there.