Archivo de la categoría: Dashboard Node-RED

Práctica 5: Programar Node-RED, MQTT y Dashboard

Ejercicio 01

Mostrar en el dashboard en un mismo grupo 3 widgets text donde cada segundo actualice el timestamp (epoch time), la fecha usando el nodo node-red-contrib-date y el día y hora en formato, usando el nodo moment para transformarlo.

Código: https://github.com/aprendiendonodered/ejercicio01 

Vídeo solución:

Ejercicio 02

Leer un dato de la temperatura publicado en MQTT y hacer que cuando la temperatura esté fuera de un rango entre 18 y 22 grados, ponga el estado de confort en un elemento del dashboard y mande un email.

Usar el nodo email y ver la información para usarlo: https://flows.nodered.org/node/node-red-node-email 

Código: https://github.com/aprendiendonodered/ejercicio02 

Vídeos solución:

Imágenes:

Ejercicio 03

Hacer un flujo para publicar datos de CPU, Memoria y temperatura de las Raspberry Pi de casa en MQTT en los topics:

  • educantabria/nodoxx/raspberrypi/CPU
  • educantabria/nodoxx/raspberrypi/Memoria
  • educantabria/nodoxx/raspberrypi/Temperatura

Luego hacer que la instancia de Node-RED que las recoja y muestre en el dashboard.

Para publicar los datos de casa, usar el broker MQTT del curso enriquecrespo.com:8883. Opcionalmente usar el broker instalado en la práctica 4: https://aprendiendoarduino.wordpress.com/2021/02/28/practica-4-instalar-configurar-y-securizar-mosquitto-y-node-red-en-raspberry-pi/.

Código Raspberry Pi:

Vídeo 1 con código Raspberry Pi:

Código Node-RED Dashboard:

Vídeo segunda parte:

Dashboard:

Código Total: https://github.com/aprendiendonodered/ejercicio03 

Anexo: Obtener Datos de Raspberry Pi

Código para obtener los datos de Raspberry Pi, usando en nodo exec: 

[{"id":"af22b399.12614","type":"exec","z":"8a81314e.a0f79","command":"vcgencmd measure_temp","addpay":false,"append":"","useSpawn":"","timer":"","name":"RPi Temp.","x":325.2221908569336,"y":46.33333683013916,"wires":[["14f0a295.59181d"],[],[]]},{"id":"408435c9.447fec","type":"exec","z":"8a81314e.a0f79","command":"top -d 0.5 -b -n2 | grep \"Cpu(s)\"|tail -n 1 | awk '{print $2 + $4}'","addpay":false,"append":"","useSpawn":"","timer":"","name":"CPU Load","x":325.2221908569336,"y":126.33333683013916,"wires":[["2c7475c7.6cc01a"],[],[]]},{"id":"8fbcbdf9.549b7","type":"exec","z":"8a81314e.a0f79","command":"free | grep Mem | awk '{print 100*($4+$6+$7)/$2}'","addpay":false,"append":"","useSpawn":"","timer":"","name":"Free Memory","x":325.2222099304199,"y":190.55557250976562,"wires":[["daf6433c.bad81"],[],[]]},{"id":"14f0a295.59181d","type":"function","z":"8a81314e.a0f79","name":"","func":"str = msg.payload\nmsg.payload = str.substring(5,9);\nreturn msg;","outputs":1,"noerr":0,"x":471.888858795166,"y":33.00000190734863,"wires":[["46daa272.4333ac","334a9ddd.345602"]]},{"id":"daf6433c.bad81","type":"function","z":"8a81314e.a0f79","name":"","func":"str = msg.payload\nmsg.payload = parseInt(str.substring(0,str.length - 1));\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":180,"wires":[["1ba39571.ba75ab"]]}]

Código para obtener los datos Raspberry Pi y mandar a MQTT: https://github.com/jecrespo/Curso-Node-RED/blob/master/Monitor%20Raspberry%20Pi/monitor_raspberrypi.json 

Ejercicio 04

Hacer un dashboard para ver la temperatura de todas las Raspberry Pi o de otros elementos publicado en MQTT y sus gráficas. Usar MQTT Explorer para explorar qué datos están publicados.

Opcionalmente hacer varias tabs/pestañas para mostrar: temperaturas, CPU y memoria agrupadas en cada pestaña.

Código: https://github.com/aprendiendonodered/ejercicio04

Vídeos:

Flujo:

Dashboard Final:

Ejercicios Curso Node-RED Developer

Ejercicios del curso “Node-RED Developer Nivel 1”: https://www.aprendiendoarduino.com/cursos/node-red-developer-para-iot-nivel-i/ 

Ejercicio 01

Mostrar en el dashboard en un mismo grupo 3 widgets text donde cada segundo actualice el timestamp (epoch time), la fecha usando el nodo node-red-contrib-date y el día y hora en formato, usando el nodo moment para transformarlo.

Código: https://github.com/aprendiendonodered/ejercicio01 

Vídeo:

Ejercicio 02

Leer el dato de la temperatura publicado en MQTT y hacer que cuando la temperatura esté fuera de un rango entre 18 y 22 grados, ponga el estado de confort en un elemento del dashboard y mande un email.

Código: https://github.com/aprendiendonodered/ejercicio02

Vídeos:

Imágenes:

Ejercicio 03

Hacer un flujo para publicar datos de CPU, Memoria y temperatura de las Raspberry Pi de casa en MQTT en los topics:

  • aprendiendonodered/alumnoxx/raspberrypi/CPU
  • aprendiendonodered/alumnoxx/raspberrypi/Memoria
  • aprendiendonodered/alumnoxx/raspberrypi/Temperatura

Luego hacer que la instancia de Node-RED del curso las recoja y muestre en el dashboard.

Para publicar los datos de casa, usar el broker MQTT del curso enriquecrespo.com:1883. Opcionalmente usar el broker integrado en Node-RED aedes o un HTTP endpoint.

Código Raspberry Pi:

Vídeo 1 con código Raspberry Pi:

Código Node-RED Dashboard:

Vídeo segunda parte:

Dashboard:

