> For the complete documentation index, see [llms.txt](https://docs.kiloiot.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kiloiot.io/kilo-docs-es/kilo-iot-server/rules-engine/cel-reference.md).

# Referencia de CEL

Referencia de sintaxis CEL para las reglas de Kilo IoT — Common Expression Language usada en pasarelas, scripts y alarmas.

El motor de reglas usa [CEL (Common Expression Language)](https://cel.dev) para expresiones dentro del editor visual de flujos de trabajo: condiciones de compuerta, cálculos de tareas de script, mensajes de alarma, consultas de enriquecimiento y definiciones de entrada/salida. CEL es un lenguaje de expresiones rápido y seguro, diseñado originalmente por Google para evaluar condiciones en políticas de seguridad y sistemas de infraestructura. La especificación completa del lenguaje está disponible en [GitHub](https://github.com/google/cel-spec).

CEL no es un lenguaje de programación de propósito general. Evalúa expresiones y devuelve resultados. No puede acceder al sistema de archivos, realizar llamadas de red, crear bucles ni modificar el estado externo. Esto lo hace seguro para ejecutar expresiones definidas por el usuario sin riesgo para la plataforma ni para otras reglas.

La mayoría de las reglas de Kilo usan solo unas pocas expresiones cortas. La estructura del flujo de trabajo sigue siendo visual y basada en BPMN; CEL es la capa de precisión que hace que esos flujos de trabajo sean útiles en escenarios reales de producción.

***

## Datos disponibles

Cada expresión en el motor de reglas tiene acceso a las variables del proceso a través de `vars` objeto. Los datos disponibles dependen de dónde se ejecute la expresión en la regla.

Los campos en `vars` se pueden acceder con **notación con punto** o **notación con corchetes**:

```cel
vars.temperature        // notación con punto — funciona para identificadores simples
vars["sensor_id"]       // notación con corchetes — funciona para cualquier nombre
vars["my-sensor"]       // se requiere notación con corchetes — el nombre contiene un guion
```

La notación con punto es conveniente para la mayoría de los nombres de campo. La notación con corchetes es necesaria cuando un nombre de campo contiene guiones, espacios u otros caracteres especiales, o cuando el nombre del campo se calcula dinámicamente a partir de otra expresión.

### Disponible después del Evento de inicio

**Lo que proporciona el Evento de inicio depende de su fuente de inicio**, así que comprueba qué tipo de regla estás escribiendo antes de buscar una variable.

**Fuente de inicio: lectura del sensor**

| Variable         | Tipo                  | Descripción                                                                                                                                                   |
| ---------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vars.value`     | Varía según el sensor | La lectura del sensor que activó la regla. Puede ser un número (temperatura, humedad), una cadena (estado de la puerta) o un booleano (movimiento detectado). |
| `vars.sensor_id` | cadena                | El identificador único del sensor que activó la regla.                                                                                                        |
| `vars.timestamp` | marca temporal        | La hora en que se registró la lectura del sensor.                                                                                                             |

**Fuente de inicio: condición de activación**

| Variable            | Tipo   | Descripción                                                                                                                                                                                       |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vars.device_name`  | cadena | El nombre del dispositivo que cumplió la condición. En un [activador](/kilo-docs-es/kilo-iot-server/rules-engine/triggers.md) supervisando varios dispositivos, esto identifica al que se activó. |
| `vars.subject_kind` | cadena | El tipo de recurso supervisado. Actualmente esto es `dispositivo`.                                                                                                                                |
| `vars.subject_id`   | cadena | El identificador del dispositivo supervisado que cumplió la condición.                                                                                                                            |
| `vars.sensor_id`    | cadena | El identificador del sensor usado para asociar la ejecución y cualquier alarma con el dispositivo supervisado.                                                                                    |
| `vars.detector_id`  | cadena | El identificador del disparador que inició la regla.                                                                                                                                              |
| `vars.timestamp`    | int    | La hora de la señal del disparador como segundos Unix.                                                                                                                                            |

> **`vars.value` no existe en una regla iniciada por disparador.** Un disparador informa una transición de condición en lugar de entregar a la regla un evento de sensor normalizado. Esto es cierto tanto para disparadores inmediatos como de duración. Una expresión que haga referencia a `vars.value` fallará en cada señal del disparador — comprueba esto primero al convertir una regla existente de **Lectura del sensor**.

### Disponible después de Tareas de script

Si una Tarea de script devuelve un mapa, cada clave se fusiona en `vars`. Por ejemplo, después de una Tarea de script con la expresión `{"level": "crítico", "delta": 15.2}`, los nodos posteriores pueden acceder a `vars.level` y `vars.delta`.

Las salidas de Tarea de script, Compuerta, Establecer alarma, Enriquecimiento y Evento de inicio también pueden publicar valores con nombre en `vars` después de que ese nodo se ejecute.

### Disponible después del Enriquecimiento

Después de que un nodo de Enriquecimiento almacena datos bajo un nombre de variable (por ejemplo, `outdoor_temp`), los datos enriquecidos son accesibles como un objeto anidado:

| Variable                         | Tipo   | Descripción                                     |
| -------------------------------- | ------ | ----------------------------------------------- |
| `vars.outdoor_temp.value`        | Varía  | La lectura más reciente del sensor enriquecido. |
| `vars.outdoor_temp.sensor_id`    | cadena | El identificador del sensor enriquecido.        |
| `vars.outdoor_temp.type`         | cadena | El tipo de datos del sensor enriquecido.        |
| `vars.outdoor_temp.timestamp_ms` | int    | Marca temporal de la lectura en milisegundos.   |

### Variables personalizadas

Las expresiones de entrada y salida en Eventos de inicio, Compuertas exclusivas, Tareas de script, nodos de Establecer alarma y nodos de Enriquecimiento te permiten nombrar un valor calculado. Elige entre ellas según hasta dónde necesite viajar el valor:

* **Salidas** añaden el nombre al contexto del flujo de trabajo. Es accesible como `vars.<name>` en cada nodo que se ejecute después.
* **Entradas** permanecen en el nodo en el que las definiste. Se calculan antes de que ese nodo se ejecute y sus propias expresiones pueden usarlas — las entradas de una compuerta están disponibles para las condiciones de flujo de esa compuerta — pero los nodos posteriores no pueden leerlas.

Si un valor calculado en un nodo se necesita más adelante en la regla, conviértelo en una salida. Consulta [Referencia de nodos](/kilo-docs-es/kilo-iot-server/rules-engine/node-reference.md#do-i-need-the-input-and-output-parameters) para ver un ejemplo.

***

## Sistema de tipos

CEL admite los siguientes tipos. Cada valor de una expresión se resuelve en uno de ellos.

| Tipo             | Descripción                | Ejemplo                                |
| ---------------- | -------------------------- | -------------------------------------- |
| `bool`           | Verdadero o falso booleano | `true`, `vars.value > 30`              |
| `int`            | Entero con signo           | `42`, `-7`                             |
| `uint`           | Entero sin signo           | `42u`                                  |
| `double`         | Número de punto flotante   | `30.5`, `-0.7`                         |
| `cadena`         | Texto                      | `"crítico"`, `"Alerta de temperatura"` |
| `bytes`          | Secuencia de bytes         | `b"\x00\xff"`                          |
| `list`           | Colección ordenada         | `[1, 2, 3]`, `["a", "b"]`              |
| `map`            | Pares clave-valor          | `{"level": "alto", "count": 5}`        |
| `null_type`      | Valor nulo                 | `null`                                 |
| `marca temporal` | Un punto en el tiempo      | `vars.timestamp`                       |
| `duration`       | Un intervalo de tiempo     | `duration("5m")`                       |

### Conversiones de tipos

Usa funciones de conversión integradas para convertir entre tipos:

| Función    | Descripción                  | Ejemplo              |
| ---------- | ---------------------------- | -------------------- |
| `int()`    | Convertir a entero           | `int(vars.value)`    |
| `uint()`   | Convertir a entero sin signo | `uint(42)`           |
| `double()` | Convertir a doble            | `double(vars.value)` |
| `string()` | Convertir a cadena           | `string(vars.value)` |
| `type()`   | Devuelve el tipo de un valor | `type(vars.value)`   |

La conversión a cadena es especialmente importante al construir mensajes de motivación de alarma, ya que CEL requiere conversión explícita de números a cadenas para la concatenación.

***

## Operadores

### Operadores de comparación

| Operador | Significado       | Ejemplo                         |
| -------- | ----------------- | ------------------------------- |
| `==`     | Igual a           | `vars.value == 0`               |
| `!=`     | Distinto de       | `vars.status != "sin conexión"` |
| `>`      | Mayor que         | `vars.value > 30.0`             |
| `>=`     | Mayor o igual que | `vars.value >= 100`             |
| `<`      | Menor que         | `vars.value < 5`                |
| `<=`     | Menor o igual que | `vars.value <= 25.0`            |

### Operadores lógicos

| Operador | Significado | Ejemplo                                |
| -------- | ----------- | -------------------------------------- |
| `&&`     | AND lógico  | `vars.value > 30 && vars.value < 50`   |
| `\|\|`   | OR lógico   | `vars.value < 0 \|\| vars.value > 100` |
| `!`      | NOT lógico  | `!has(vars.humidity)`                  |

### Operadores aritméticos

| Operador | Significado    | Ejemplo                                |
| -------- | -------------- | -------------------------------------- |
| `+`      | Suma           | `vars.value + 10`                      |
| `-`      | Resta          | `vars.value - vars.outdoor_temp.value` |
| `*`      | Multiplicación | `vars.value * 1.8 + 32`                |
| `/`      | División       | `vars.value / 100.0`                   |
| `%`      | Módulo         | `vars.value % 10`                      |

### Operador ternario

El operador ternario `? :` devuelve uno de dos valores según una condición:

```cel
vars.value > 80 ? "crítico" : "normal"
```

Las expresiones ternarias se pueden anidar para una clasificación multinivel:

```cel
vars.value > 80 ? "crítico" : vars.value > 50 ? "advertencia" : "normal"
```

### Operadores y funciones de cadena

| Operación              | Sintaxis       | Ejemplo                                 |
| ---------------------- | -------------- | --------------------------------------- |
| Concatenación          | `+`            | `"Temp: " + string(vars.value)`         |
| Longitud               | `size()`       | `vars.name.size() > 0`                  |
| Contiene               | `contains()`   | `vars.status.contains("error")`         |
| Comienza con           | `startsWith()` | `vars.zone.startsWith("warehouse")`     |
| Termina con            | `endsWith()`   | `vars.device_id.endsWith("-prod")`      |
| Coincidencia con regex | `matches()`    | `vars.device_id.matches("^WH-[0-9]+$")` |

### Funciones de colecciones

| Función                    | Descripción                                              | Ejemplo                                       |
| -------------------------- | -------------------------------------------------------- | --------------------------------------------- |
| `x en list`                | Comprobación de pertenencia                              | `vars.zone in ["A", "B", "C"]`                |
| `key en map`               | La clave existe en el mapa                               | `"humidity" in vars`                          |
| `list.exists(x, expr)`     | Verdadero si cualquier elemento satisface la expresión   | `[10, 25, 40].exists(t, t > 30)`              |
| `list.all(x, expr)`        | Verdadero si todos los elementos satisfacen la expresión | `[10, 25, 40].all(t, t > 0)`                  |
| `list.filter(x, expr)`     | Devuelve los elementos que satisfacen la expresión       | `[10, 25, 40].filter(t, t > 20)` → `[25, 40]` |
| `list.map(x, expr)`        | Transforma cada elemento                                 | `[1, 2, 3].map(x, x * 10)` → `[10, 20, 30]`   |
| `list.exists_one(x, expr)` | Verdadero si exactamente un elemento satisface           | `[10, 25, 40].exists_one(t, t > 30)` → `true` |

### Variables intermedias con cel.bind

Utilice `cel.bind()` para definir una variable temporal dentro de una expresión, evitando cálculos redundantes:

```cel
cel.bind(delta, vars.value - vars.baseline.value,
  {"delta": delta, "severity": delta > 20 ? "crítico" : delta > 10 ? "advertencia" : "normal"}
)
```

El primer argumento nombra la variable, el segundo calcula su valor y el tercero es la expresión que la usa.

### Comprobación de existencia

| Función | Descripción                        | Ejemplo              |
| ------- | ---------------------------------- | -------------------- |
| `has()` | Devuelve `true` si el campo existe | `has(vars.humidity)` |

Utilice `has()` antes de acceder a una variable que podría no existir. Después de un nodo de Enriquecimiento con un Evento de error de borde, por ejemplo, los datos enriquecidos solo están disponibles si el enriquecimiento tuvo éxito.

***

## Patrones comunes de expresiones

### Comprobación de umbral

El patrón más simple y común. Se usa en condiciones de compuerta para ramificar en función de un solo valor.

```cel
vars.value > 30.0
```

Devuelve `true` si la lectura del sensor supera 30. Funciona en condiciones de Compuerta exclusiva para enrutar el flujo.

### Comprobación de rango

Comprueba si una lectura cae dentro de un intervalo aceptable.

```cel
vars.value >= 20.0 && vars.value <= 25.0
```

Útil para cumplimiento HVAC, supervisión de cadena de frío o cualquier escenario en el que importen tanto el límite superior como el inferior.

### Comprobación de múltiples condiciones

Combina comprobaciones entre varias variables. Este patrón suele aparecer después del enriquecimiento, cuando hay datos de más de un sensor disponibles.

```cel
vars.value > 30.0 && has(vars.humidity) && vars.humidity < 20
```

Usa siempre `has()` antes de hacer referencia a variables que provienen de fuentes opcionales (enriquecimiento, tareas de script previas con salidas condicionales).

### Clasificación de severidad (Tarea de script)

Devuelve un mapa desde una Tarea de script para clasificar una lectura en categorías. Cada clave se convierte en una variable de proceso separada.

```cel
{"level": vars.value > 80 ? "crítico" : vars.value > 50 ? "advertencia" : "normal"}
```

Después de esta Tarea de script, `vars.level` está disponible para condiciones de compuerta o mensajes de alarma.

### Mensaje de alarma dinámico (Establecer alarma)

Construye una cadena legible que incluye datos vivos del sensor. El campo del mensaje de motivación en un nodo de Establecer alarma usa este patrón.

```cel
"Temperatura " + string(vars.value) + " grados superó el umbral de " + string(vars.threshold)
```

Recuerda usar `string()` para convertir valores numéricos — CEL no convierte implícitamente números a cadenas durante la concatenación.

### Comprobación de datos enriquecidos (después del Enriquecimiento)

Haz referencia a un valor obtenido por un nodo de Enriquecimiento. El nombre de variable usado en la configuración de Enriquecimiento se convierte en la clave dentro de `vars`.

```cel
vars.outdoor_temp.value > 35.0
```

### Valores derivados calculados (Tarea de script)

Calcula nuevos valores a partir de múltiples entradas y guárdalos para su uso posterior.

```cel
{"delta": vars.value - vars.outdoor_temp.value, "needs_alarm": vars.value - vars.outdoor_temp.value > 10}
```

Después de esta Tarea de script, la compuerta puede comprobar `vars.needs_alarm` directamente, y el mensaje de alarma puede hacer referencia a `vars.delta` para contexto.

### Combinación de clasificación con valores calculados

Una sola Tarea de script puede realizar varios cálculos a la vez.

```cel
{
  "severity": vars.value > 90 ? "crítico" : vars.value > 70 ? "advertencia" : "normal",
  "deviation": vars.value - vars.baseline.value,
  "message": "Lectura " + string(vars.value) + ", desviación " + string(vars.value - vars.baseline.value) + " de la referencia"
}
```

***

## Dónde se usa CEL

Las expresiones CEL aparecen en varios lugares del motor de reglas. El contexto determina qué debe devolver la expresión.

| Ubicación                                     | Tipo de retorno esperado      | Propósito                                                                                   |
| --------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------- |
| **Evento de inicio — Entradas**               | Cualquiera                    | Crea valores auxiliares locales cuando el evento del sensor entra en la regla.              |
| **Evento de inicio — Salidas**                | Cualquiera                    | Publica valores con nombre en el contexto compartido del flujo de trabajo.                  |
| **Tarea de script — Script**                  | Cualquiera (se prefiere mapa) | Transforma o clasifica datos. Las claves del mapa se fusionan en las variables del proceso. |
| **Tarea de script — Entradas**                | Cualquiera                    | Crea valores auxiliares locales antes de que se ejecute el script.                          |
| **Tarea de script — Salidas**                 | Cualquiera                    | Publica valores adicionales con nombre después de que se ejecute la tarea.                  |
| **Compuerta exclusiva — Condición**           | `bool`                        | Enruta la ejecución. Debe devolver `true` o `false`.                                        |
| **Compuerta exclusiva — Entradas**            | Cualquiera                    | Prepara valores locales usados por las condiciones de la rama.                              |
| **Compuerta exclusiva — Salidas**             | Cualquiera                    | Publica valores después de la decisión de enrutamiento.                                     |
| **Establecer alarma — Mensaje de motivación** | `cadena`                      | Describe por qué se activó la alarma. Se muestra a los equipos de respuesta.                |
| **Establecer alarma — Entradas**              | Cualquiera                    | Prepara valores antes de que se ejecute la acción de alarma.                                |
| **Establecer alarma — Salidas**               | Cualquiera                    | Publica valores después de que se ejecute el nodo de alarma.                                |
| **Enriquecimiento — ID del sensor**           | `cadena`                      | Identifica de qué sensor obtener los datos.                                                 |
| **Enriquecimiento — Entradas**                | Cualquiera                    | Prepara valores antes de que se ejecute la consulta.                                        |
| **Enriquecimiento — Salidas**                 | Cualquiera                    | Publica valores después de que el resultado del enriquecimiento esté disponible.            |

### Alcance de Entradas y Salidas

Las Entradas y Salidas de un nodo sirven para propósitos diferentes y tienen distinta visibilidad:

* **Entradas** crear **locales** variables limitadas solo al nodo actual. No modifican el contexto compartido del flujo de trabajo. Las expresiones de entrada se evalúan contra el `vars` estado actual. Úsalas para preparar valores auxiliares o precomputar resultados intermedios antes de que se ejecute la lógica principal del nodo.
* **Salidas** escribir valores en el **compartido** contexto del flujo de trabajo (`vars`). Las expresiones de salida se evalúan en el ámbito local del nodo — que incluye tanto el original `vars` y cualquier variable local definida por la entrada. Los valores publicados por las salidas persisten y son accesibles para todos los nodos posteriores.

**Implicación práctica:** Si defines una entrada llamada `umbral` en un Gateway exclusivo, los nodos posteriores no pueden ver `vars.threshold` — solo existe durante la evaluación de la condición de ese gateway. Para hacer que un valor calculado esté disponible aguas abajo, defínelo como una salida en su lugar.

***

## Seguridad y aislamiento

CEL está aislado por diseño. Las expresiones se ejecutan en un entorno restringido sin acceso a:

* el sistema de archivos
* los recursos de red
* los relojes del sistema (excepto a través de las variables de marca de tiempo proporcionadas)
* servicios externos
* estado mutable fuera de la propia evaluación de la expresión

Una expresión no puede crear bucles infinitos, asignar memoria sin límites ni afectar a otras reglas. Si una expresión falla (error de tipo, división por cero, referencia a una variable ausente sin `has()` protección), el nodo que la contiene lanza un error. Adjunta un evento de error de borde para gestionar estos fallos con elegancia.

***

## Funciones de la plataforma

Además de la biblioteca estándar de CEL, el motor de reglas proporciona dos funciones específicas de la plataforma.

### error(message)

Toma un argumento de tipo cadena y siempre produce un valor de error. Cuando una expresión de una tarea de script se evalúa como error, el motor comprueba si hay un evento de error de borde adjunto. Si existe uno, la ejecución se enruta por la ruta de error. Si no, el error detiene la regla.

Utilice `error()` para un fallo condicional deliberado — situaciones en las que una condición específica de los datos debe activar la ruta de manejo de errores en lugar de continuar con la ejecución normal.

```cel
vars.temperature > 200 ? error("critical overheat detected") : {"status": "ok"}
```

En este ejemplo, las temperaturas superiores a 200 hacen fallar deliberadamente la tarea de script. Si hay un evento de error de borde adjunto, se ejecuta la ruta de error (quizá activando una alarma de emergencia). Por debajo de 200, la tarea de script muestra `{"status": "ok"}` como es normal.

```cel
has(vars.calibration_date) ? {"calibrated": true} : error("sensor not calibrated")
```

### random()

Devuelve un número de punto flotante pseudorrandom en el intervalo \[0.0, 1.0). Útil para muestreo probabilístico, enrutamiento basado en porcentaje o generación de identificadores aleatorios.

```cel
random() < 0.1 ? "sampled" : "skipped"
```

```cel
{"random_id": random() * 1000000.0}
```

***

## Consejos prácticos

**Mantén las expresiones enfocadas.** Una sola expresión debe hacer una sola cosa. Si necesitas clasificar una lectura, calcular un delta y construir un mensaje, usa varias tareas de script en lugar de una expresión compleja. Esto hace que la regla sea más fácil de leer, depurar y mantener.

**Prioriza primero la estructura visual.** Usa el lienzo para mostrar el flujo de trabajo y luego usa CEL dentro de los campos relevantes. Un diagrama BPMN legible con unas pocas expresiones claras es más fácil de auditar que una regla que oculta demasiada lógica dentro de una sola expresión enorme.

**Utilice `has()` antes de los campos opcionales.** Cualquier variable que provenga de enriquecimiento, tareas de script condicionales o entradas opcionales puede no existir. Accede a ella sin `has()` y la expresión lanza un error.

```cel
has(vars.outdoor_temp) && vars.outdoor_temp.value > 35.0
```

**Convierte los tipos explícitamente.** CEL no realiza conversión implícita de tipos. Al construir mensajes de alarma, convierte los números con `string()`. Al realizar operaciones aritméticas, asegúrate de que ambos operandos sean del mismo tipo numérico — mezclar `int` y `double` puede producir resultados inesperados.

**Usa mapas para salidas de múltiples valores.** Devolver un mapa desde una tarea de script es la forma estándar de poner varios valores calculados a disposición aguas abajo. Cada clave se convierte en una variable de proceso independiente.

**Prueba las expresiones frente a casos extremos.** Considera qué ocurre cuando un sensor informa cero, un valor negativo o un número inesperadamente grande. Las condiciones del gateway deben manejar todo el rango de entradas posibles sin enrutar a una rama no deseada.

**Nombra las variables de forma descriptiva.** Al definir nombres de variables de Enrichment o claves de salida de tareas de script, usa nombres que describan los datos — `outdoor_temp`, `humidity_reading`, `severity_level` — no `x`, `val2`, o `tmp`. Tus colegas los leerán al revisar o modificar la regla.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kiloiot.io/kilo-docs-es/kilo-iot-server/rules-engine/cel-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
