- Added command handler for processing Discord interactions related to lights and switches. - Implemented command registration for light control commands: list, on, off, and toggle. - Created a gRPC client for communicating with the home automation gateway. - Developed application logic for handling light and switch commands, including listing, turning on/off, and toggling lights. - Introduced telemetry setup for OpenTelemetry integration. - Added configuration loading for Discord token, gateway address, and OpenTelemetry endpoint. - Defined core driven ports for interacting with the home automation gateway.
home-service
This workspace contains a Home Assistant gRPC gateway and the protobuf contract it serves.
The current implementation is centered on ha-gateway, a Go service that:
- exposes Home Assistant operations over gRPC
- translates gRPC requests into Home Assistant REST API calls
- emits OpenTelemetry traces and metrics when configured
- keeps protobuf definitions and generated Go stubs in-repo
EntityService is implemented, LightService supports both discovery and
control, SwitchService supports discovery, and EventService is still
scaffolded.
Workspace Layout
.
├── proto/ # Source protobuf definitions
├── gen/ # Generated Go protobuf/grpc code (committed)
├── ha-gateway/ # Go gRPC server
├── buf.yaml # Buf module config
├── buf.gen.yaml # Buf codegen config
└── go.work # Go workspace linking gen + ha-gateway
Architecture
ha-gateway follows a ports-and-adapters structure:
internal/core/domain: pure domain typesinternal/core/ports/driving: interfaces exposed to primary adaptersinternal/core/ports/driven: interfaces the app layer depends oninternal/app: application logic for entity, light, and switch operationsinternal/adapters/primary/grpc: gRPC handlers and proto/domain mappinginternal/adapters/secondary/ha: Home Assistant REST clientinternal/telemetry: OpenTelemetry setup
The runtime flow is:
- A gRPC client calls
EntityService,LightService, orSwitchService. - The gRPC adapter maps protobuf messages into domain parameters.
- The app layer orchestrates the use case.
- The HA adapter calls Home Assistant's REST API.
- The response is mapped back into the protobuf response.
For discovery-style RPCs, the light and switch apps cache filtered entity lists from Home Assistant state snapshots. The cache is primed at startup on a best-effort basis and lazily refreshed on the first request if startup discovery fails.
Services
Implemented
ha.v1.EntityServiceGetStateListStates
ha.v1.LightServiceListLightsTurnOnTurnOffToggle
ha.v1.SwitchServiceListSwitches
Partially Stubbed
ha.v1.SwitchServiceTurnOnTurnOffToggle
Stubbed
ha.v1.EventService
The event path is planned around Home Assistant WebSocket subscriptions, but the WebSocket adapter and fan-out broker are not implemented yet.
Configuration
ha-gateway reads configuration from environment variables. A sample file lives
at ha-gateway/.env.example.
| Variable | Required | Default | Notes |
|---|---|---|---|
HA_TOKEN |
yes | none | Home Assistant long-lived access token |
GRPC_PORT |
no | 50051 |
gRPC listen port |
HA_BASE_URL |
effectively yes | empty | Example: http://ha.home.arpa:8123 |
OTEL_ENDPOINT |
no | empty | OTLP gRPC endpoint; empty disables telemetry |
Notes:
- startup fails if
HA_TOKENis missing HA_BASE_URLis not validated on load, but the gateway cannot reach Home Assistant without it- if
OTEL_ENDPOINTis empty, the service installs no-op telemetry providers
Prerequisites
- Go
1.26 buffor protobuf generation- a reachable Home Assistant instance
- a valid Home Assistant long-lived access token
Generate Protobuf Code
Run from the repo root:
buf generate
Generated files are written to gen/ha/v1.
Build
Sync the workspace, then build the gateway:
go work sync
cd ha-gateway
go build ./...
Run Locally
Create a local env file:
cp ha-gateway/.env.example ha-gateway/.env
Fill in HA_TOKEN and HA_BASE_URL, then start the server:
cd ha-gateway
go run ./cmd/gateway
The gateway listens on :50051 by default.
Smoke Test With grpcurl
The server registers gRPC reflection, so grpcurl can inspect it directly:
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe ha.v1.LightService
Examples against a locally running gateway:
# List states filtered by domain
grpcurl -plaintext -d '{"domain":"light"}' \
localhost:50051 ha.v1.EntityService/ListStates
# List discovered lights
grpcurl -plaintext -d '{}' \
localhost:50051 ha.v1.LightService/ListLights
# List discovered switches
grpcurl -plaintext -d '{}' \
localhost:50051 ha.v1.SwitchService/ListSwitches
# Get one entity
grpcurl -plaintext -d '{"entity_id":"light.living_room"}' \
localhost:50051 ha.v1.EntityService/GetState
# Turn on a light at 80% brightness
grpcurl -plaintext -d '{"entity_id":"light.living_room","brightness_pct":80}' \
localhost:50051 ha.v1.LightService/TurnOn
# Toggle a light
grpcurl -plaintext -d '{"entity_id":"light.living_room"}' \
localhost:50051 ha.v1.LightService/Toggle
Docker
Build from the repo root:
docker build -f ha-gateway/Dockerfile -t ha-gateway:dev .
Run it with the same env file:
docker run --env-file ha-gateway/.env -p 50051:50051 ha-gateway:dev
Telemetry
When OTEL_ENDPOINT is set, the gateway exports:
- traces via OTLP/gRPC
- metrics via OTLP/gRPC
The service name is ha-gateway. When OTEL_ENDPOINT is unset, telemetry is
disabled for local development.
Current Limitations
- no authentication/authorization on inbound gRPC requests yet
SwitchServicecontrol RPCs (TurnOn,TurnOff,Toggle) still returnUnimplementedEventServiceis not implemented- Home Assistant event streaming over WebSocket is not implemented
- there are currently no unit tests in the repo
The auth note in ha-gateway/cmd/gateway/main.go explicitly calls out API-key and mTLS as future options before exposing the gateway outside a trusted network.