Código Total: https://github.com/aprendiendonodered/ejercicio03 

Anexo: Obtener Datos de Raspberry Pi

Código para obtener los datos de Raspberry Pi, usando en nodo exec: 

[{"id":"af22b399.12614","type":"exec","z":"8a81314e.a0f79","command":"vcgencmd measure_temp","addpay":false,"append":"","useSpawn":"","timer":"","name":"RPi Temp.","x":325.2221908569336,"y":46.33333683013916,"wires":[["14f0a295.59181d"],[],[]]},{"id":"408435c9.447fec","type":"exec","z":"8a81314e.a0f79","command":"top -d 0.5 -b -n2 | grep \"Cpu(s)\"|tail -n 1 | awk '{print $2 + $4}'","addpay":false,"append":"","useSpawn":"","timer":"","name":"CPU Load","x":325.2221908569336,"y":126.33333683013916,"wires":[["2c7475c7.6cc01a"],[],[]]},{"id":"8fbcbdf9.549b7","type":"exec","z":"8a81314e.a0f79","command":"free | grep Mem | awk '{print 100*($4+$6+$7)/$2}'","addpay":false,"append":"","useSpawn":"","timer":"","name":"Free Memory","x":325.2222099304199,"y":190.55557250976562,"wires":[["daf6433c.bad81"],[],[]]},{"id":"14f0a295.59181d","type":"function","z":"8a81314e.a0f79","name":"","func":"str = msg.payload\nmsg.payload = str.substring(5,9);\nreturn msg;","outputs":1,"noerr":0,"x":471.888858795166,"y":33.00000190734863,"wires":[["46daa272.4333ac","334a9ddd.345602"]]},{"id":"daf6433c.bad81","type":"function","z":"8a81314e.a0f79","name":"","func":"str = msg.payload\nmsg.payload = parseInt(str.substring(0,str.length - 1));\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":180,"wires":[["1ba39571.ba75ab"]]}]

Ejercicio 04

Hacer un dashboard para ver la temperatura de todas las Raspberry Pi publicada en MQTT y sus gráficas. Usar MQTT Explorer para explorar qué datos están publicados.

Opcionalmente hacer varias tabs/pestañas para mostrar: temperaturas, CPU y memoria agrupadas en cada pestaña.

Código: https://github.com/aprendiendonodered/ejercicio04

Vídeos:

  • Vídeo 1:
  • Vídeo 2:
  • Vídeo 3:

Flujo:

Dashboard Final:

Ejercicio 05

Hacer IFTTT con Node-RED. Ejemplos:

  • Si recibo un correo con el asunto “twitter”, entonces twittear el contenido del correo.
  • Si recibo una llamada por el HTTP endpoint “mail”, entonces mandar un email al destinatario, asunto y contenido indicados

Integración de Node-RED con email, twitter y HTTP endpoint:

La llamada http en un GET y debe tener el formato: https://enriquecrespo.com:188xx/mail?destinatario=pepe@gmail.com&asunto=mandacorreo&contenido=hola_que_tal

Código: https://github.com/aprendiendonodered/ejercicio05

Vídeos:

  • Primera parte:
  • Segunda parte: 

Petición usada: https://enriquecrespo.com:18802/mail?destinatario=aprendiendoraspberrypi@gmail.com&asunto=correo_desde_http_para_twitter&contenido=hola_node_red 

Además en este caso si hago una petición HTTP GET o POST manda el email y luego la segunda condición cuando lee el email, manda el tweet, lo que hace que se desencadenen los dos eventos.

Ejercicio 06

Sobre el ejercicio del día 2, añadir de forma dinámica los umbrales máximos y mínimos de alerta mediante dos sliders en el dashboard.

También hacer que los valores de los colores del gauge cambien en función del valor de MAX y MIN introducido por los sliders

Opcionalmente mostrar los mensajes de alerta y los iconos:

  • Todo Correcto: <i class=”fa fa-lightbulb-o fa-2x nr-dashboard-ok”></i>
  • Temperatura Alta: <i class=”fa fa-warning fa-2x nr-dashboard-warning”></i>
  • Temperatura Baja: <i class=”fa fa-exclamation-circle fa-2x nr-dashboard-error”></i>

Para cambiar la configuración de los widgets de dashboard se usa mediante msg.ui_control como se indica aquí: https://github.com/node-red/node-red-dashboard/blob/master/config-fields.md 

En este caso uso un nodo change donde borro paylaod y pongo las propiedades msg.ui_control.seg1 y msg.ui_control.seg2 a los valores almacenados en las variables de contexto.

Por último no dejar que el valor MIN sea mayor que MAX, ni que MAX sea menor que min en el dashboard, para ello controlar el valor de MIN y MAX al cambiar con un nodo switch y tomar la decisión a hacer.

Código: https://github.com/aprendiendonodered/ejercicio06.git

Vídeos:

  • Primera Parte
  • Segunda Parte

Ejercicio 07

Hacer un subflujo con 5 salidas, que pasándole una variable de entorno con el número de datos a analizar, devuelva la media, máxima y mínima de esos datos por cada salida, una vez retenidos y analizados. En la cuarta salida me devuelva un array de los datos analizados y en la quinta el contador interno.

Personalizar el flujo, añadiendo documentación de lo que hace el subflow y lo que devuelve cada salida.

Código: https://github.com/aprendiendonodered/ejercicio07 

Vídeos:

  • Primera Parte
  • Segunda Parte
  • Tercera Parte
  • Cuarta Parte

Ejercicio 08

Clonar el proyecto https://github.com/aprendiendonodered/ejercicio04 y añadir toda la información de todas las Raspberry Pi que publican datos en mqtt://enriquecrespo.com:1883 y añadir las chart a cada datos, mostrando los datos de forma visual y personalizando el dashboard.

Código: https://github.com/aprendiendonodered/ejercicio08 

Vídeos:

  • Primera Parte:
  • Segunda Parte:
  • Tercera Parte:

Ejercicio 09

Hacer una función que guarde los últimos 20 valores en una variable de contexto y una vez tenga los 20, mande la media, máximo y mínimo por tres salidas y en una cuarta mande los 20 mensajes que ha retenido de forma consecutiva. En la quinta salida mandar el dato del contador interno.

