· limen

Home Assistant, con limen como límite

limen nació para que un agente de IA entrase en nuestras máquinas sin darle una shell. Ahora también lleva su estado a Home Assistant, desde donde gestionamos la oficina, con la misma regla: cada máquina decide qué se puede ejecutar en ella, y limen decide qué sale por MQTT y qué se puede pulsar desde allí.

limen, el límite del agente

limen es un servidor MCP que llega a cada máquina por SSH. Cada máquina dice en su limen.toml qué ficheros se pueden leer y qué packs de scripts ofrece, y cada script es una herramienta con los argumentos que declara su cabecera: limen los comprueba antes de ejecutarlo, y no hay shell ni línea de comandos. Los límites están en la máquina, no en el agente: cada permiso nuevo pasa por un pull request.

El mismo límite, para Home Assistant

El hub, limen serve, ya hablaba con todas las máquinas. Con [mqtt] se conecta además a un broker y usa el discovery de Home Assistant: cada máquina aparece como un dispositivo, sin integración que instalar. Y en el hub decidimos dos cosas:

# limen.toml del hub
[mqtt]
enabled = true
url = "mqtt://192.168.1.229:1883"
user = "limen"
password_file = "mqtt-password"

[[mqtt.sensor]]
node = "nas"
id = "disk_used"
script = "disk_used"
args = { path = "/srv" }
every = "60s"
discovery = { unit_of_measurement = "%" }

[[mqtt.button]]
node = "nas"
id = "restart_immich"
script = "restart_container"
args = { name = "immich_server" }

Que una máquina ofrezca un script no lo lleva a Home Assistant: solo llega lo que está escrito en el hub.

Un sensor es un script que imprime un valor

limen no interpreta nada de lo que ejecuta, tampoco aquí: el estado del sensor es la salida del script, ya redactada. Así que los scripts para sensores imprimen un solo valor, redondeado para que no cambie en cada lectura. El pack system trae cpu_used, memory_used, disk_used, temperature y gpu, y el pack docker, container_state:

# packs/system/disk_used
#!/bin/sh
#: description = "Percent used of the file system that holds a path, as a whole number"
#: read_only = true
#: [args.path]
#: type = "string"
#: default = "/"
#: pattern = '^/[A-Za-z0-9/._-]{0,255}$'
set -eu
df -P -- "$LIMEN_ARG_PATH" | awk 'NR == 2 { sub(/%/, "", $5); print $5 }'

Si hace falta un campo de un JSON, lo saca el value_template de Home Assistant, no limen.

Dónde sigue el límite

¿Y lnxlink?

lnxlink es el complemento natural para llevar un Linux a Home Assistant: se instala en cada máquina, publica decenas de sensores y ofrece acciones como apagarla. Nosotros preferimos que el agente y Home Assistant entren por la misma puerta: no hay nada más que instalar ni actualizar en las máquinas, y todo lo que Home Assistant ve o pulsa es un script que la máquina ofrece y que hemos declarado en el hub. El precio: un estado tiene la frescura de su every, cada lectura es una ejecución por SSH, solo hay los sensores para los que hay un script, y si el hub se para, Home Assistant ve todo como no disponible.

Los detalles, en la especificación de limen, y cómo escribir un script para un sensor, en su docs/scripts.md.