home-services/README.md
Nik Afiq 94ab7ea42b feat(discord-bot): implement Discord command handler and register commands for light and switch control
- 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.
2026-04-06 20:13:15 +09:00

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.