Una vez hecho, añadir una funcionalidad para que si recibe un mensaje en la propiedad msg.acumulado, tome ese dato como el número de datos a acumular para hacer el cálculo. Una vez llegue este dato, actualiza el acumulado del contador y en función de lo que lleve acumulado, espera hasta llegar al nuevo dato o si lo ha superado, manda lo que ya tiene.

Por último, encapsular esta función en un subflow para poder reutilizarlo.

Añadir dato a un array: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/push 

Función reduce: https://www.w3schools.com/jsref/jsref_reduce.asp 

Función Max: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Math/max 

Código: https://github.com/aprendiendonodered/ejercicio09

Vídeos:

  • Primera parte:
  • Segunda parte:
  • Tercera parte:

Ejercicio 10 (Ejercicio Final)

Hacer un CSV con los datos de temperatura, memoria y CPU que manda vuestra Raspberry PI y guardar en un fichero llamado raspberrypi.csv. Añadir también el campo timestamp al fichero csv.

Para ello tomar los datos publicados en MQTT, ordenarlos y prepararlos para que los pase a un nodo join y los ponga en este orden: timestamp,CPU,Memory,Temperature 

Para que los ordene de forma automática el nodo join debemos hacer uso de la propiedad parts como hace el nodo split usando:

  • parts.index – para el orden de los elementos del grupo a unir
  • parts.id – para indicar que es el mismo grupo
  • parts.count – para indicar el nº de mensajes del grupo

También crear un flujo con un nodo inject que cree el fichero con la cabecera: “timestamp,CPU,Memory,Temperature” y que además al hacerlo vacíe el fichero existente.

Crear otro flujo con un nodo inject que lee con “file in” el contenido del fichero y lo mande a la pantalla de debug.

Luego con un botón del dashboard hacer la parte de inicializar el fichero (que aparezca una notificación del dashboard para confirmar que se desea inicializar) y con otro botón del dashboard un flujo que lea el fichero y lo mande al nodo chart del dashboard y hacer las gráficas.

Opcionalmente, hacer un tercer botón que mande por email el fichero csv, como fichero adjunto.

Opcionalmente, hacer un flujo que lea los datos del fichero llamado raspberrypi.csv y obtener los datos de media, máxima y mínima de la temperatura, memoria y CPU y mostrar en el dashboard en un nodo texto.

Opcionalmente, hacer un flujo que agrupe los datos en grupos de 100 y sacar los datos de media, máxima y mínima cada 100 valores y guardar en otro fichero llamado raspberrypi_agrupados.csv con los campos: timestamp,CPU_AVG,CPU_MAX,CPU_MIN,Memory_AVG,Memory_MAX,Memory_MIN,Temperature_AVG,Temperature_MAX,Temperature_MIN

Mandar datos al nodo chart: https://github.com/node-red/node-red-dashboard/blob/master/Charts.md 

Código: https://github.com/aprendiendonodered/ejercicio10

Vídeos:

  • Primera parte:
  • Segunda parte:
  • Tercera parte:
  • Cuarta parte:
  • Quinta parte:
  • Sexta parte:

Manejo de errores en Node-RED

Node-RED proporciona los nodos Catch y Status como formas de generar flujos que pueden responder a errores. Como no existe una asociación visual directa entre un nodo Catch y los nodos a los que apunta, debe considerar cómo colocarlos para mantener los flujos legibles.

Colocarlos cerca de las partes del flujo a las que corresponden puede ayudar, pero debe tener cuidado de no sobrecargar los flujos.

Otro enfoque es agrupar todos los flujos de manejo de errores debajo del flujo principal, haciendo que la ruta «buena» sea claramente distinta de las rutas de error.

Dar a sus nodos Catch un nombre claro también es muy importante para ayudar a identificar fácilmente los escenarios que deben manejar. Cualquiera que sea el enfoque que elija, intente ser coherente en los diferentes flujos.

Manejar errores con Node-RED: https://nodered.org/docs/user-guide/handling-errors

Manejo de errores:

Si bien es fácil crear flujos que hacen lo correcto cuando todo funciona, también es importante pensar en lo que podría salir mal.

Por ejemplo, si el flujo interactúa con una base de datos externa o API, ¿qué sucede si deja de responder a las solicitudes? ¿O qué pasa si los nodos MQTT pierden su conexión con un corredor?

El manejo de errores en cualquier aplicación es esencial para garantizar que este tipo de eventos se manejen correctamente. Lo que signifique manejar el error dependerá de los requisitos de la aplicación. Es posible que desee probar una acción que falló o activar una alerta por separado, o tal vez el error sea un evento completamente esperado que es solo otra parte de la lógica de la aplicación.

Node-RED proporciona dos formas para que un nodo informe un error. Puede simplemente escribir un mensaje en el registro o puede notificar al tiempo de ejecución del error y hacer que se active un flujo.

El nodo de catch, es similar a la instrucción “try/except” en python, es decir, sirve para el manejo de excepciones.

Si el error solo se escribe en el registro, verá el mensaje en la barra lateral de depuración y la salida del registro, pero no podrá crear un flujo para manejarlo.

Si notifica al tiempo de ejecución correctamente, entonces es un error detectable que puede usarse para desencadenar un flujo de manejo de errores.

Hay un tercer tipo de error que puede hacer que el tiempo de ejecución de Node-RED se apague. Estos errores uncaughtException no se pueden manejar en el flujo y son causados por errores en los nodos.

Más información: https://techexplorations.com/guides/esp32/nore-red-esp32-project/node-red-catch/ 

Logging Errors

Cuando se produzca un error, este aparecerá en la barra de debug.

Esto muestra el mensaje de error, la fecha/hora del error y el nodo que registró el error. Al igual que con otros mensajes de depuración, al colocar el cursor sobre él, se resaltará el nodo en el espacio de trabajo. Si no está en la vista actual, al hacer clic en el nombre del nodo en la esquina superior se mostrará en el espacio de trabajo.

Nodo Catch

