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 guionLa 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
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
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.valueno 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 avars.valuefallará 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:
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.
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:
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
==
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
&&
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
+
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
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
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
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.
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
varsestado 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 originalvarsy 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