Nik Afiq b327150d45
All checks were successful
CI / test (push) Successful in 5s
CI / build-ai-gateway (push) Successful in 39s
CI / build-ha-gateway (push) Successful in 39s
CI / build-discord-bot (push) Successful in 40s
feat: implement ClimateApp for managing HVAC entities
- Added ClimateApp to handle climate entity operations including turning on/off, adjusting temperature, and setting HVAC modes.
- Implemented caching mechanism for climate entities to optimize state retrieval.
- Created domain model for Climate with attributes such as current temperature, target temperature, and HVAC modes.
- Developed unit tests for ClimateApp to ensure functionality and correctness.

feat: add RemoteApp for SwitchBot commands

- Introduced RemoteApp to facilitate sending commands to SwitchBot devices.
- Implemented SendCommand method for executing device commands without state management.

chore: update configuration for SwitchBot integration

- Added SwitchBotToken and SwitchBotSecret to configuration for enabling SwitchBot Cloud commands.

feat: define gRPC services for Climate and Remote operations

- Created climate.proto and remote.proto files to define gRPC services for climate management and remote command execution.
- Implemented corresponding request and response message structures for gRPC interactions.
2026-07-23 13:49:54 +09:00

144 lines
4.3 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 and, when
`SWITCHBOT_TOKEN`/`SWITCHBOT_SECRET` are set, a SwitchBot Cloud adapter.
3. Light, switch, and climate discovery caches are refreshed during startup
when possible.
4. gRPC clients call entity, light, switch, climate, remote, or event services.
5. The adapter maps requests to Home Assistant REST state/service calls, or
for `RemoteService`, signed SwitchBot Cloud Open API requests.
## 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.TurnOn`
- `SwitchService.TurnOff`
- `SwitchService.Toggle`
- `SwitchService.ListSwitches`
- `ClimateService.TurnOn`
- `ClimateService.TurnOff`
- `ClimateService.IncreaseTemperature`
- `ClimateService.DecreaseTemperature`
- `ClimateService.SetHVACMode`
- `ClimateService.ListClimates`
- `RemoteService.SendCommand`
Stubbed:
- `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` |
| `SWITCHBOT_TOKEN` | empty | optional; enables SwitchBot Cloud custom IR commands |
| `SWITCHBOT_SECRET` | empty | optional; enables SwitchBot Cloud custom IR commands |
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 ha.v1.ClimateService/ListClimates
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/adapters/secondary/switchbot/ # SwitchBot Cloud Open API adapter
internal/app/ # entity, light, switch, climate, and remote 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
- `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.