Si un nodo notifica al tiempo de ejecución de un error, entonces el nodo Catch puede usarse para crear un flujo para manejarlo. Si un nodo Catch detecta un error, no se registrará en la barra lateral Debug.

El mensaje enviado por Catch será el mensaje proporcionado por el nodo que informa del error. Este mensaje tendrá un conjunto de propiedades de error que proporciona información sobre el error:

 {
     "topic": ...,
     "payload": ...,
     "error": {
         "message": "An error",
         "source": {
             "id": "2e25823d.fa3f7e",
             "type": "function",
             "name": "My Function",
             "count": 1
         }
     }
 } 

Count indica cuántas veces un nodo ha lanzado este mensaje. Esta propiedad es utilizada por el tiempo de ejecución para detectar mensajes atascados en un bucle, donde se devuelven al nodo de origen, que luego registra el error nuevamente, y así sucesivamente. El tiempo de ejecución permitirá que un mensaje se repita 9 veces antes de registrar otro error, que no se puede detectar que rompa el bucle. Eliminar esta propiedad deshabilitará la verificación.

Si el mensaje ya tenía una propiedad msg.error cuando el nodo informó el error, esa propiedad se moverá a msg._error.

De forma predeterminada, el nodo Catch está configurado para ser activado por todos los nodos en la misma pestaña en el editor, pero también se puede configurar para apuntar a nodos específicos en la pestaña.

Si un nodo Catch está configurado para que lo activen todos los nodos, también se puede configurar para que solo se active en errores que aún no hayan sido detectados por otro nodo Catch. Esto le permite crear flujos de manejo de errores que se dirigen a nodos específicos y también tener un controlador de errores que capturará «todo lo demás».

Si se registra un error desde el interior de un subflow, el tiempo de ejecución primero comprobará si hay nodos Catch dentro del subflujo. Si no hay ninguno allí, el error se propagará hasta el flujo que contiene la instancia del subflujo.

Más información: https://nodered.org/docs/user-guide/handling-errors#catchable-errors 

Errores no Registrados

Estos son los errores que un nodo escribe en el registro sin notificar al tiempo de ejecución correctamente. No se pueden manejar mediante el nodo Catch.

El nodo puede proporcionar formas alternativas de manejar el error. Por ejemplo, actualizando su propiedad de estado (que se puede monitorear con el nodo Status). Puede enviar un mensaje normalmente, pero con alguna propiedad adicional establecida para indicar el error.

Errores “uncaughtException”

Estos son un tipo particular de error de node.js que puede ocurrir cuando un nodo no puede manejar correctamente un error interno. Hacen que todo el tiempo de ejecución de Node-RED se apague, ya que eso es lo único seguro que se puede hacer.

Más información: https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly 

La causa típica será que un nodo haya iniciado una tarea asincrónica y esa tarea haya producido un error. Un nodo bien escrito habrá registrado un controlador de errores para esa tarea, pero si no hay uno, el error no se detectará.

Si encuentra este tipo de error, debe intentar identificar qué nodo causó el error y elevar un caso contra el autor del nodo. Esto no siempre es fácil debido a la naturaleza asincrónica del error.

Nodo Status

No todas las condiciones de error aparecerán como eventos de error que pueden detectarse en un nodo Catch. Por ejemplo, los nodos MQTT que pierden su conexión no generarán un error, pero sí un cambio de estado.

Así como el nodo Catch se puede usar para manejar eventos de error, el nodo Status se puede usar para manejar cambios en el estado de un nodo.

El mensaje enviado por el nodo Estado incluye la propiedad de estado que proporciona información sobre el estado y el nodo que desencadenó el evento.

 {
     "status": {
         "fill": "red",
         "shape": "ring",
         "text": "node-red:common.status.disconnected",
         "source": {
             "id": "27bbb5b1.d3eb3a",
             "type": "mqtt out"
         }
     }
 } 

El la visualización del estado de los nodos está habilitado de forma predeterminada, pero se puede deshabilitar en el menú > configuración

Aunque es útil como indicador visual, es aún más útil tener esta información de estado disponible en el flujo y poder tomar medidas en función del estado del nodo. Para ello disponemos del nodo “Status”.

Al añadir este nodo debe indicarse que nodos vamos a monitorizar su estado. El nodo de estado envía un mensaje cada vez que cambia el estado de los nodos supervisados. Este mensaje consta de dos objetos.

  • objeto de estado: se utiliza para obtener información de estado
  • objeto de origen: identifica el origen del mensaje mediante el ID de nodo. Cada nodo tiene una identificación de nodo única.

Debido a que la información de estado solo se envía cuando el estado cambia, generalmente es necesario colocar la información en una variable global o de flujo. Para hacer esto, pase el mensaje de estado a un nodo de función que tenga el siguiente código.

 var mqtt_status=msg.status;
 global.set('mqtt_status',mqtt_status);
 return;

Al crear funciones puedo poner un estado que sea leible desde el nod status: https://nodered.org/docs/creating-nodes/status 

Más información: http://www.steves-internet-guide.com/using-the-node-red-status-node/ 

Nodo Complete

Activa un flujo cuando otro nodo completa el tratamiento de un mensaje. Si un nodo le dice al runtime cuando ha terminado de manejar un mensaje, este nodo puede usarse para desencadenar un segundo flujo.

Por ejemplo, esto se puede utilizar junto con un nodo sin puerto de salida, como el nodo de envío de correo electrónico, para continuar el flujo.

Este nodo debe estar configurado para manejar el evento para los nodos seleccionados en el flujo. A diferencia del nodo Catch, no proporciona un modo ‘manejar todo’ que se aplica automáticamente a todos los nodos del flujo.

No todos los nodos activarán este evento; dependerá de si se han implementado para admitir esta función como se introdujo en Node-RED 1.0.

Más información: https://nodered.org/blog/2019/09/20/node-done

Debug Node-RED

El nodo debug puede ver las propiedades del mensaje enviando el mensaje al nodo de depuración.

La barra lateral de depuración muestra los mensajes que se pasan a los nodos de depuración dentro del flujo, así como ciertos mensajes de registro del tiempo de ejecución.

