> 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-pt/kilo-iot-server/rules-engine/cel-reference.md).

# Referência CEL

Referência de sintaxe CEL para regras do Kilo IoT — Common Expression Language usada em gateways, scripts e alarmes.

O Motor de Regras usa [CEL (Common Expression Language)](https://cel.dev) para expressões dentro do editor visual de workflows — condições de gateway, cálculos de Tarefas de Script, mensagens de alarme, pesquisas de enriquecimento e definições de entrada/saída. O CEL é uma linguagem de expressões rápida e segura, originalmente criada pelo Google para avaliar condições em políticas de segurança e sistemas de infraestrutura. A especificação completa da linguagem está disponível no [GitHub](https://github.com/google/cel-spec).

O CEL não é uma linguagem de programação de uso geral. Ele avalia expressões e devolve resultados. Não pode aceder ao sistema de ficheiros, fazer chamadas de rede, criar ciclos nem modificar o estado externo. Isto torna-o seguro para executar expressões definidas pelo utilizador sem risco para a plataforma ou para outras regras.

A maioria das regras do Kilo usa apenas algumas expressões curtas. A estrutura do workflow continua visual e baseada em BPMN; o CEL é a camada de precisão que torna esses workflows úteis em cenários reais de produção.

***

## Dados disponíveis

Cada expressão no Motor de Regras tem acesso às variáveis do processo através do `vars` objeto. Os dados disponíveis dependem de onde na regra a expressão é executada.

Os campos em `vars` podem ser acedidos com **notação de ponto** ou **notação de colchetes**:

```cel
vars.temperature        // notação de ponto — funciona para identificadores simples
vars["sensor_id"]       // notação de colchetes — funciona para qualquer nome
vars["my-sensor"]       // notação de colchetes obrigatória — hífen no nome
```

A notação de ponto é conveniente para a maioria dos nomes de campos. A notação de colchetes é obrigatória quando um nome de campo contém hífens, espaços ou outros caracteres especiais, ou quando o nome do campo é calculado dinamicamente a partir de outra expressão.

### Disponível após o Evento de Início

**O que o Evento de Início fornece depende da sua origem de início**, por isso verifique que tipo de regra está a escrever antes de procurar uma variável.

**Origem de início: leitura do sensor**

| Variável         | Tipo                     | Descrição                                                                                                                                        |
| ---------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `vars.value`     | Varia consoante o sensor | A leitura do sensor que disparou a regra. Pode ser um número (temperatura, humidade), string (estado da porta) ou booleano (movimento detetado). |
| `vars.sensor_id` | string                   | O identificador exclusivo do sensor que disparou a regra.                                                                                        |
| `vars.timestamp` | timestamp                | O momento em que a leitura do sensor foi registada.                                                                                              |

**Origem de início: condição de gatilho**

| Variável            | Tipo   | Descrição                                                                                                                                                                                 |
| ------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vars.device_name`  | string | O nome do dispositivo que satisfez a condição. Numa [gatilho](/kilo-docs-pt/kilo-iot-server/rules-engine/triggers.md) que monitoriza vários dispositivos, isto identifica o que disparou. |
| `vars.subject_kind` | string | O tipo de recurso monitorizado. Atualmente isto é `dispositivo`.                                                                                                                          |
| `vars.subject_id`   | string | O identificador do dispositivo monitorizado que satisfez a condição.                                                                                                                      |
| `vars.sensor_id`    | string | O identificador do sensor usado para associar a execução e qualquer alarme ao dispositivo monitorizado.                                                                                   |
| `vars.detector_id`  | string | O identificador do gatilho que iniciou a regra.                                                                                                                                           |
| `vars.timestamp`    | int    | O tempo do sinal de gatilho em segundos Unix.                                                                                                                                             |

> **`vars.value` não existe numa regra iniciada por gatilho.** Um gatilho reporta uma transição de condição em vez de entregar à regra um evento de sensor normalizado. Isto é verdade tanto para gatilhos imediatos como para gatilhos de duração. Uma expressão que se refira a `vars.value` falhará em todos os sinais de gatilho — verifique isto primeiro ao converter uma regra existente de **Leitura do sensor**.

### Disponível após Tarefas de Script

Se uma Tarefa de Script devolver um mapa, cada chave é mesclada em `vars`. Por exemplo, após uma Tarefa de Script com a expressão `{"level": "crítico", "delta": 15.2}`, os nós a jusante podem aceder a `vars.level` e `vars.delta`.

As saídas de Tarefa de Script, Gateway, Definir Alarme, Enriquecimento e Evento de Início também podem publicar valores nomeados em `vars` depois de esse nó ser executado.

### Disponível após Enriquecimento

Depois de um nó de Enriquecimento armazenar dados sob um nome de variável (por exemplo, `outdoor_temp`), os dados enriquecidos ficam acessíveis como um objeto aninhado:

| Variável                         | Tipo   | Descrição                                         |
| -------------------------------- | ------ | ------------------------------------------------- |
| `vars.outdoor_temp.value`        | Varia  | A leitura mais recente do sensor enriquecido.     |
| `vars.outdoor_temp.sensor_id`    | string | O identificador do sensor enriquecido.            |
| `vars.outdoor_temp.type`         | string | O tipo de dados do sensor enriquecido.            |
| `vars.outdoor_temp.timestamp_ms` | int    | Carimbo de data/hora da leitura em milissegundos. |

### Variáveis personalizadas

As expressões de entrada e saída em Eventos de Início, Gateways Exclusivos, Tarefas de Script, nós Definir Alarme e nós de Enriquecimento permitem-lhe nomear um valor calculado. Escolha entre elas conforme a distância que o valor precisa de percorrer:

* **Saídas** adicionam o nome ao contexto do workflow. Fica acessível como `vars.<name>` em qualquer nó executado depois.
* **Entradas** ficam no nó em que as definiu. São calculadas antes de esse nó ser executado e as suas próprias expressões podem usá-las — as entradas de um gateway ficam disponíveis para as condições de fluxo desse gateway — mas os nós posteriores não as podem ler.

Se um valor calculado num nó for necessário mais adiante na regra, torne-o uma saída. Veja [Referência de Nós](/kilo-docs-pt/kilo-iot-server/rules-engine/node-reference.md#do-i-need-the-input-and-output-parameters) para um exemplo.

***

## Sistema de tipos

O CEL suporta os seguintes tipos. Cada valor numa expressão é resolvido para um destes.

| Tipo        | Descrição                    | Exemplo                                |
| ----------- | ---------------------------- | -------------------------------------- |
| `bool`      | Booleano verdadeiro ou falso | `verdadeiro`, `vars.value > 30`        |
| `int`       | Inteiro com sinal            | `42`, `-7`                             |
| `uint`      | Inteiro sem sinal            | `42u`                                  |
| `double`    | Número de ponto flutuante    | `30.5`, `-0.7`                         |
| `string`    | Texto                        | `"crítico"`, `"Alerta de temperatura"` |
| `bytes`     | Sequência de bytes           | `b"\x00\xff"`                          |
| `list`      | Coleção ordenada             | `[1, 2, 3]`, `["a", "b"]`              |
| `map`       | Pares chave-valor            | `{"level": "alto", "count": 5}`        |
| `null_type` | Valor nulo                   | `null`                                 |
| `timestamp` | Um momento no tempo          | `vars.timestamp`                       |
| `duration`  | Um intervalo de tempo        | `duration("5m")`                       |

### Conversões de tipos

Use funções de conversão incorporadas para converter entre tipos:

| Função     | Descrição                        | Exemplo              |
| ---------- | -------------------------------- | -------------------- |
| `int()`    | Converter para inteiro           | `int(vars.value)`    |
| `uint()`   | Converter para inteiro sem sinal | `uint(42)`           |
| `double()` | Converter para double            | `double(vars.value)` |
| `string()` | Converter para string            | `string(vars.value)` |
| `type()`   | Devolve o tipo de um valor       | `type(vars.value)`   |

A conversão para string é particularmente importante quando se constroem mensagens de motivação de alarme, uma vez que o CEL exige conversão explícita de números para strings para concatenar.

***

## Operadores

### Operadores de comparação

| Operador | Significado      | Exemplo                    |
| -------- | ---------------- | -------------------------- |
| `==`     | Igual a          | `vars.value == 0`          |
| `!=`     | Diferente de     | `vars.status != "offline"` |
| `>`      | Maior que        | `vars.value > 30.0`        |
| `>=`     | Maior ou igual a | `vars.value >= 100`        |
| `<`      | Menor que        | `vars.value < 5`           |
| `<=`     | Menor ou igual a | `vars.value <= 25.0`       |

### Operadores lógicos

| Operador | Significado | Exemplo                                |
| -------- | ----------- | -------------------------------------- |
| `&&`     | 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   | Exemplo                                |
| -------- | ------------- | -------------------------------------- |
| `+`      | Adição        | `vars.value + 10`                      |
| `-`      | Subtração     | `vars.value - vars.outdoor_temp.value` |
| `*`      | Multiplicação | `vars.value * 1.8 + 32`                |
| `/`      | Divisão       | `vars.value / 100.0`                   |
| `%`      | Módulo        | `vars.value % 10`                      |

### Operador ternário

O operador ternário `? :` devolve um de dois valores com base numa condição:

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

As expressões ternárias podem ser aninhadas para classificação em vários níveis:

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

### Operadores e funções de strings

| Operação              | Sintaxe        | Exemplo                                 |
| --------------------- | -------------- | --------------------------------------- |
| Concatenação          | `+`            | `"Temp.: " + string(vars.value)`        |
| Comprimento           | `size()`       | `vars.name.size() > 0`                  |
| Contém                | `contains()`   | `vars.status.contains("error")`         |
| Começa com            | `startsWith()` | `vars.zone.startsWith("warehouse")`     |
| Termina com           | `endsWith()`   | `vars.device_id.endsWith("-prod")`      |
| Correspondência regex | `matches()`    | `vars.device_id.matches("^WH-[0-9]+$")` |

### Funções de coleção

| Função                     | Descrição                                                 | Exemplo                                             |
| -------------------------- | --------------------------------------------------------- | --------------------------------------------------- |
| `x na lista`               | Verificação de pertença                                   | `vars.zone in ["A", "B", "C"]`                      |
| `chave no mapa`            | A chave existe no mapa                                    | `"humidity" in vars`                                |
| `list.exists(x, expr)`     | Verdadeiro se qualquer elemento satisfizer a expressão    | `[10, 25, 40].exists(t, t > 30)`                    |
| `list.all(x, expr)`        | Verdadeiro se todos os elementos satisfizerem a expressão | `[10, 25, 40].all(t, t > 0)`                        |
| `list.filter(x, expr)`     | Devolve os elementos que satisfazem a expressão           | `[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)` | Verdadeiro se exatamente um elemento satisfizer           | `[10, 25, 40].exists_one(t, t > 30)` → `verdadeiro` |

### Variáveis intermédias com cel.bind

Use `cel.bind()` para definir uma variável temporária dentro de uma expressão, evitando computação redundante:

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

O primeiro argumento nomeia a variável, o segundo calcula o seu valor e o terceiro é a expressão que a usa.

### Verificação de existência

| Função  | Descrição                               | Exemplo              |
| ------- | --------------------------------------- | -------------------- |
| `has()` | Devolve `verdadeiro` se o campo existir | `has(vars.humidity)` |

Use `has()` antes de aceder a uma variável que pode não existir. Depois de um nó de Enriquecimento com um Evento de Erro de Fronteira, por exemplo, os dados enriquecidos só ficam disponíveis se o enriquecimento tiver sido bem-sucedido.

***

## Padrões de expressão comuns

### Verificação de limiar

O padrão mais simples e mais comum. Usado nas condições de gateway para ramificar com base num único valor.

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

Devolve `verdadeiro` se a leitura do sensor exceder 30. Funciona em condições de Gateway Exclusivo para encaminhar o fluxo.

### Verificação de intervalo

Testa se uma leitura se encontra dentro de uma faixa aceitável.

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

Útil para conformidade HVAC, monitorização da cadeia de frio ou qualquer cenário em que os limites superior e inferior sejam importantes.

### Verificação de múltiplas condições

Combine verificações em várias variáveis. Este padrão aparece frequentemente após o enriquecimento, quando estão disponíveis dados de mais do que um sensor.

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

Use sempre `has()` antes de referenciar variáveis que venham de fontes opcionais (enriquecimento, tarefas de script anteriores com saídas condicionais).

### Classificação de severidade (Tarefa de Script)

Devolva um mapa a partir de uma Tarefa de Script para classificar uma leitura em categorias. Cada chave torna-se uma variável de processo separada.

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

Depois desta Tarefa de Script, `vars.level` fica disponível para condições de gateway ou mensagens de alarme.

### Mensagem de alarme dinâmica (Definir Alarme)

Crie uma string legível que inclua dados do sensor em tempo real. O campo de mensagem de motivação num nó Definir Alarme usa este padrão.

```cel
"Temperatura " + string(vars.value) + " graus ultrapassou o limiar de " + string(vars.threshold)
```

Lembre-se de usar `string()` para converter valores numéricos — o CEL não converte implicitamente números em strings durante a concatenação.

### Verificação de dados enriquecidos (após Enriquecimento)

Referencie um valor obtido por um nó de Enriquecimento. O nome de variável usado na configuração de Enriquecimento torna-se a chave em `vars`.

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

### Valores derivados calculados (Tarefa de Script)

Calcule novos valores a partir de várias entradas e armazene-os para uso posterior.

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

Depois desta Tarefa de Script, o gateway pode verificar `vars.needs_alarm` diretamente, e a mensagem de alarme pode referenciar `vars.delta` para contexto.

### Combinar classificação com valores calculados

Uma única Tarefa de Script pode executar vários cálculos ao mesmo tempo.

```cel
{
  "severity": vars.value > 90 ? "crítico" : vars.value > 70 ? "aviso" : "normal",
  "deviation": vars.value - vars.baseline.value,
  "message": "Leitura " + string(vars.value) + ", desvio de " + string(vars.value - vars.baseline.value) + " do valor base"
}
```

***

## Onde o CEL é usado

As expressões CEL aparecem em vários pontos do Motor de Regras. O contexto determina o que a expressão deve devolver.

| Local                                      | Tipo de retorno esperado  | Objetivo                                                                                    |
| ------------------------------------------ | ------------------------- | ------------------------------------------------------------------------------------------- |
| **Evento de Início — Entradas**            | Qualquer                  | Crie valores auxiliares locais quando o evento do sensor entra na regra.                    |
| **Evento de Início — Saídas**              | Qualquer                  | Publique valores nomeados no contexto partilhado do workflow.                               |
| **Tarefa de Script — Script**              | Qualquer (mapa preferido) | Transforme ou classifique dados. As chaves do mapa são mescladas nas variáveis de processo. |
| **Tarefa de Script — Entradas**            | Qualquer                  | Crie valores auxiliares locais antes de o script ser executado.                             |
| **Tarefa de Script — Saídas**              | Qualquer                  | Publique valores nomeados adicionais depois de a tarefa ser executada.                      |
| **Gateway Exclusivo — Condição**           | `bool`                    | Encaminhe a execução. Deve devolver `verdadeiro` ou `falso`.                                |
| **Gateway Exclusivo — Entradas**           | Qualquer                  | Prepare valores locais usados pelas condições de ramificação.                               |
| **Gateway Exclusivo — Saídas**             | Qualquer                  | Publique valores depois da decisão de encaminhamento.                                       |
| **Definir Alarme — Mensagem de motivação** | `string`                  | Descreva por que o alarme foi disparado. Mostrado aos intervenientes.                       |
| **Definir Alarme — Entradas**              | Qualquer                  | Prepare valores antes de a ação de alarme ser executada.                                    |
| **Definir Alarme — Saídas**                | Qualquer                  | Publique valores depois de o nó de alarme ser executado.                                    |
| **Enriquecimento — ID do sensor**          | `string`                  | Identifique de que sensor obter dados.                                                      |
| **Enriquecimento — Entradas**              | Qualquer                  | Prepare valores antes de a consulta ser executada.                                          |
| **Enriquecimento — Saídas**                | Qualquer                  | Publique valores depois de o resultado do enriquecimento estar disponível.                  |

### Âmbito de Entradas e Saídas

As Entradas e Saídas num nó servem objetivos diferentes e têm visibilidades diferentes:

* **Entradas** criar **local** variáveis locais com âmbito apenas para o nó atual. Não modificam o contexto partilhado do workflow. As expressões de entrada são avaliadas em relação ao estado `vars` atual. Use-as para preparar valores auxiliares ou pré-calcular resultados intermédios antes de a lógica principal do nó ser executada.
* **Saídas** escrever valores no **partilhado** contexto do workflow (`vars`). As expressões de saída são avaliadas no escopo local do nó — que inclui tanto o original `vars` e quaisquer variáveis locais definidas pela entrada. Os valores publicados pelas Saídas persistem e são acessíveis a todos os nós subsequentes.

**Implicação prática:** Se definir uma entrada chamada `threshold` num Gateway Exclusivo, os nós subsequentes não conseguem ver `vars.threshold` — ele existe apenas durante a avaliação da condição desse gateway. Para tornar um valor calculado disponível a jusante, defina-o como uma Saída em vez disso.

***

## Segurança e sandboxing

O CEL é isolado por design. As expressões são executadas num ambiente restrito sem acesso a:

* O sistema de ficheiros
* Recursos de rede
* Relógios do sistema (exceto através das variáveis de carimbo de data/hora fornecidas)
* Serviços externos
* Estado mutável fora da própria avaliação da expressão

Uma expressão não pode criar ciclos infinitos, alocar memória ilimitada ou afetar outras regras. Se uma expressão falhar (erro de tipo, divisão por zero, referência a uma variável em falta sem `has()` proteção), o nó que a contém lança um erro. Anexe um Boundary Error Event para tratar estas falhas de forma elegante.

***

## Funções da plataforma

Além da biblioteca CEL padrão, o Rules Engine fornece duas funções específicas da plataforma.

### error(message)

Recebe um argumento de string e produz sempre um valor de erro. Quando uma expressão de Script Task é avaliada como erro, o motor verifica se existe um Boundary Error Event anexado. Se existir, a execução segue pelo caminho de erro. Se não, o erro interrompe a regra.

Use `error()` para falha condicional deliberada — situações em que uma condição específica dos dados deve acionar o caminho de tratamento de erro em vez de continuar a execução normal.

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

Neste exemplo, temperaturas acima de 200 falham deliberadamente a Script Task. Se um Boundary Error Event estiver anexado, o caminho de erro é executado (talvez acionando um alarme de emergência). Abaixo de 200, a Script Task produz `{"status": "ok"}` como normal.

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

### random()

Devolve um número de vírgula flutuante pseudoaleatório no intervalo \[0.0, 1.0). Útil para amostragem probabilística, encaminhamento baseado em percentagens ou geração de identificadores aleatórios.

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

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

***

## Dicas práticas

**Mantenha as expressões focadas.** Uma única expressão deve fazer apenas uma coisa. Se precisar classificar uma leitura, calcular um delta e construir uma mensagem, use várias Script Tasks em vez de uma expressão complexa. Isso torna a regra mais fácil de ler, depurar e manter.

**Dê prioridade à estrutura visual.** Use o canvas para mostrar o workflow e depois use o CEL nos campos relevantes. Um diagrama BPMN legível com algumas expressões claras é mais fácil de auditar do que uma regra que esconde lógica demais dentro de uma única expressão gigante.

**Use `has()` antes dos campos opcionais.** Qualquer variável que venha de enriquecimento, tarefas de script condicionais ou entradas opcionais pode não existir. Aceda-lhe sem `has()` e a expressão lança um erro.

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

**Converta os tipos explicitamente.** O CEL não realiza conversão implícita de tipos. Ao construir mensagens de alarme, converta números com `string()`. Ao realizar operações aritméticas, certifique-se de que ambos os operandos são do mesmo tipo numérico — misturar `int` e `double` pode produzir resultados inesperados.

**Use mapas para saídas com vários valores.** Devolver um mapa a partir de uma Script Task é a forma padrão de disponibilizar vários valores calculados a jusante. Cada chave torna-se uma variável de processo independente.

**Teste as expressões contra casos extremos.** Considere o que acontece quando um sensor reporta zero, um valor negativo ou um número inesperadamente grande. As condições do gateway devem lidar com toda a gama de entradas possíveis sem encaminhar para um ramo não pretendido.

**Nomeie as variáveis de forma descritiva.** Ao definir nomes de variáveis de Enrichment ou chaves de saída da Script Task, use nomes que descrevam os dados — `outdoor_temp`, `humidity_reading`, `severity_level` — e não `x`, `val2`, ou `tmp`. Os seus colegas irão lê-los ao rever ou modificar a regra.


---

# 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-pt/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.
