portfolio/content/projects/home-service.md
Nik Afiq c6ca4d23fc
All checks were successful
CI / build-check (push) Has been skipped
CI / build-and-push (push) Successful in 4m27s
Update .gitignore and add Home Service project documentation; enhance Homelab details
2026-05-06 19:50:06 +09:00

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.
go
grpc
protobuf
microservices
homeautomation
ollama
opentelemetry
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 dependencies
  • core/ports — interfaces the app layer depends on (driven ports)
  • adapters/primary — inbound adapters: gRPC server, Discord slash-command handler
  • adapters/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-gateway prompts 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 through ha-gateway — unsupported or malformed responses return a plain explanation without taking action
  • Light discovery results are cached in ai-gateway with a configurable TTL, reducing round-trips to ha-gateway on every AI query
  • mTLS enforced on all gRPC channels — TLS_DIR points each service to its cert bundle; certificates are issued by the cluster's internal CA via cert-manager
  • discord-bot tracks 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.