Saltar a contenido

Netsocs Template Language

El Netsocs Template Language es el motor de expresiones de Netsocs que permite construir valores dinámicos dentro de cualquier campo de texto. Se usa en configuraciones de acciones, payloads de eventos, filtros de dashboards y cualquier campo que soporte templates.


Sintaxis básica

Las expresiones se escriben entre dobles llaves {{ }}. Todo lo que esté fuera de las llaves se trata como texto literal.

{{expresión}}

Puede haber varias expresiones en una misma cadena:

"Sensor {{sensor_id}} reporta {{get_state(sensor_id)}}"

Regla clave: Una plantilla con una sola expresión devuelve el tipo nativo del resultado (número, booleano, objeto, array). Una plantilla con texto mixto o múltiples expresiones siempre devuelve string.


Probar plantillas

En el playground escribes la plantilla directamente en el campo TEMPLATE (texto plano, sin envolverla en JSON). Abajo, en CONTEXT, defines en formato JSON las variables disponibles para la plantilla. El panel OUTPUT muestra en tiempo real el resultado y su tipo.

Template Language Playground

En el ejemplo de la imagen:

  • TEMPLATE:
    Hello, {{name}}! The sum is {{n_sum(1, 2, 3)}}.
    
  • CONTEXT:
    {
      "name": "World"
    }
    
  • OUTPUT: Hello, World! The sum is 6. (tipo string)

El context inyecta las variables disponibles dentro de {{ }}. Las funciones integradas —tanto las de Netsocs como las nativas del lenguaje— siempre están disponibles sin declarar nada en el contexto.

En los ejemplos de este documento la plantilla se muestra tal cual se escribe en TEMPLATE; cuando un ejemplo necesita variables, su JSON corresponde al campo CONTEXT.


Variables de contexto

Las variables pasadas en context se referencian directamente por nombre dentro de {{ }}.

{
  "template": "{{nombre}} tiene {{edad}} años",
  "context": {
    "nombre": "Ana",
    "edad": 30
  }
}
Resultado: "Ana tiene 30 años"

Para acceder a propiedades anidadas se usa punto .:

{
  "template": "{{device.brand}}",
  "context": {
    "device": { "brand": "Hanwha", "model": "XNV-8080R" }
  }
}
Resultado: "Hanwha"


Tipos de resultado

Tipo Descripción Ejemplo de template
string Texto, o mezcla de texto+expresión "Hola {{nombre}}", "texto"
number Número entero o decimal {{42}}, {{1 + 2.5}}
boolean true o false {{5 > 3}}
object JSON object {{{ "k": "v" }}}
array JSON array {{[1, 2, 3]}}
null Nulo explícito (retornado por funciones vacías)

Operadores

Aritméticos

Operador Descripción Ejemplo Resultado
+ Suma {{10 + 5}} 15
- Resta {{10 - 3}} 7
* Multiplicación {{4 * 3}} 12
/ División {{10 / 4}} 2.5
% Módulo {{10 % 3}} 1

Comparación

Operador Ejemplo Resultado
== {{3 == 3}} true
!= {{3 != 4}} true
> {{5 > 3}} true
< {{2 < 4}} true
>= {{5 >= 5}} true
<= {{4 <= 4}} true

Lógicos

Operador Ejemplo Resultado
&& {{true && false}} false
\|\| {{true \|\| false}} true
! {{!true}} false

Ternario

{{condición ? "valor_si_verdadero" : "valor_si_falso"}}

Ejemplo:

{{"Temperatura " + (temperatura > 30 ? "ALTA" : "normal")}}

Con temperatura = 35"Temperatura ALTA"


Funciones matemáticas

n_sum(...valores) / n_suma(...valores)

Suma todos los argumentos. Acepta números, strings numéricos, y arrays.

{{n_sum(1, 2, 3)}}           → 6
{{n_sum(1, '2.5', 3)}}       → 6.5
{{n_sum([10, 20, 30])}}      → 60
{{n_suma(5, 10)}}            → 15   (alias en español)

Uso con contexto:

{
  "template": "{{n_sum(temp_interior, temp_exterior) / 2}}",
  "context": { "temp_interior": 22, "temp_exterior": 18 }
}
Resultado: 20


n_multiply(...valores) / n_multiplicar(...valores)

Multiplica todos los argumentos. Acepta números, strings numéricos, y arrays.

{{n_multiply(2, 3, 4)}}      → 24
{{n_multiply(1.5, 2.0)}}     → 3
{{n_multiply([2, 5])}}       → 10

Conversión de unidades:

