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.

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.(tipostring)
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
}
}
"Ana tiene 30 años"
Para acceder a propiedades anidadas se usa punto .:
{
"template": "{{device.brand}}",
"context": {
"device": { "brand": "Hanwha", "model": "XNV-8080R" }
}
}
"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 }
}
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 }
}
"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 }
}
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 }
}
"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": {}
}
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
horaes""o"now", usa la hora actual (el servidor trabaja en UTC) sumándole eloffsetindicado, 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:00–06: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": {}
}
["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_sumsobre un array de strings no numéricos devuelve 0. Para contar, preferircount_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}}}
{"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" }
}
}
"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": {}
}
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": {}
}
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": {}
}
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" }
}
"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 |