For the complete documentation index, see llms.txt. This page is also available as Markdown.

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) 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.

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:

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 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 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:

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

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:

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.

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.

Ú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.

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.

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.

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.

Valores derivados calculados (Tarea de script)

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

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.


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.

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.

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.


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.

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.

Última actualización