Junto a cada mensaje, la barra lateral de depuración incluye información sobre la hora a la que se recibió el mensaje y qué nodo de depuración lo envió. Al hacer clic en la identificación del nodo de origen, se revelará ese nodo dentro del espacio de trabajo.

El botón en el nodo se puede utilizar para habilitar o deshabilitar su salida. Se recomienda deshabilitar o eliminar los nodos de depuración que no se estén utilizando.

El nodo también se puede configurar para enviar todos los mensajes al registro de tiempo de ejecución o para enviar mensajes cortos (32 caracteres) al texto de estado en el nodo de depuración.

El nodo de depuración puede establecer su estado independientemente de lo que pase a la barra lateral de depuración. Útil si desea depurar un resumen más corto del estado, mientras muestra información más completa en la barra lateral donde hay más espacio. Para ello debe activarse en la configuración del nodo mostrar su estado.

Los mensaje de debug de la barra lateral se pueden borrar y filtrar por:

  • Todos los nodos
  • Flujo/pestaña actual
  • Nodos seleccionados.

De forma predeterminada, la barra lateral debug muestra todos los mensajes que se le pasan. Esto se puede filtrar haciendo clic en el botón para abrir el panel de opciones de filtro.

La barra lateral de depuración solo puede mostrar los 100 mensajes más recientes. Si la barra lateral muestra actualmente una lista filtrada de mensajes, los mensajes ocultos aún cuentan para el límite de 100. Si un flujo tiene nodos de depuración ruidosos, en lugar de filtrarlos desde la barra lateral, puede ser mejor desactivarlos haciendo clic en su botón en el espacio de trabajo.

El botón en el pie de página de la barra lateral se puede utilizar para abrir una ventana separada del navegador que contiene la barra lateral Depurar.

Existen una serie de acciones en bloque sobre los nodos de debug. Pueden verse en Action List (Ctrl/Cmd-Shift-P or View -> Action List) y asignar atajos de teclado a ellos:

  • core:activate-selected-debug-nodes
  • core:activate-all-debug-nodes
  • core:activate-all-flow-debug-nodes
  • core:deactivate-selected-debug-nodes
  • core:deactivate-all-debug-nodes
  • core:deactivate-all-flow-debug-nodes

Nodo para ver el payload que pasa: https://flows.nodered.org/node/node-red-show-value 

Más información:

Logging en Node-RED

Para ver los logs de node red:

  • node-red-log command (se se ha instalado con el script)
  • sudo journalctl -f -u nodered -o cat

Logging en Node-RED: https://nodered.org/docs/user-guide/runtime/logging

Node-RED usa un logger que escribe su salida en la consola. También admite el uso de módulos de registro personalizados para permitir que la salida se envíe a otra parte.

El logger de la consola se puede configurar en la propiedad de logging en su archivo de configuración settings.js:

 // Configure the logging output
 logging: {
     // Console logging
     console: {
         level: "info",
         metrics: false,
         audit: false
     }
 } 

Hay 3 propiedades que se utilizan para configurar el comportamiento del logger:

  • level: Nivel de logging a registrar
  • metrics: Cuando se establece en true, el tiempo de ejecución de Node-RED genera información sobre la ejecución del flujo y el uso de la memoria.
  • audit: Cuando se establece en verdadero, se registran los eventos de acceso a la API HTTP de administrador. El evento incluye información adicional como el punto final al que se accede, la dirección IP y la marca de tiempo.

También se puede utilizar un módulo de logging personalizado. Por ejemplo, la salida de métricas puede enviarse a un sistema separado para monitorear el desempeño del sistema. Esto luego se puede mandar a un sistema de análisis de log externo como Kibana o Logstash

Más información: https://nodered.org/docs/user-guide/runtime/logging 

El log está completamente administrado por journald fuera de node-red. Si está ejecutando en un sistema Linux utilizando el archivo de servicio systemd para ejecutar node-red, la salida se envía a /var/log/syslog.

El log se puede ver con el comando: sudo journalctl -f -u nodered -o cat

Salida de log al inicializar Node-RED:

 Start Node-RED
 Once Node-RED has started, point a browser at http://127.0.0.1:1880
 On Pi Node-RED works better with the Firefox or Chrome browser
 Use   sudo systemctl enable nodered.service  to autostart Node-RED at every boot
 Use   sudo systemctl disable nodered.service to disable autostart on boot
 To find more nodes and example flows - go to http://flows.nodered.org
 21 Nov 11:04:58 - [info] 
 Welcome to Node-RED
 ===================
 21 Nov 11:04:58 - [info] Node-RED version: v1.2.5
 21 Nov 11:04:58 - [info] Node.js  version: v12.19.1
 21 Nov 11:04:58 - [info] Linux 4.18.0-193.28.1.el8_2.x86_64 x64 LE
 21 Nov 11:04:59 - [info] Loading palette nodes
 node-telegram-bot-api deprecated Automatic enabling of cancellation of promises is deprecated.
 In the future, you will have to enable it yourself.
 See https://github.com/yagop/node-telegram-bot-api/issues/319. internal/modules/cjs/loader.js:1015:30
 21 Nov 11:05:03 - [info] RedBot version: 0.19.7 (node-red-contrib-chatbot)
 21 Nov 11:05:09 - [info] Dashboard version 2.23.4 started at /ui
 21 Nov 11:05:09 - [info] Settings file  : /root/.node-red/settings.js
 21 Nov 11:05:09 - [info] Context store  : 'memoryOnly' [module=memory]
 21 Nov 11:05:09 - [info] Context store  : 'file' [module=localfilesystem]
 21 Nov 11:05:09 - [info] User directory : /user/.node-red
 21 Nov 11:05:09 - [info] Server now running at https://127.0.0.1:1880/
 21 Nov 11:05:09 - [info] Active project : Demo_Proyecto
 21 Nov 11:05:09 - [info] Flows file     : /user/.node-red/projects/Demo_Proyecto/flow.json
 21 Nov 11:05:09 - [info] Starting flows
 21 Nov 11:05:09 - [info] Started flows
 21 Nov 11:05:09 - [info] [mqtt-broker:2ac34a26.7bd096] Connected to broker: mqtt://localhost:1883 