{
  "template": "{{n_multiply(voltaje, corriente)}} W",
  "context": { "voltaje": 220, "corriente": 2.5 }
}
Resultado: "550 W"


n_abs(valor)

Devuelve el valor absoluto.

{{n_abs(-15)}}    → 15
{{n_abs(7.3)}}    → 7.3
{{n_abs(-0.5)}}   → 0.5

add(a, b)

Suma dos enteros.

{{add(5, 3)}}    → 8

Para sumar más de dos valores o trabajar con decimales, preferir n_sum.


Funciones de comparación numérica

Estas funciones son útiles cuando los valores vienen como strings del estado de un objeto (donde "25.5" > "9" fallaría con operadores nativos).

n_less_than(a, b) / n_menor_que(a, b)

{{n_less_than(3, 5)}}         → true
{{n_less_than('9', '25.5')}}  → true   (compara correctamente strings numéricos)
{{n_less_than(5, 5)}}         → false

n_greater_than(a, b) / n_mayor_que(a, b)

{{n_greater_than(10, 3)}}     → true
{{n_greater_than(3, 10)}}     → false

n_less_equal_than(a, b) / n_menor_igual_que(a, b)

{{n_less_equal_than(5, 5)}}   → true
{{n_less_equal_than(6, 5)}}   → false

n_greater_equal_than(a, b) / n_mayor_igual_que(a, b)

{{n_greater_equal_than(5, 5)}}  → true
{{n_greater_equal_than(4, 5)}}  → false

n_equal(a, b) / n_igual(a, b)

Comparación numérica estricta. Acepta strings numéricos.

{{n_equal(1, 1)}}           → true
{{n_equal('1.5', 1.5)}}     → true
{{n_equal(1, 2)}}           → false

n_equal_epsilon(a, b, epsilon) / n_igual_epsilon(a, b, epsilon)

Compara si dos valores son iguales dentro de un margen de tolerancia. Útil para sensores con ruido.

{{n_equal_epsilon(25.001, 25.002, 0.01)}}   → true
{{n_equal_epsilon(25.0, 25.1, 0.01)}}       → false

Ejemplo real: Alertar si la temperatura se desvía más de 0.5°C del setpoint:

{
  "template": "{{n_not(n_equal_epsilon(temp_actual, temp_setpoint, 0.5))}}",
  "context": { "temp_actual": 22.8, "temp_setpoint": 22.0 }
}
Resultado: true (hay desviación mayor a 0.5°C)


Funciones lógicas

n_and(a, b)

AND lógico. Acepta booleans, strings ("true"/"false"/"1"/"0"/"on"/"off") y números.

{{n_and(true, true)}}       → true
{{n_and(true, false)}}      → false
{{n_and("1", "on")}}        → true
{{n_and(1, 0)}}             → false

n_or(a, b)

OR lógico con los mismos tipos que n_and.

{{n_or(true, false)}}       → true
{{n_or(false, false)}}      → false
{{n_or("0", "off")}}        → false

n_not(a)

Negación lógica.

{{n_not(true)}}             → false
{{n_not(false)}}            → true
{{n_not("on")}}             → false
{{n_not(0)}}                → true

Ejemplo combinado: Verificar que una puerta esté cerrada Y la alarma esté activa:

{
  "template": "{{n_and(get_state_property('door.main', 'status'), get_state_property('alarm.zone1', 'armed'))}}",
  "context": {}
}


Funciones de string

netconcat(a, b)

Concatena dos strings.

{{netconcat("sensor.", "001")}}    → "sensor.001"

Para concatenación con el operador nativo, se puede usar + directamente en la expresión cuando ambos valores son strings del contexto.


letter_add(letra, cantidad) / sumar_letra(letra, cantidad)

Suma un entero a una letra avanzando en orden alfabético inglés, tratando las letras como un número en base 26 estilo columnas de hoja de cálculo (A=1, B=2, …, Z=26, AA=27, AB=28, …). Al pasar de Z se agrega una letra al principio.

Conserva la caja de la entrada: si la letra viene en minúsculas el resultado se devuelve en minúsculas. cantidad acepta número o string numérico. Si el resultado cae por debajo de A, devuelve "".

{{letter_add("A", 2)}}      → "C"
{{letter_add("Z", 3)}}      → "AC"
{{letter_add("Z", 1)}}      → "AA"
{{letter_add("AZ", 1)}}     → "BA"
{{letter_add("a", 2)}}      → "c"     (minúscula entra, minúscula sale)
{{letter_add("A", "2")}}    → "C"     (cantidad como string)
{{sumar_letra("A", 2)}}     → "C"     (alias en español)

Ejemplo - generar etiquetas de columna secuenciales:

