- 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.
217 lines
5.8 KiB
Markdown
217 lines
5.8 KiB
Markdown
# 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
|
|
|
|
```text
|
|
.
|
|
├── 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](/Users/nik-macbookair/repo/home-service/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:
|
|
|
|
```bash
|
|
buf generate
|
|
```
|
|
|
|
Generated files are written to `gen/ha/v1`.
|
|
|
|
## Build
|
|
|
|
Sync the workspace, then build the gateway:
|
|
|
|
```bash
|
|
go work sync
|
|
cd ha-gateway
|
|
go build ./...
|
|
```
|
|
|
|
## Run Locally
|
|
|
|
Create a local env file:
|
|
|
|
```bash
|
|
cp ha-gateway/.env.example ha-gateway/.env
|
|
```
|
|
|
|
Fill in `HA_TOKEN` and `HA_BASE_URL`, then start the server:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
grpcurl -plaintext localhost:50051 list
|
|
grpcurl -plaintext localhost:50051 describe ha.v1.LightService
|
|
```
|
|
|
|
Examples against a locally running gateway:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
docker build -f ha-gateway/Dockerfile -t ha-gateway:dev .
|
|
```
|
|
|
|
Run it with the same env file:
|
|
|
|
```bash
|
|
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](/Users/nik-macbookair/repo/home-service/ha-gateway/cmd/gateway/main.go) explicitly calls out API-key and mTLS as future options before exposing the gateway outside a trusted network.
|