Flow Debugger

Una de las características que se mejorará en las siguientes versiones de Node-RED es un depurador de flujo adecuado.

Node-RED proporciona el nodo de depuración para ayudar al desarrollador a comprender lo que sucede en su flujo. Son herramientas útiles, pero tienen sus límites. Solo puede depurar en los puntos en los que ha agregado un nodo al flujo. Cuando tiene un flujo que involucra múltiples ramas y eventos de tiempo, puede ser difícil ver cómo fluyen los mensajes en cada rama al mismo tiempo.

Flow Debugger permitirá al desarrollador establecer puntos de interrupción a lo largo de su flujo. Cuando un mensaje llega a un punto de interrupción, el tiempo de ejecución se pausará y los mensajes dejarán de fluir. Luego, el desarrollador puede inspeccionar el flujo en diferentes puntos para inspeccionar los mensajes que esperan ser entregados.

Cuando se ejecuta con el depurador habilitado, el usuario podrá visualizar el rendimiento de sus flujos, para ver dónde se está gastando el tiempo o si existen cuellos de botella que podrían optimizarse.

Funciones en Node-RED

El nodo Function te permite ejecutar cualquier código JavaScript contra el mensaje. Esto te da una completa flexibilidad en lo que haces con el mensaje, pero requiere familiaridad con JavaScript y es innecesario para muchos casos simples. 

Muchas veces existen nodos ya hechos para alguna función que deseemos hacer, si eres novato, antes de hacer una función busca en https://flows.nodered.org/ si hay algún nodo o flujo que lo haga.

El nodo función es un bloque de funciones de JavaScript que se ejecuta contra los mensajes que recibe el nodo. Los mensajes se pasan como un objeto JavaScript llamado msg.

Por convención, tendrá una propiedad msg.payload que contiene el cuerpo del mensaje, además de otras propiedades opcionales como msg.topic.

Se espera que la función devuelva un objeto de mensaje (o varios objetos de mensaje), pero puede optar por no devolver nada para detener un flujo.

La pestaña Setup contiene código que se ejecutará siempre que se inicie el nodo. La pestaña Close contiene código que se ejecutará cuando se detenga el nodo.

Desde la versión 1.1.0, el nodo Función proporciona una pestaña Configuración donde puede proporcionar código que se ejecutará siempre que se inicie el nodo. Esto se puede utilizar para configurar cualquier estado que requiera el nodo Función.

Por ejemplo, puede inicializar valores en contexto local que usará la función principal:

 if (context.get("counter") === undefined) {
     context.set("counter", 0)
 } 

Tenga en cuenta que cada fragmento de código está en un ámbito separado; no puede declarar variables en uno y acceder a ellas en los demás. Necesitas usar el contexto para pasar cosas entre ellos.

La función de Setup puede devolver una Promise (promesa) si necesita completar un trabajo asincrónico antes de que la función principal pueda comenzar a procesar mensajes. Cualquier mensaje que llegue antes de que se complete la función de configuración se pondrá en cola y se manejará cuando esté listo.

Si usa código callback asíncrono en sus funciones, es posible que deba limpiar las solicitudes pendientes o cerrar cualquier conexión, cuando el nodo sea re-deployed. Puede hacer esto de dos formas diferentes.

O agregando un controlador de eventos en caso de close:

 node.on('close', function() {
     // tidy up any async code here - shutdown connections and so on.
 }); 

O, desde Node-RED 1.1.0, puede agregar código a la pestaña Close en el cuadro de diálogo de edición del nodo.

Las pestañas Setup y Close se ejecutan cuando se hace un deploy o al reiniciar Node-RED.

Mas información sobre escribir funciones en Node-RED:

Pros y contras de usar funciones en lugar de nodos: https://discourse.nodered.org/t/pros-and-cons-about-using-custom-functions-vs-the-basic-set-of-nodes/2959 

Otro nodo alternativo con procesos hijos es func-exec: https://flows.nodered.org/node/node-red-contrib-func-exec

Vídeo: https://www.youtube.com/watch?v=8XL3Zq1HjCo 

Escribir una Función

El código dentro del nodo Función representa el cuerpo de la función. La función más simple simplemente devuelve el mensaje exactamente como está: return msg;

Si la función devuelve un valor nulo, no se transmite ningún mensaje y el flujo finaliza.

La función siempre debe devolver un objeto msg. Devolver un número o una cadena resultará en un error.

El objeto del mensaje devuelto no necesita ser el mismo objeto que se pasó; la función puede construir un objeto completamente nuevo antes de devolverlo. La construcción de un nuevo objeto de mensaje perderá todas las propiedades del mensaje recibido. Esto interrumpirá algunos flujos, por ejemplo, el flujo de entrada/respuesta HTTP requiere que las propiedades msg.req y msg.res se conserven de un extremo a otro. En general, los nodos de función deben devolver el objeto de mensaje que se les pasó después de haber realizado cambios en sus propiedades.

Función que devuelve un nuevo mensaje eliminando las propiedades del mensaje original:

 var newMsg = { payload: msg.payload.length };
 return newMsg; 

Ejemplo, crear una función que pase el payload y el topic a mayúsculas:

 var payload=msg.payload; //get payload
 msg.payload=payload.toUpperCase(); //convert to uppercase
 var topic=msg.topic; //get topic
 msg.topic=topic.toUpperCase();//convert to uppercase
 return msg; 

Enviando mensajes

La función puede devolver los mensajes que quiere pasar a los siguientes nodos del flujo con return o puede llamar a node.send (mensajes).

Puede devolver o enviar:

  • un objeto de mensaje único: se pasa a los nodos conectados a la primera salida
  • una matriz de objetos de mensaje: se pasa a los nodos conectados a las salidas correspondientes (múltiples salidas)

Si algún elemento de la matriz es en sí mismo una matriz de mensajes, se envían varios mensajes a la salida correspondiente.

Si se devuelve un valor nulo, ya sea por sí mismo o como un elemento de la matriz, no se transmite ningún mensaje.