{
  "template": "Columna: {{letter_add(col, offset)}}",
  "context": { "col": "A", "offset": 4 }
}
Resultado: "Columna: E"


Funciones de fecha y hora

Todas las funciones de fecha devuelven strings en formato RFC3339 ("2026-05-27T14:30:00Z"). Las funciones start_of_* y end_of_* usan la hora local del servidor.

n_now()

Timestamp actual en UTC.

{{n_now()}}   → "2026-05-27T14:30:00Z"

minutes_ago(minutos)

Timestamp de hace N minutos (UTC).

{{minutes_ago(30)}}    → "2026-05-27T14:00:00Z"
{{minutes_ago(1440)}}  → (ayer a la misma hora)
{{minutes_ago(-60)}}   → (en 60 minutos, valor futuro)

Uso en filtro de eventos:

{
  "template": "{{minutes_ago(60)}}",
  "context": {}
}
Devuelve el timestamp de hace una hora para usarlo como start_time en consultas.


time_in_range(hora, inicio, fin [, offset]) / hora_en_rango(...)

Indica (true/false) si una hora cae dentro del rango [inicio, fin], con bordes inclusivos. Las horas se escriben como "HH:MM" (también acepta "HH:MM:SS"; los segundos se ignoran).

  • Si hora es "" o "now", usa la hora actual (el servidor trabaja en UTC) sumándole el offset indicado, que corresponde a la zona horaria del país (ej. -4, +2, -5; admite fracciones como -4.5).
  • El rango soporta cruce de medianoche: si inicio > fin (ej. 22:0006:00), se interpreta que abarca la medianoche.
{{time_in_range("15:32", "12:00", "16:00")}}   → true
{{time_in_range("16:01", "12:00", "16:00")}}   → false
{{time_in_range("12:00", "12:00", "16:00")}}   → true   (inicio inclusivo)
{{time_in_range("16:00", "12:00", "16:00")}}   → true   (fin inclusivo)
{{time_in_range("23:00", "22:00", "06:00")}}   → true   (cruza medianoche)
{{time_in_range("12:00", "22:00", "06:00")}}   → false
{{time_in_range("now", "08:00", "18:00", -4)}} → ¿la hora actual (UTC-4) está en horario laboral?
{{hora_en_rango("15:32", "12:00", "16:00")}}   → true   (alias en español)

Ejemplo - habilitar una acción solo en horario laboral del país (UTC-5):

{
  "template": "{{time_in_range(\"now\", \"08:00\", \"17:00\", -5)}}",
  "context": {}
}


Funciones de inicio de período

Función Descripción
start_of_minute() Inicio del minuto actual (segundos = 0)
start_of_hour() Inicio de la hora actual
start_of_day() 00:00:00 del día actual
start_of_week() Lunes 00:00:00 de la semana actual
start_of_month() Día 1, 00:00:00 del mes actual
start_of_quarter() Inicio del trimestre (ene/abr/jul/oct)
start_of_year() 1 enero 00:00:00 del año actual
{{start_of_day()}}      → "2026-05-27T00:00:00Z"
{{start_of_week()}}     → "2026-05-25T00:00:00Z"  (lunes)
{{start_of_month()}}    → "2026-05-01T00:00:00Z"
{{start_of_quarter()}}  → "2026-04-01T00:00:00Z"
{{start_of_year()}}     → "2026-01-01T00:00:00Z"

Funciones de fin de período

Función Descripción
end_of_minute() Fin del minuto actual (seg = 59.999…)
end_of_hour() Fin de la hora actual
end_of_day() 23:59:59.999… del día actual
end_of_week() Domingo 23:59:59.999… de la semana actual
end_of_month() Último segundo del mes actual
end_of_quarter() Fin del trimestre (mar/jun/sep/dic)
end_of_year() 31 diciembre 23:59:59.999… del año actual
{{end_of_day()}}      → "2026-05-27T23:59:59.999999999Z"
{{end_of_week()}}     → "2026-06-01T23:59:59.999999999Z"  (domingo)
{{end_of_month()}}    → "2026-05-31T23:59:59.999999999Z"
{{end_of_year()}}     → "2026-12-31T23:59:59.999999999Z"

Ejemplo - rango del mes actual:

{
  "template": "{{[start_of_month(), end_of_month()]}}",
  "context": {}
}
Resultado: ["2026-05-01T00:00:00Z", "2026-05-31T23:59:59.999999999Z"] (tipo array)


Funciones de acceso a objetos

Estas funciones consultan en tiempo real el estado de los objetos en la plataforma Netsocs.

get_state(objectId)

