130 lines
3.7 KiB
Markdown
130 lines
3.7 KiB
Markdown
# 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.
|
|
3. Light and switch discovery caches are refreshed during startup when possible.
|
|
4. gRPC clients call entity, light, switch, or event services.
|
|
5. The adapter maps requests to Home Assistant REST state and service calls.
|
|
|
|
## gRPC API
|
|
|
|
Contracts are defined under [proto/ha/v1](https://gitea.nik4nao.com/nik/home-services/src/branch/main/proto/ha/v1).
|
|
|
|
Implemented:
|
|
|
|
- `EntityService.GetState`
|
|
- `EntityService.ListStates`
|
|
- `LightService.TurnOn`
|
|
- `LightService.TurnOff`
|
|
- `LightService.Toggle`
|
|
- `LightService.ListLights`
|
|
- `SwitchService.ListSwitches`
|
|
|
|
Stubbed:
|
|
|
|
- `SwitchService.TurnOn`
|
|
- `SwitchService.TurnOff`
|
|
- `SwitchService.Toggle`
|
|
- `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` |
|
|
|
|
Example env file: [.env.example](https://gitea.nik4nao.com/nik/home-services/src/branch/main/ha-gateway/.env.example)
|
|
|
|
When `TLS_DIR` is set, the directory must contain `tls.crt`, `tls.key`, and
|
|
`ca.crt`.
|
|
|
|
## Local Run
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
go run ./cmd/gateway
|
|
```
|
|
|
|
Run from `ha-gateway/` so `godotenv` loads `ha-gateway/.env`.
|
|
|
|
For local plaintext development:
|
|
|
|
```text
|
|
GRPC_PORT=50051
|
|
HA_BASE_URL=http://ha.home.arpa:8123
|
|
HA_TOKEN=<long-lived-token>
|
|
TLS_DIR=
|
|
```
|
|
|
|
## Smoke Checks
|
|
|
|
```bash
|
|
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 grpc.health.v1.Health/Check
|
|
```
|
|
|
|
## Test And Build
|
|
|
|
```bash
|
|
go test ./...
|
|
go build ./...
|
|
```
|
|
|
|
Build the container image from the workspace root:
|
|
|
|
```bash
|
|
docker build -f ha-gateway/Dockerfile -t ha-gateway:dev .
|
|
```
|
|
|
|
Optionally pass a build version:
|
|
|
|
```bash
|
|
docker build -f ha-gateway/Dockerfile --build-arg VERSION=$(git rev-parse --short HEAD) -t ha-gateway:dev .
|
|
```
|
|
|
|
## Package Map
|
|
|
|
```text
|
|
cmd/gateway/ # process entrypoint and wiring
|
|
internal/adapters/primary/grpc/ # gRPC service implementations
|
|
internal/adapters/secondary/ha/ # Home Assistant REST adapter
|
|
internal/app/ # entity, light, and switch 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
|
|
|
|
- Switch control RPCs return not implemented.
|
|
- `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.
|