Múltiples Salidas

El diálogo de edición de funciones permite cambiar el número de salidas. Si hay más de una salida, la función puede devolver una matriz de mensajes para enviar a las salidas.

Esto facilita escribir una función que envíe el mensaje a diferentes salidas dependiendo de alguna condición. Por ejemplo, esta función enviaría cualquier cosa sobre el tema banana a la segunda salida en lugar de a la primera:

 if (msg.topic === "banana") {
    return [ null, msg ];
 } else {
    return [ msg, null ];
 } 

El siguiente ejemplo pasa el mensaje original tal cual en la primera salida y un mensaje que contiene la longitud de la carga útil se pasa a la segunda salida:

 var newMsg = { payload: msg.payload.length };
 return [msg, newMsg]; 

Ejemplo. Usamos dos nodos inject para inyectar un mensaje sobre dos temas diferentes en un nodo de función con dos salidas. La función envía el mensaje a la salida según el nombre del tema. El tema test1 va a output1 y test2 va a output2.

Código:

 var topic=msg.topic;
 if (topic=="test1"){
     return [msg,null];
 }
 if (topic=="test2"){
     return [null,msg];
 } 

Múltiples Mensajes

Una función puede devolver varios mensajes en una salida al devolver una matriz de mensajes dentro de la matriz devuelta. Cuando se devuelven varios mensajes para una salida, los nodos posteriores recibirán los mensajes uno a uno en el orden en que se devolvieron.

En el siguiente ejemplo, msg1, msg2, msg3 se enviarán a la primera salida y msg4 se enviará a la segunda salida.

 var msg1 = { payload:"first out of output 1" };
 var msg2 = { payload:"second out of output 1" };
 var msg3 = { payload:"third out of output 1" };
 var msg4 = { payload:"only message from output 2" };
 return [ [ msg1, msg2, msg3 ], msg4 ]; 

El siguiente ejemplo divide la carga útil recibida en palabras individuales y devuelve un mensaje para cada una de las palabras.

 var outputMsgs = [];
 var words = msg.payload.split(" ");
 for (var w in words) {
     outputMsgs.push({payload:words[w]});
 }
 return [outputMsgs]; 

Ejemplo. Usar un nodo inject para inyectar una cadena de prueba en el nodo de función. El nodo de función toma la cadena, pero en lugar de enviar 1 mensaje, tiene un bucle for que crea 3 mensajes y los coloca en un array.

Código:

 var m_out=[]; //array for message objects
 var message=msg.payload;
 for (i=0;i<3;i++){
     message=message+i; //add count to message
     var newmsg={payload:message,topic:msg.topic}
     m_out.push(newmsg);
 }
 return [m_out]; 

Enviar mensajes de forma asincrónica

Si la función necesita realizar una acción asincrónica antes de enviar un mensaje, no puede devolver el mensaje al final de la función con return.

En su lugar, debe hacer uso de la función node.send(), pasando los mensajes que se enviarán.

Por ejemplo:

 doSomeAsyncWork(msg, function(result) {
     msg.payload = result;
     node.send(msg);
 });
 return; 

Esto es útil, por ejemplo, al recorrer una matriz u objeto y enviar datos a medida que se leen. Por ejemplo:

 count=0;
 for(var i=0;i<10;i++)
 {
 msg.payload=count;
 node.send(msg)
 count+=1; 
 } 

Ejemplo: ver como al mandar send, los mensajes se procesan de forma simultánea al ser asíncrono:

 for(var i=0;i<10;i++) {
     msg.payload=i;
     await new Promise(r => setTimeout(r, 1000)); //sleep(1000)
     node.send(msg);
 } 

Desde la versión Node-RED 1.0, el nodo Función clonará cada objeto de mensaje que pase a node.send para garantizar que no haya modificaciones no intencionales de los objetos de mensaje que se reutilicen en la función.

La función puede solicitar al tiempo de ejecución (runtime) que no clone el primer mensaje pasado a node.send pasando falso como segundo argumento de la función. Haría esto si el mensaje contiene algo que de otra manera no se puede clonar, o por razones de rendimiento para minimizar la sobrecarga de enviar mensajes: node.send(msg,false);

Si un nodo Función realiza un trabajo asincrónico con un mensaje, el tiempo de ejecución no sabrá automáticamente cuándo ha terminado de manejar el mensaje.

Para ayudarlo a hacerlo, el nodo Función debe llamar a node.done() en el momento apropiado. Esto permitirá que el tiempo de ejecución rastree correctamente los mensajes a través del sistema.

 doSomeAsyncWork(msg, function(result) {
     msg.payload = result;
     node.send(msg);
     node.done();
 });
 return; 

Registro y manejo de errores

Para registrar cualquier información o reportar un error, las siguientes funciones están disponibles:

  • node.log(«Log message»)
  • node.warn(«Warning»)
  • node.error(«Error»)

Los mensajes de warn y error también se envían a la pestaña de debug del editor de flujo.

Para un registro más fino, también están disponibles node.trace() y node.debug(). Si no hay ningún logger configurado para capturar esos niveles, no se verán.

Si la función encuentra un error que debería detener el flujo actual, no debería devolver nada. El nodo Catch se puede utilizar para manejar errores. Para invocar un nodo Catch, pase msg como segundo argumento a node.error: node.error(«Error», msg);

Acceder a la información del nodo

En el bloque de funciones, se puede hacer referencia a la identificación y el nombre del nodo mediante las siguientes propiedades:

  • node.id – id del nodo
  • node.name – nombre del nodo

Uso de variables de entorno

Se puede acceder a las variables de entorno usando: env.get («MY_ENV_VAR»).

Variables de entorno: https://nodered.org/docs/user-guide/environment-variables 

Almacenamiento de datos

Aparte del objeto msg, la función también puede almacenar datos en el almacén de contexto: https://nodered.org/docs/user-guide/context 

En el nodo Función hay tres variables predefinidas que se pueden usar para acceder al contexto:

  • context: el contexto local del nodo
  • flow: el contexto del alcance del flujo
  • global: el contexto de alcance global