Devuelve el estado principal del objeto como string.

{{get_state("hikvision.camera.ch1")}}   → "online"
{{get_state("door.main.lock")}}         → "locked"

Ejemplo: Generar un mensaje dinámico:

{
  "template": "La cámara está: {{get_state('hikvision.camera.ch1')}}",
  "context": {}
}


get_state_property(objectId, propiedad)

Devuelve el valor de una propiedad específica del estado adicional del objeto.

{{get_state_property("sensor.temp.001", "temperature")}}  → "23.5"
{{get_state_property("camera.001", "recording")}}         → "true"

Ejemplo - calcular diferencia de temperatura:

{
  "template": "{{n_sum(get_state_property('sensor.interior', 'temp'), '-', get_state_property('sensor.exterior', 'temp'))}}",
  "context": {}
}


get_object_state(objectId)

Devuelve el objeto completo StateChangeRecord con todos los datos del último estado.

{{get_object_state("sensor.001")}}

Permite acceder a campos del registro:

{{get_object_state("sensor.001").State}}
{{get_object_state("sensor.001").CreatedAt}}


get_object_by_id(objectId)

Devuelve el objeto completo (metadatos + configuración).

{{get_object_by_id("camera.001").Name}}
{{get_object_by_id("camera.001").Domain}}

get_objects_by_ids(ids)

Devuelve un array de objetos. Acepta una lista separada por comas, un array, o un array del contexto. Máximo 1000 IDs.

{
  "template": "{{get_objects_by_ids(camera_ids)}}",
  "context": {
    "camera_ids": ["camera.001", "camera.002", "camera.003"]
  }
}

También acepta string con IDs separados por coma:

{{get_objects_by_ids("camera.001,camera.002")}}


get_objectids_where_state(estado)

Devuelve un array de IDs de todos los objetos que tienen el estado indicado.

{{get_objectids_where_state("online")}}   → ["camera.001", "camera.003", "sensor.002"]
{{get_objectids_where_state("alarm")}}    → ["zone.1", "zone.3"]

Ejemplo - contar cámaras online:

{
  "template": "{{n_sum(get_objectids_where_state('online'))}}",
  "context": {}
}

Nota: n_sum sobre un array de strings no numéricos devuelve 0. Para contar, preferir count_objects_by_prop.


get_objectids_where_property(propiedad, valor)

Devuelve un array de IDs de objetos donde una propiedad del estado tiene exactamente el valor indicado.

{{get_objectids_where_property("zone", "A")}}
{{get_objectids_where_property("model", "XNV-8080R")}}

get_first_objectid_where_property(propiedad, valor)

Devuelve el ID del primer objeto que cumpla la condición. Útil cuando se espera un único resultado.

{{get_first_objectid_where_property("serial_number", "SN-123456")}}
→ "hanwha.camera.main"

count_objects_by_prop(propiedad, condición, valor [, dominio])

Cuenta objetos cuyo estado adicional cumpla una condición. La única condición soportada actualmente es "eq" (igual).

count_objects_by_prop(propiedad, "eq", valor)
count_objects_by_prop(propiedad, "eq", valor, dominio)

Ejemplos:

{{count_objects_by_prop("status", "eq", "open")}}
→ 5   (5 objetos tienen status = "open")

{{count_objects_by_prop("recording", "eq", "true", "cameras")}}
→ 3   (3 objetos en el dominio "cameras" tienen recording = "true")

Ejemplo en dashboard:

{
  "template": "Puertas abiertas: {{count_objects_by_prop('status', 'eq', 'open', 'access.doors')}}",
  "context": {}
}


Función de eventos

get_total_events(eventTypes [, objectsId [, domains [, startTime, endTime]]])

Cuenta eventos que coincidan con los filtros indicados. Todos los parámetros son strings separados por coma cuando hay múltiples valores.

Parámetros:

Parámetro Tipo Descripción
eventTypes string Tipos de evento separados por coma
objectsId string IDs de objeto separados por coma (opcional)
domains string Dominios separados por coma (opcional)
startTime string Fecha inicio RFC3339 o YYYY-MM-DD:HH:MM:SS
endTime string Fecha fin RFC3339 o YYYY-MM-DD:HH:MM:SS

Ejemplos:

Contar todos los eventos de movimiento del día:

{
  "template": "{{get_total_events('motion_detected', '', '', start_of_day(), end_of_day())}}",
  "context": {}
}

Contar eventos de una cámara específica en la última hora:

{
  "template": "{{get_total_events('motion_detected,tamper', 'hanwha.camera.001', '', minutes_ago(60), n_now())}}",
  "context": {}
}

