Nik Afiq 7a0b0f1540
All checks were successful
CI / test (push) Successful in 3s
CI / build-ha-gateway (push) Successful in 52s
CI / build-discord-bot (push) Successful in 45s
feat: add health check service to gRPC server
2026-04-06 21:17:21 +09:00
2026-03-25 19:51:34 +09:00
2026-03-25 19:52:15 +09:00
2026-03-25 19:52:15 +09:00

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 types
  • internal/core/ports/driving: interfaces exposed to primary adapters
  • internal/core/ports/driven: interfaces the app layer depends on
  • internal/app: application logic for entity, light, and switch operations
  • internal/adapters/primary/grpc: gRPC handlers and proto/domain mapping
  • internal/adapters/secondary/ha: Home Assistant REST client
  • internal/telemetry: OpenTelemetry setup

The runtime flow is:

  1. A gRPC client calls EntityService, LightService, or SwitchService.
  2. The gRPC adapter maps protobuf messages into domain parameters.
  3. The app layer orchestrates the use case.
  4. The HA adapter calls Home Assistant's REST API.
  5. 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.EntityService
    • GetState
    • ListStates
  • ha.v1.LightService
    • ListLights
    • TurnOn
    • TurnOff
    • Toggle
  • ha.v1.SwitchService
    • ListSwitches

Partially Stubbed

  • ha.v1.SwitchService
    • TurnOn
    • TurnOff
    • Toggle

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_TOKEN is missing
  • HA_BASE_URL is not validated on load, but the gateway cannot reach Home Assistant without it
  • if OTEL_ENDPOINT is empty, the service installs no-op telemetry providers

Prerequisites

  • Go 1.26
  • buf for 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
  • SwitchService control RPCs (TurnOn, TurnOff, Toggle) still return Unimplemented
  • EventService is 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.

Description
No description provided
Readme 326 KiB
Languages
Go 99.2%
Dockerfile 0.8%