Estas variables predefinidas son una característica del nodo Función y no se usan en la creación de nodos.

Hay dos modos de acceder al contexto; ya sea sincrónico o asincrónico. Los almacenes de contexto integrados proporcionan ambos modos. Es posible que algunos almacenes solo den acceso asincrónico y arrojará un error si se accede a ellas de manera sincrónica.

Para obtener un valor del contexto: var myCount = flow.get(«count»);

Para establecer un valor: flow.set(«count», 123);

El siguiente ejemplo mantiene un recuento de cuántas veces se ha ejecutado la función:

 // initialise the counter to 0 if it doesn't exist already
 var count = context.get('count')||0;
 count += 1;
 // store the value back
 context.set('count',count);
 // make it part of the outgoing msg object
 msg.count = count;
 return msg; 

get/set multiples valores:

 var values = flow.get(["count", "colour", "temperature"]);
 // values[0] is the 'count' value
 // values[1] is the 'colour' value
 // values[2] is the 'temperature' value
 flow.set(["count", "colour", "temperature"], [123, "red", "12.5"]); 

El contexto global se puede rellenar previamente con objetos cuando se inicia Node-RED. Esto se define en el archivo principal settings.js bajo la propiedad functionGlobalContext.

Esto se puede utilizar para cargar módulos adicionales dentro del nodo Función: https://nodered.org/docs/user-guide/writing-functions#loading-additional-modules 

Acceso al contexto asincrónico

Si el almacén de contexto requiere acceso asincrónico, las funciones get y set requieren un parámetro de devolución de llamada adicional.

 // Get single value
 flow.get("count", function(err, myCount) { ... });
 // Get multiple values
 flow.get(["count", "colour"], function(err, count, colour) { ... })
 // Set single value
 flow.set("count", 123, function(err) { ... })
 // Set multiple values
 flow.set(["count", "colour", [123, "red"], function(err) { ... }) 

El primer argumento pasado a la callback, err, solo se establece si ocurrió un error al acceder al contexto.

La versión asincrónica del ejemplo de recuento se convierte en:

 context.get('count', function(err, count) {
     if (err) {
         node.error(err, msg);
     } else {
         // initialise the counter to 0 if it doesn't exist already
         count = count || 0;
         count += 1;
         // store the value back
         context.set('count',count, function(err) {
             if (err) {
                 node.error(err, msg);
             } else {
                 // make it part of the outgoing msg object
                 msg.count = count;
                 // send the message
                 node.send(msg);
             }
         });
     }
 }); 

Múltiples Almacenes de Contexto

Es posible configurar múltiples almacenes de contexto. Por ejemplo, se podría utilizar un almacén basado en memoria y en archivos.

Las funciones de contexto get/set aceptan un parámetro opcional para identificar el almacén a utilizar.

 // Get value - sync
 var myCount = flow.get("count", storeName);
 // Get value - async
 flow.get("count", storeName, function(err, myCount) { ... });
 // Set value - sync
 flow.set("count", 123, storeName);
 // Set value - async
 flow.set("count", 123, storeName, function(err) { ... }) 

Añadir Estado

El nodo de función también puede proporcionar su propia decoración de estado de la misma forma que otros nodos. Para establecer el estado, llame a la función node.status. Por ejemplo:

 node.status({fill:"red",shape:"ring",text:"disconnected"});
 node.status({fill:"green",shape:"dot",text:"connected"});
 node.status({text:"Just text status"});
 node.status({});   // to clear the status 

Para los parámetros aceptados en el estado ver https://nodered.org/docs/creating-nodes/status

  • fill – red, green, yellow, blue or grey
  • shape – Ring or Dot

Las actualizaciones de estado también pueden ser capturadas por el nodo Estado.

Carga de módulos adicionales

Si se necesita usar un módulos de node.js es necesario activarlos previamente. Los módulos de nodo adicionales no se pueden cargar directamente dentro de un nodo de función. Deben cargarse en el archivo settings.js y agregarse a la propiedad functionGlobalContext.

Por ejemplo, el módulo del sistema operativo integrado puede estar disponible para todas las funciones agregando lo siguiente al archivo settings.js.

 functionGlobalContext: {
     osModule:require('os')
 } 

En ese momento, se puede hacer referencia al módulo dentro de una función como global.get (‘osModule’).

Los módulos cargados desde el archivo de configuración deben instalarse en el mismo directorio que el archivo de configuración. Para la mayoría de los usuarios, ese será el directorio de usuario predeterminado: ~/.node-red: 

 cd ~/.node-red
 npm install name_of_3rd_party_module 

Reusar Nodos de Función

Se pueden guardar los nodos de función en la library y reutilizarlos en otros flujos importándolos. Para guardar una función en la library, haga doble clic en la función para editarla y haga clic en el icono de marcador junto al nombre de la función. Aparece un menú desplegable para importar o guardar la función.

También se puede utilizar un subflow para almacenar funciones. El uso de un subflujo los hace disponibles como nodos que puede seleccionar en la paleta de nodos de la izquierda.

Otros Objetos Disponibles

Objetos disponibles en el nodo función:

context

  • context.get(..): get a node-scoped context property
  • context.set(..): set a node-scoped context property
  • context.keys(..): return a list of all node-scoped context property keys
  • context.flow : same as flow
  • context.global : same as global

flow

  • flow.get(..) : get a flow-scoped context property
  • flow.set(..) : set a flow-scoped context property
  • flow.keys(..) : return a list of all flow-scoped context property keys

global

  • global.get(..) : get a global-scoped context property
  • global.set(..) : set a global-scoped context property
  • global.keys(..) : return a list of all global-scoped context property keys

RED

  • RED.util.cloneMessage(..) : safely clones a message object so it can be reused

env

  • env.get(..) : get an environment variable

El nodo Función también pone a disposición los siguientes módulos y funciones:

  • Buffer – the Node.js Buffer module
  • console – the Node.js console module (node.log is the preferred method of logging)
  • util – the Node.js util module
  • setTimeout/clearTimeout – the javascript timeout functions.
  • setInterval/clearInterval – the javascript interval functions.