Nik Afiq 39b7d6d4b4
All checks were successful
CI / changes (push) Successful in 19s
CI / test (push) Successful in 31s
CI / build-ai-gateway (push) Successful in 1m8s
CI / build-ha-gateway (push) Successful in 1m19s
CI / build-discord-bot (push) Successful in 59s
CI / build-alexa-bridge (push) Successful in 1m8s
CI / build-alert-bridge (push) Successful in 1m17s
CI / build-tts-gateway (push) Successful in 1m10s
CI / build-tts-sidecar (push) Has been skipped
CI / build-tts-model (push) Has been skipped
feat: add alert-bridge service for external cronjob -> Discord alerts
New standalone service that receives authenticated HTTP POSTs (e.g. from a
cronjob elsewhere on the home network) and posts them to a Discord channel
via an Incoming Webhook. Fire-and-forget, no dependency on discord-bot's bot
session at all - mirrors alexa-bridge's precedent of a small bridge service
with its own HTTP listener and auth rather than adding an inbound edge to an
existing internal service.

Wires alert-bridge into go.work, CI (changes filter, test job, new
build-alert-bridge job), and the other five Dockerfiles' COPY lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-01 22:45:18 +09:00
..
2026-03-25 19:52:15 +09:00

ha-gateway

ha-gateway is the internal gRPC boundary for Home Assistant. It exposes a small protobuf API to other home services and keeps Home Assistant tokens and REST details out of clients.

Runtime Flow

  1. The service loads .env, configures logging and telemetry, and starts gRPC on GRPC_PORT.
  2. App services are wired to a Home Assistant REST adapter and, when SWITCHBOT_TOKEN/SWITCHBOT_SECRET are set, a SwitchBot Cloud adapter.
  3. Light, switch, and climate discovery caches are refreshed during startup when possible.
  4. gRPC clients call entity, light, switch, climate, remote, or event services.
  5. The adapter maps requests to Home Assistant REST state/service calls, or for RemoteService, signed SwitchBot Cloud Open API requests.

gRPC API

Contracts are defined under proto/ha/v1.

Implemented:

  • EntityService.GetState
  • EntityService.ListStates
  • LightService.TurnOn
  • LightService.TurnOff
  • LightService.Toggle
  • LightService.ListLights
  • SwitchService.TurnOn
  • SwitchService.TurnOff
  • SwitchService.Toggle
  • SwitchService.ListSwitches
  • ClimateService.TurnOn
  • ClimateService.TurnOff
  • ClimateService.IncreaseTemperature
  • ClimateService.DecreaseTemperature
  • ClimateService.SetHVACMode
  • ClimateService.ListClimates
  • RemoteService.SendCommand

Stubbed:

  • EventService

The server also registers gRPC health checks and reflection.

Configuration

Environment variables:

Variable Default Description
GRPC_PORT 50051 gRPC listen port
HA_BASE_URL empty Home Assistant base URL, for example http://ha.home.arpa:8123
HA_TOKEN required Home Assistant long-lived access token
TLS_DIR empty Enables mTLS for the gRPC server when set
OTEL_ENDPOINT empty OTLP gRPC collector endpoint; empty disables telemetry
LOG_LEVEL info debug, info, warn, or error
LOG_FORMAT json json or text
SWITCHBOT_TOKEN empty optional; enables SwitchBot Cloud custom IR commands
SWITCHBOT_SECRET empty optional; enables SwitchBot Cloud custom IR commands

Example env file: .env.example

When TLS_DIR is set, the directory must contain tls.crt, tls.key, and ca.crt.

Local Run

cp .env.example .env
go run ./cmd/gateway

Run from ha-gateway/ so godotenv loads ha-gateway/.env.

For local plaintext development:

GRPC_PORT=50051
HA_BASE_URL=http://ha.home.arpa:8123
HA_TOKEN=<long-lived-token>
TLS_DIR=

Smoke Checks

grpcurl -plaintext -d '{"domain":"light"}' \
  localhost:50051 ha.v1.EntityService/ListStates

grpcurl -plaintext -d '{}' localhost:50051 ha.v1.LightService/ListLights

grpcurl -plaintext -d '{"entity_id":"light.living_room","brightness_pct":80}' \
  localhost:50051 ha.v1.LightService/TurnOn

grpcurl -plaintext -d '{}' localhost:50051 ha.v1.ClimateService/ListClimates

grpcurl -plaintext -d '{}' localhost:50051 grpc.health.v1.Health/Check

Test And Build

go test ./...
go build ./...

Build the container image from the workspace root:

docker build -f ha-gateway/Dockerfile -t ha-gateway:dev .

Optionally pass a build version:

docker build -f ha-gateway/Dockerfile --build-arg VERSION=$(git rev-parse --short HEAD) -t ha-gateway:dev .

Package Map

cmd/gateway/                         # process entrypoint and wiring
internal/adapters/primary/grpc/      # gRPC service implementations
internal/adapters/secondary/ha/      # Home Assistant REST adapter
internal/adapters/secondary/switchbot/ # SwitchBot Cloud Open API adapter
internal/app/                        # entity, light, switch, climate, and remote orchestration
internal/config/                     # environment loading
internal/core/domain/                # domain types
internal/core/ports/                 # driving and driven interfaces
internal/logger/                     # slog setup
internal/telemetry/                  # OpenTelemetry setup

Limitations

  • EventService is registered but not implemented.
  • Home Assistant WebSocket event streaming is still a TODO.
  • Keep this service internal or protect it with mTLS; it does not implement separate app-layer authorization.