3.9 KiB
3.9 KiB
title, date, draft, description, tags, github, url
| title | date | draft | description | tags | github | url | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Home Service | 2026-05-06 | false | Discord slash-command bot for home automation, built with Go microservices, gRPC, and a local LLM for natural language intent detection. |
|
https://gitea.nik4nao.com/nik/home-services |
Overview
A Go microservice system that controls Home Assistant devices through Discord slash commands.
Direct commands (/light, /switch) go straight to a typed gRPC gateway. Free-form
natural language (/ai query) routes through a local LLM (Ollama) for intent extraction
before dispatching the resolved action. All three services run on my homelab Kubernetes
cluster, deployed via ArgoCD.
Architecture
Discord user
│
├── /light, /switch ──(gRPC / mTLS)──▶ ha-gateway ──(REST)──▶ Home Assistant
│
└── /ai query ────────(gRPC / mTLS)──▶ ai-gateway ──(HTTP)──▶ Ollama
│ (structured JSON intent)
└──(gRPC / mTLS)──▶ ha-gateway ──(REST)──▶ Home Assistant
Three services, each its own Go module, communicating over mTLS-secured gRPC.
Proto definitions live in a shared gen module compiled with buf.
Stack
- Language: Go — multi-module workspace (
go.work) - API layer: gRPC + Protocol Buffers, schema managed with buf
- Auth: mTLS between all services — certificates provisioned by cert-manager
- LLM: Ollama (local, self-hosted) — structured JSON intent extraction
- External APIs: Home Assistant REST API, Discord Gateway API
- Observability: OpenTelemetry instrumentation in every service, traces exported to Tempo
- CI/CD: Gitea Actions — multi-arch image builds (amd64 + arm64) on every push to
main - Deployment: K3s via ArgoCD, credentials managed with Sealed Secrets
Architecture Pattern
Each service follows a hexagonal (ports and adapters) layout:
core/domain— domain types (intents, lights, entities), no framework dependenciescore/ports— interfaces the app layer depends on (driven ports)adapters/primary— inbound adapters: gRPC server, Discord slash-command handleradapters/secondary— outbound adapters: Home Assistant REST client, Ollama HTTP client, gRPC clients
Dependencies point inward — adapters depend on ports, ports know nothing about adapters.
Highlights
ai-gatewayprompts Ollama with user text and a cached list of known HA lights, parses the model's structured JSON response as an intent, and executes it throughha-gateway— unsupported or malformed responses return a plain explanation without taking action- Light discovery results are cached in
ai-gatewaywith a configurable TTL, reducing round-trips toha-gatewayon every AI query - mTLS enforced on all gRPC channels —
TLS_DIRpoints each service to its cert bundle; certificates are issued by the cluster's internal CA via cert-manager discord-bottracks in-flight AI requests so graceful shutdown waits briefly before terminating, avoiding dropped responses- OpenTelemetry spans propagated across all three service boundaries — full distributed traces visible in Grafana Tempo
- Multi-arch images (amd64 + arm64) built on every push via Gitea Actions and pushed to the self-hosted container registry
- Test coverage at the domain and adapter layer in each service
Services
| Service | Port | Responsibility |
|---|---|---|
ha-gateway |
50051 | gRPC boundary for Home Assistant — entity, light, and switch RPCs |
ai-gateway |
50052 | Intent extraction via Ollama, dispatches resolved actions to ha-gateway |
discord-bot |
— | Slash-command registration and routing (/light, /switch, /ai) |
Status
Active and in daily use.