Contar alarmas del mes en un dominio:

{
  "template": "{{get_total_events('alarm_triggered', '', 'security.zones', start_of_month(), end_of_month())}}",
  "context": {}
}


Tipos de datos en expresiones

Objetos JSON

Se puede construir un objeto directamente en la expresión:

{{{"name": "sensor1", "value": 42}}}
Resultado: {"name":"sensor1","value":42} (tipo object)

Acceder a propiedades de objetos inline:

{{{"greeting": "hola"}.greeting}}   → "hola"

Arrays JSON

{{[1, 2, 3, 4, 5]}}   → [1,2,3,4,5] (tipo `array`)
{{["cam1", "cam2"]}}  → ["cam1","cam2"]

Fechas con date() y duration()

La librería subyacente (expr-lang) provee funciones nativas para comparar fechas:

{{date("2026-01-01T00:00:00Z") < date("2026-12-31T00:00:00Z")}}   → true

{{date(fecha_inicio) - date(fecha_fin) < duration("24h")}}

Con contexto:

{
  "template": "{{(date(request.Time) - date(resource.Age) < duration(\"24h\")) ? \"reciente\" : \"antiguo\"}}",
  "context": {
    "request": { "Time": "2026-05-27T23:59:00Z" },
    "resource": { "Age": "2026-05-27T00:00:00Z" }
  }
}
Resultado: "reciente"


Lenguaje base (expr-lang)

Además de las funciones de Netsocs descritas arriba, dentro de {{ }} está disponible todo el lenguaje base, con sus operadores y un amplio set de funciones integradas. Todo lo de esta sección se escribe igual: dentro de dobles llaves en el playground.

Literales

Tipo Ejemplos
Comentario /* ... */ o // ...
Booleano true, false
Entero 42, 0x2A (hex), 0o52 (octal), 0b101010 (binario)
Decimal 0.5, .5
String "foo", 'bar'
String multilínea `texto en\nvarias líneas` (comillas invertidas, sin escapes)
Array [1, 2, 3]
Map (objeto) {a: 1, b: 2}
Nulo nil

Los strings con comillas dobles/simples soportan escapes: \n, \t, \uXXXX.

Operadores

Categoría Operadores
Aritméticos +, -, *, /, % (módulo), ^ o ** (potencia)
Comparación ==, !=, <, >, <=, >=
Lógicos not o !, and o &&, or o \|\|
Condicional ?: (ternario), ?? (coalescencia de nulos)
Membresía [], ., ?., in
String + (concatenar), contains, startsWith, endsWith
Regex matches
Rango ..
Slice [:]
Pipe \|
{{2 ^ 10}}                          → 1024
{{"a" in ["a", "b"]}}               → true
{{"name" in {"name": "x"}}}         → true
{{"hello" contains "ell"}}          → true
{{"camera.001" startsWith "cam"}}   → true
{{"sensor.temp" endsWith ".temp"}}  → true
{{"abc123" matches "[a-z]+[0-9]+"}} → true
{{1..3}}                            → [1, 2, 3]

Acceso e índices

Los campos se acceden con . o [] (equivalentes). Los índices negativos cuentan desde el final.

{{device.brand}}        equivale a   {{device["brand"]}}
{{array[0]}}            → primer elemento
{{array[-1]}}           → último elemento

Slices [:]

Con array = [1, 2, 3, 4, 5]:

{{array[1:4]}}   → [2, 3, 4]
{{array[:3]}}    → [1, 2, 3]
{{array[3:]}}    → [4, 5]

Encadenamiento opcional ?. y coalescencia ??

?. evita errores si un valor intermedio es nil (devuelve nil); ?? entrega el lado derecho cuando el izquierdo es nil.

{{persona?.direccion?.ciudad}}
{{persona?.nombre ?? "Anónimo"}}

Operador pipe |

Pasa el resultado de la izquierda como primer argumento de la derecha.

{{nombre | lower() | split(" ")}}   equivale a   {{split(lower(nombre), " ")}}

Variables (let)

Se pueden declarar variables temporales con let, separando sentencias con ;.

{{let x = 42; x * 2}}                → 84
{{let a = 10; let b = 5; a + b}}     → 15

$env

$env es un mapa con todas las variables del contexto. Útil para nombres con espacios o para verificar si una variable existe.

{{'temperatura' in $env}}            → true si el contexto trae "temperatura"
{{$env["var con espacios"]}}

Predicados

Funciones como filter, map, all, any, etc. reciben un predicado: una expresión donde # es el elemento actual. Si el elemento es un objeto, se puede omitir # y usar .Campo directamente.

{{filter(0..9, # % 2 == 0)}}             → [0, 2, 4, 6, 8]
{{filter(camaras, .estado == "online")}}
{{map(sensores, .temperatura)}}

Funciones de string (base)

{{trim("  Hola  ")}}                       → "Hola"
{{trim("__Hola__", "_")}}                  → "Hola"
{{trimPrefix("HolaMundo", "Hola")}}        → "Mundo"
{{trimSuffix("HolaMundo", "Mundo")}}       → "Hola"
{{upper("hola")}}                          → "HOLA"
{{lower("HOLA")}}                          → "hola"
{{split("a,b,c", ",")}}                    → ["a", "b", "c"]
{{splitAfter("a,b,c", ",")}}               → ["a,", "b,", "c"]
{{replace("Hola Mundo", "Mundo", "Todos")}}→ "Hola Todos"
{{repeat("ab", 3)}}                        → "ababab"
{{indexOf("sensor.001", ".")}}             → 6
{{lastIndexOf("a.b.c", ".")}}              → 3
{{hasPrefix("cam.1", "cam")}}              → true
{{hasSuffix("cam.1", ".1")}}               → true

Funciones numéricas (base)

{{max(5, 7)}}      → 7
{{min(5, 7)}}      → 5
{{abs(-5)}}        → 5
{{ceil(1.5)}}      → 2
{{floor(1.5)}}     → 1
{{round(1.5)}}     → 2

Para sumas/multiplicaciones/comparaciones tolerantes a strings numéricos del estado, preferir las funciones Netsocs n_sum, n_multiply, n_greater_than, etc.

Funciones de array (base)

{{all([2, 4, 6], # % 2 == 0)}}        → true
{{any([1, 2, 3], # > 2)}}             → true
{{one([1, 2, 3], # == 2)}}            → true
{{none([1, 2, 3], # > 5)}}            → true
{{map([1, 2, 3], # * 2)}}             → [2, 4, 6]
{{filter([1, 2, 3, 4], # > 2)}}       → [3, 4]
{{find([1, 2, 3, 4], # > 2)}}         → 3
{{findIndex([1, 2, 3, 4], # > 2)}}    → 2
{{findLast([1, 2, 3, 4], # > 2)}}     → 4
{{findLastIndex([1, 2, 3, 4], # > 2)}}→ 3
{{count([1, 2, 3, 4], # > 2)}}        → 2
{{count([true, false, true])}}        → 2
{{concat([1, 2], [3, 4])}}            → [1, 2, 3, 4]
{{flatten([1, [2, [3]]])}}            → [1, 2, 3]
{{uniq([1, 2, 2, 3])}}                → [1, 2, 3]
{{join(["a", "b", "c"], ",")}}        → "a,b,c"
{{reduce(1..4, #acc + #, 0)}}         → 10
{{sum([1, 2, 3])}}                    → 6
{{mean([1, 2, 3])}}                   → 2
{{median([1, 2, 3])}}                 → 2
{{first([1, 2, 3])}}                  → 1
{{last([1, 2, 3])}}                   → 3
{{take([1, 2, 3, 4], 2)}}             → [1, 2]
{{reverse([3, 1, 4])}}                → [4, 1, 3]
{{sort([3, 1, 4])}}                   → [1, 3, 4]
{{sort([3, 1, 4], "desc")}}           → [4, 3, 1]
{{sortBy(usuarios, .edad)}}
{{sortBy(usuarios, .edad, "desc")}}
{{groupBy(usuarios, .edad)}}

En reduce, dentro del predicado están disponibles # (elemento actual), #acc (acumulador) y #index (índice).

Funciones de map (base)

{{keys({"name": "John", "age": 30})}}    → ["name", "age"]
{{values({"name": "John", "age": 30})}}  → ["John", 30]
{{toPairs({"a": 1, "b": 2})}}            → [["a", 1], ["b", 2]]
{{fromPairs([["a", 1], ["b", 2]])}}      → {"a": 1, "b": 2}

Conversión de tipos (base)

{{type(42)}}                  → "int"
{{type("hola")}}              → "string"
{{int("123")}}               → 123
{{float("123.45")}}          → 123.45
{{string(123)}}              → "123"
{{toJSON({"a": 1})}}         → "{\"a\":1}"
{{fromJSON("{\"a\":1}")}}    → {"a": 1}
{{toBase64("Hola")}}         → "SG9sYQ=="
{{fromBase64("SG9sYQ==")}}   → "Hola"

Misceláneas (base)

{{len([1, 2, 3])}}                       → 3
{{len("Hola")}}                          → 4
{{get([10, 20, 30], 1)}}                 → 20
{{get({"name": "John"}, "name")}}        → "John"

Funciones de bits (base)

{{bitand(0b1010, 0b1100)}}   → 8     (0b1000)
{{bitor(0b1010, 0b1100)}}    → 14    (0b1110)
{{bitxor(0b1010, 0b1100)}}   → 6     (0b110)
{{bitnand(0b1010, 0b1100)}}  → 2
{{bitnot(0b1010)}}           → -11
{{bitshl(0b1011, 2)}}        → 44
{{bitshr(0b1011, 2)}}        → 2

Ejemplos completos

1. Estado de una cámara con emoji de estado

{
  "template": "Cámara {{cam_id}}: {{get_state(cam_id) == 'online' ? 'ACTIVA' : 'INACTIVA'}}",
  "context": { "cam_id": "hanwha.camera.lobby" }
}

2. Verificar si temperatura está en rango

{
  "template": "{{n_and(n_greater_equal_than(get_state_property('sensor.temp', 'value'), '18'), n_less_equal_than(get_state_property('sensor.temp', 'value'), '26'))}}",
  "context": {}
}
Devuelve true si la temperatura está entre 18 y 26.

3. Payload dinámico para acción de driver

{
  "template": "{\"camera_id\": \"{{camera_id}}\", \"recording\": true, \"start_time\": \"{{start_of_day()}}\", \"end_time\": \"{{end_of_day()}}\"}",
  "context": { "camera_id": "cam.001" }
}

4. Resumen diario de eventos

{
  "template": "Hoy: {{get_total_events('motion_detected', '', 'cameras', start_of_day(), end_of_day())}} movimientos, {{get_total_events('alarm_triggered', '', '', start_of_day(), end_of_day())}} alarmas",
  "context": {}
}

5. Obtener IDs de cámaras en alarma y construir payload

{
  "template": "{{get_objectids_where_state('alarm')}}",
  "context": {}
}
Devuelve un array de IDs (tipo array) listo para usar en otro campo.

6. Calcular promedio de dos sensores

{
  "template": "{{n_sum(get_state_property('sensor.norte', 'temp'), get_state_property('sensor.sur', 'temp')) / 2}} °C",
  "context": {}
}

7. Número de dispositivos online de un dominio específico

{
  "template": "Cámaras activas: {{count_objects_by_prop('connection_status', 'eq', 'connected', 'cameras')}}",
  "context": {}
}

8. Verificar si un dispositivo reportó recientemente

{
  "template": "{{date(get_state_property('sensor.001', 'last_seen')) > date(minutes_ago(10))}}",
  "context": {}
}
Devuelve true si el sensor reportó en los últimos 10 minutos.


Funciones de control de acceso

Estas funciones consultan en tiempo real los datos de personas registradas en el módulo de access control.

get_person(personId)

Devuelve el objeto completo de una persona dado su ID. Retorna un objeto vacío si el ID no existe o está en blanco.

Campos disponibles:

Campo Tipo Descripción
ID string UUID de la persona
ExternalID string ID externo (ej. número de empleado)
Name string Nombre completo
Photo string URL de la foto
PersonType string "employee", "visitor", "contractor"
ActivationDate string / null Fecha de activación (RFC3339)
ExpirationDate string / null Fecha de expiración (RFC3339)
Timezone string Zona horaria (default "UTC")
Enabled bool Si la persona está habilitada
CustomFields object Campos personalizados {"key": "value"}
APBExempt bool Exento de anti-passback
ExtendedUnlock bool Tiempo extendido de apertura
EscortRequired bool Requiere acompañante
TenantID string ID del tenant
CreatedAt string Fecha de creación (RFC3339)
UpdatedAt string Fecha de última actualización (RFC3339)

Ejemplos:

Obtener el nombre de una persona:

{{get_person("abc-123-uuid").Name}}   → "María García"

Verificar si una persona está habilitada:

{{get_person("abc-123-uuid").Enabled}}   → true

Usar el tipo de persona en lógica condicional:

{
  "template": "{{get_person(person_id).PersonType == 'visitor' ? 'Acceso limitado' : 'Acceso completo'}}",
  "context": { "person_id": "abc-123-uuid" }
}

Construir un payload enriquecido con datos del portador:

{
  "template": "{\"name\": \"{{get_person(person_id).Name}}\", \"type\": \"{{get_person(person_id).PersonType}}\", \"enabled\": {{get_person(person_id).Enabled}}}",
  "context": { "person_id": "abc-123-uuid" }
}

Acceder a un campo personalizado:

{
  "template": "Departamento: {{get_person(person_id).CustomFields.department}}",
  "context": { "person_id": "abc-123-uuid" }
}


get_person_location(personId)

Devuelve la ubicación actual de una persona: si está dentro, en qué área, cuándo fue visto por última vez, y por qué lector.

Campos disponibles:

Campo Tipo Descripción
PersonID string ID de la persona
IsInside bool true si la persona está dentro del área
AreaID string ID del área donde fue visto por última vez
LastReaderID string ID del lector por el que pasó
LastSeenAt string / null Timestamp de último avistamiento (RFC3339)
LastDirection string "entry" o "exit"
TenantID string ID del tenant
UpdatedAt string Última actualización del registro

Ejemplos:

Verificar si una persona está adentro:

{{get_person_location("abc-123-uuid").IsInside}}   → true

Obtener el área donde está una persona:

{{get_person_location("abc-123-uuid").AreaID}}   → "building.entrance.lobby"

Mensaje dinámico con estado de presencia:

{
  "template": "{{get_person(person_id).Name}} está {{get_person_location(person_id).IsInside ? 'dentro' : 'fuera'}} del edificio",
  "context": { "person_id": "abc-123-uuid" }
}
Resultado: "María García está dentro del edificio"

Verificar última dirección de paso:

{
  "template": "{{get_person_location(person_id).LastDirection == 'entry' ? 'Ingresó' : 'Salió'}} por {{get_person_location(person_id).LastReaderID}}",
  "context": { "person_id": "abc-123-uuid" }
}


Referencia rápida de funciones

Función Retorna Descripción
n_sum(...) number Suma de valores/array
n_suma(...) number Alias en español de n_sum
n_multiply(...) number Producto de valores/array
n_multiplicar(...) number Alias en español de n_multiply
n_abs(a) number Valor absoluto
add(a, b) number Suma de dos enteros
n_less_than(a, b) boolean a < b numérico
n_menor_que(a, b) boolean Alias de n_less_than
n_greater_than(a, b) boolean a > b numérico
n_mayor_que(a, b) boolean Alias de n_greater_than
n_less_equal_than(a, b) boolean a <= b numérico
n_menor_igual_que(a, b) boolean Alias de n_less_equal_than
n_greater_equal_than(a, b) boolean a >= b numérico
n_mayor_igual_que(a, b) boolean Alias de n_greater_equal_than
n_equal(a, b) boolean a == b numérico
n_igual(a, b) boolean Alias de n_equal
n_equal_epsilon(a, b, ε) boolean \|a - b\| <= ε
n_igual_epsilon(a, b, ε) boolean Alias de n_equal_epsilon
n_and(a, b) boolean AND lógico
n_or(a, b) boolean OR lógico
n_not(a) boolean NOT lógico
netconcat(a, b) string Concatena dos strings
letter_add(letra, n) string Suma n a una letra en base-26 (A+2=C, Z+3=AC)
sumar_letra(letra, n) string Alias en español de letter_add
n_now() string Timestamp UTC actual
minutes_ago(n) string Timestamp de hace N minutos
time_in_range(hora, inicio, fin [, offset]) boolean ¿hora está en [inicio, fin]? ("now"+offset = hora actual; soporta cruce de medianoche)
hora_en_rango(...) boolean Alias en español de time_in_range
start_of_minute() string Inicio del minuto actual
start_of_hour() string Inicio de la hora actual
start_of_day() string Inicio del día actual
start_of_week() string Inicio de la semana (lunes)
start_of_month() string Inicio del mes actual
start_of_quarter() string Inicio del trimestre
start_of_year() string Inicio del año actual
end_of_minute() string Fin del minuto actual
end_of_hour() string Fin de la hora actual
end_of_day() string Fin del día actual
end_of_week() string Fin de la semana (domingo)
end_of_month() string Fin del mes actual
end_of_quarter() string Fin del trimestre
end_of_year() string Fin del año actual
get_state(id) string Estado principal del objeto
get_state_property(id, prop) string Propiedad del estado adicional
get_object_state(id) object Registro completo de estado
get_object_by_id(id) object Objeto completo
get_objects_by_ids(ids) array Lista de objetos por IDs
get_objectids_where_state(state) array IDs con ese estado
get_objectids_where_property(prop, val) array IDs donde propiedad = valor
get_first_objectid_where_property(prop, val) string Primer ID coincidente
count_objects_by_prop(prop, cond, val [, domain]) number Cuenta objetos
get_total_events(types [, ids [, domains [, start, end]]]) number Cuenta eventos
get_person(personId) object Datos completos de una persona por ID
get_person_location(personId) object Ubicación actual de una persona