Archivo de la categoría: Node-RED Developer

Ejercicios Finales Node-RED

Ejercicio 1

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

Ejercicio 2 – Seguridad

Poner un pin para encender los Reles, de forma que si no hay pin no se puede encender desde el dashboard. Simular una cerradura de forma que al poner el pin correcto se abre y luego a los 5 segundos se cierra.

Mostar en el dashboard el estado de la cerradura.

Crear un tab nuevo en el dashboard llamado pin de seguridad

Basarse en el flujo:

Hacer el flujo como un subflow.

Dashboard

Flujo

Subflow

Código:

Ejercicio 3 – Email y SMS

Hacer un formulario en el dashboard para mandar un correo electrónico y otro para mandar un SMS usando el servicio de Twilio: https://www.twilio.com/

Dashboard

Flujo

Código: https://github.com/jecrespo/Curso-Node-RED/blob/master/04-Ejercicio%20Final/Ejercicio_4/Ejercicio_4.json

Luego usar esta configuración para enviar un correo y un SMS cuando se pulse el botón de la Raspberry Pi y el relé del nodo remoto 14 esté encendido, viéndolo en un dashboard.

Ejercicio 4 – Predicción de Lluvia con datos AEMET

Hacer un panel de control que muestre la predicción de lluvia de los próximos 7 días de una ciudad usando los datos de AEMET y su API.

Solución: https://github.com/aprendiendonodered/AEMET_Prediccion_Dias

Además mostrar la predicción de temperatura de los próximos 5 días usado el nodo de open weather map https://flows.nodered.org/node/node-red-node-openweathermap 

Por último publicar todos los datos por MQTT con el topic: “educantabria/nodo00/santander/predicción/[lluvia/santander]/{dia}/{hora}”

Topics publicados:

  • casa/santander/prediccion/lluvia/{dia} – Publica la predicción de lluvia de santander obtenida de AEMET
  • casa/santander/prediccion/temperatura/{dia}/{hora} – Publica la predicción de temperatura de santander obtenida de Open Weather

Creación y Modificación Nodos en Node-RED

Es posible ampliar Node-RED creando nuevos nodos y agregar los nuevos nodos a su paleta. Los nodos se pueden publicar como módulos npm en el repositorio público de npm y agregar a la biblioteca de flujo de Node-RED para que estén disponibles para la comunidad.

Los nodos de Node-RED se empaquetan como módulos y se publican en el repositorio público de npm https://www.npmjs.com/. Una vez publicados en npm, se pueden agregar a la biblioteca de flujo mediante un formulario.

Hay algunos principios generales a seguir al crear nuevos nodos. Estos reflejan el enfoque adoptado por los nodos centrales y ayudan a proporcionar una experiencia de usuario coherente.

Consideraciones Generales

Hay algunos principios generales a seguir al crear nuevos nodos. Estos reflejan el enfoque adoptado por los nodos centrales y ayudan a proporcionar una experiencia de usuario coherente.

Los nodos deben:

  • Estar bien definidos en su propósito. Un nodo que expone todas las opciones posibles de una API es potencialmente menos útil que un grupo de nodos, cada uno de los cuales tiene un único propósito.
  • Ser fácil de usar, independientemente de la funcionalidad subyacente. Ocultar la complejidad.
  • Ser abierto con los tipos de propiedades de mensaje que acepta. Las propiedades de los mensajes pueden ser cadenas, números, valores booleanos, búferes, objetos, matrices o nulos. Un nodo debe hacer lo correcto cuando se enfrenta a cualquiera de estos, evitando ser estricto en el tipo de dato a admitir..
  • Sea consistente en lo que envían. Los nodos deben documentar las propiedades que agregan a los mensajes y deben ser consistentes y predecibles en su comportamiento.
  • Detectar errores. Si un nodo arroja un error no detectado, Node-RED detendrá todo el flujo ya que ya no se conoce el estado del sistema. Siempre que sea posible, los nodos deben detectar errores o registrar controladores de errores para cualquier llamada asincrónica que realicen.

Más información: https://nodered.org/docs/creating-nodes/ 

Crear Primer Nodo

Los nodos se crean cuando se implementa un flujo, pueden enviar y recibir algunos mensajes mientras el flujo se está ejecutando y se eliminan cuando se implementa el siguiente flujo.

Consisten en un par de archivos:

  • un archivo JavaScript que define lo que hace el nodo,
  • un archivo html que define las propiedades del nodo, el diálogo de edición y el texto de ayuda.

Se usa un archivo package.json para empaquetarlo todo junto como un módulo npm.

Este ejemplo mostrará cómo crear un nodo que convierta las cargas útiles de los mensajes a todos los caracteres en minúsculas. Asegúrese de tener la versión LTS actual de Node.js instalada en su sistema.

Crea un directorio donde desarrollarás tu código. Dentro de ese directorio, cree los siguientes archivos:

  • package.json – Un archivo json que npm lo usa para empaquetar. Este es el archivo estándar que utilizan los módulos de node.js para describir el contenido

Este es un archivo estándar utilizado por los módulos de Node.js para describir su contenido. Para generar un archivo package.json estándar, puede usar el comando npm init. Esto hará una serie de preguntas para ayudar a crear el contenido inicial del archivo, utilizando valores predeterminados sensibles cuando sea posible. Cuando se le solicite, asígnele el nombre node-red-contrib-example-lower-case.

Una vez generada, debe agregar una sección de Node-RED. Esto le dice al runtime qué archivos de nodo contiene el módulo:

{
  "name": "red-contrib-example-lower-case",
  "version": "1.0.0",
  "description": "first node example",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "node-red"
  ],
  "author": "jecrespo",
  "license": "ISC",
"node-red" : {
        "nodes": {
            "lower-case": "lower-case.js"
        }
    }
}

Para obtener más información sobre cómo empaquetar su nodo, incluidos los requisitos de nomenclatura y otras propiedades que deben establecerse antes de publicar su nodo, consulte la guía de empaquetado: https://nodered.org/docs/creating-nodes/packaging 

Nota: ¡No publicar este nodo de ejemplo en npm!

  • lower-case.js – Este archivo normalmente contiene una función javascript que tiene la funcionalidad del módulo de nodo. Esto se llama cuando el tiempo de ejecución carga el nodo al iniciarse.

El nodo está empaquetado como un módulo Node.js. El módulo exporta una función que se llama cuando el tiempo de ejecución carga el nodo en el inicio. La función se llama con un solo argumento, RED, que proporciona al módulo acceso a la API de tiempo de ejecución de Node-RED.

El nodo en sí está definido por una función, LowerCaseNode, que se llama cada vez que se crea una nueva instancia del nodo. Se le pasa un objeto config que contiene las propiedades específicas del nodo establecidas en el editor de flujo. La función llama a la función RED.nodes.createNode para inicializar las características compartidas por todos los nodos.

En este caso, el nodo registra un listener en el evento de entrada que se llama cada vez que llega un mensaje al nodo. Dentro de este listener, cambia la carga útil a minúsculas y luego llama a la función de envío para pasar el mensaje en el flujo.

Finalmente, la función LowerCaseNode se registra con el tiempo de ejecución usando el nombre del nodo “lower-case”.

Si el nodo tiene dependencias de módulos externos, deben incluirse en la sección de dependencias de su archivo package.json.

Para obtener más información sobre la parte de tiempo de ejecución del nodo: https://nodered.org/docs/creating-nodes/node-js 

module.exports = function(RED) {
    function LowerCaseNode(config) {
        RED.nodes.createNode(this,config);
        var node = this;
        node.on('input', function(msg) {
            msg.payload = msg.payload.toLowerCase();
            node.send(msg);
        });
    }
    RED.nodes.registerType("lower-case",LowerCaseNode);
}
  • lower-case.html – Este archivo html contiene la definición de nodo principal que está registrada con el editor. También incluye la plantilla que se muestra cuando se configura el nodo. El contenido de la ayuda y las instrucciones sobre las configuraciones necesarias para el nodo también van aquí.

El archivo HTML de un nodo proporciona lo siguiente:

  • la definición de nodo principal que está registrada con el editor
  • la plantilla de edición
  • el texto de ayuda

En este ejemplo, el nodo tiene una única propiedad editable, name. Si bien no es obligatorio, existe una convención ampliamente utilizada para esta propiedad para ayudar a distinguir entre múltiples instancias de un nodo en un solo flujo.

Para obtener más información sobre la parte del editor del nodo, ver https://nodered.org/docs/creating-nodes/node-html

<script type="text/javascript">
    RED.nodes.registerType('lower-case',{
        category: 'function',
        color: '#a6bbcf',
        defaults: {
            name: {value:""}
        },
        inputs:1,
        outputs:1,
        icon: "file.png",
        label: function() {
            return this.name||"lower-case";
        }
    });
</script>

<script type="text/html" data-template-name="lower-case">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
        <input type="text" id="node-input-name" placeholder="Name">
    </div>
</script>

<script type="text/html" data-help-name="lower-case">
    <p>A simple node that converts the message payloads into all lower-case characters</p>
</script>

Una vez creado, vamos a instalarlo. Una vez que haya creado un módulo de nodo básico, puede instalarlo en su runtime de Node-RED. Para probar un módulo de nodo localmente, se puede usar el comando npm install <carpeta>. Esto le permite desarrollar el nodo en un directorio local y vincularlo a una instalación local de node-red durante el desarrollo.

En su directorio de usuario de node-red, normalmente ~/node-red, ejecutar: npm install ../nr_node_development/node-red-contrib-example-lower-case/

Devuelve:

+ red-contrib-example-lower-case@1.0.0
added 1 package from 1 contributor and audited 675 packages in 7.513s

Esto crea un enlace simbólico al directorio del proyecto del módulo de nodo en ~/.node-red/node_modules para que Node-RED detecte el nodo cuando se inicie. Cualquier cambio en el archivo del nodo se puede recoger simplemente reiniciando Node-RED. 

Reiniciar Node-RED y comprobar que aparece el nuevo nodo: 

Más información: https://nodered.org/docs/creating-nodes/ 

Ejemplo. Basado en el nodo lower-case crear un nodo que cuentes los caracteres de un string llamado “char-count”

Pasos:

  • mkdir node-red-contrib-example-charcount
  • crear package.json – npm init
  • Añadir en package.json:
"node-red" : {
        "nodes": {
            "charcount": "charcount.js"
        }
    }
  • crear el fichero charcount.js
module.exports = function(RED) {
    function CharCountNode(config) {
        RED.nodes.createNode(this,config);
        var node = this;
        node.on('input', function(msg) {
            msg.payload = Number(msg.payload.length);
            node.send(msg);
        });
    }
    RED.nodes.registerType("charcount",CharCountNode);
}
  • Crear el fichero charcount.html
<script type="text/javascript">
    RED.nodes.registerType('charcount',{
        category: 'function',
        color: '#a6bbcf',
        defaults: {
            name: {value:""}
        },
        inputs:1,
        outputs:1,
        icon: "file.png",
        label: function() {
            return this.name||"charcount";
        }
    });
</script>
<script type="text/html" data-template-name="charcount">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i>Name</label>
        <input type="text" id="node-input-name" placeholder="Name">
    </div>
</script>
<type="text/html" data-help-name="charcount">
    <p>Node to find the number of characters in payload</p>
</script>
  • Ir al directorio de la instancia de Node-RED e instalar con:  npm install ../nr_node_development/node-red-contrib-example-charcount/
  • Reiniciar Node-RED y comprobar que funciona.

Más información:

Crear Nodos a Partir de Subflows

Ahora es posible empaquetar un subflujo como un módulo y publicarlo en npm para que se instale en la paleta como cualquier otro nodo

Los subflujos se pueden empaquetar como módulos npm y distribuirse como cualquier otro nodo. Cuando estén instalados, aparecerán en la paleta como nodos regulares. Los usuarios no pueden ver ni modificar el flujo dentro del subflujo.

En esta etapa, la creación del módulo de subflujo es un proceso manual que requiere la edición manual del subflujo JSON. Se proporcionarán herramientas en el futuro para ayudar a automatizar esto.

Cualquier subflujo se puede empaquetar como un módulo. Antes de hacerlo, debe pensar en cómo se utilizará. La siguiente lista de verificación es un recordatorio útil de las cosas a considerar:

  • Configuración: qué deberá configurar el usuario en el subflujo. Puede definir propiedades de subflujo y qué interfaz de usuario se proporciona para establecer esas propiedades a través del cuadro de diálogo de edición propiedades de subflujo.
  • Manejo de errores: ¿El subflujo maneja los errores correctamente? Algunos errores pueden tener sentido de manejar dentro del subflujo, algunos pueden necesitar ser pasados fuera del subflujo para permitir que el usuario final los maneje.
  • Estado: agregar una salida de estado personalizada a su subflujo que puede manejar el nodo «Estado».
  • Apariencia: dar al subflujo un ícono, color y categoría que tenga sentido para la función que proporciona.

Añadir metadatos al subflujo. El subflujo puede contener metadatos adicionales que se pueden usar para definir el módulo en el que se empaquetará. En el cuadro de diálogo de edición Propiedades del módulo de subflujo, puede establecer las siguientes propiedades:

  • Módulo: el nombre del paquete npm
  • Tipo de nodo: se establecerá de forma predeterminada en la propiedad id del subflujo. Es útil proporcionar un mejor valor de tipo. Como ocurre con los tipos de nodos normales, debe ser único para evitar conflictos con otros nodos.
  • Versión
  • Descripción
  • Licencia
  • Autor
  • Palabras clave

Como ejemplo vamos a crear un nodo basándose en un subflow llamado charcount2 que tiene una entrada y una salida y una función que cuenta caracteres del mensaje entrante:

var newMsg = { "payload": Number(msg.payload.length)};
return newMsg;

Crear el módulo. Este trabajo es manual fuera de Node-RED.

  • crear un directorio con el nombre que quieras darle al módulo. Para este ejemplo, usaremos node-red-example-charcount2.: mkdir node-red-example-charcount2
  • User npm init para crear un archivo package.json. Hará una serie de preguntas coincidentes a los valores agregados a los metadatos del subflujo.
  • Agregar un archivo README.md, ya que todos los módulos buenos deben tener un archivo README.
  • Crear un contenedor de JavaScript para el módulo. Para este ejemplo, usaremos example.js. Esto lee el contenido de un archivo llamado subflow.json, que crearemos luego, lo analiza y luego lo pasa a la función RED.nodes.registerSubflow.
const fs = require("fs");
 const path = require("path");

 module.exports = function(RED) {
     const subflowFile = path.join(__dirname,"subflow.json");
     const subflowContents = fs.readFileSync(subflowFile);
     const subflowJSON = JSON.parse(subflowContents);
     RED.nodes.registerSubflow(subflowJSON);
 }

Añadir el json del subflow. Ahora agregar el subflujo al módulo. Esto requiere una edición cuidadosa del subflujo json. 

  • En el editor Node-RED, agregue una nueva instancia de su subflujo al espacio de trabajo.
  • Con la instancia seleccionada, exporte el nodo (Ctrl-E o Menú-> Exportar) y pegue el JSON en un editor de texto. Los siguientes pasos serán más fáciles si selecciona la opción «formateado» en la pestaña JSON del cuadro de diálogo Exportar.

El JSON está estructurado como un array de objetos de nodo. La última entrada menos una es la definición de subflujo y la última entrada es la instancia de subflujo que agregó al espacio de trabajo.

  • Elimine el nodo de instancia de subflujo, la última entrada en la matriz.
  • Mueva el nodo de definición de subflujo a la parte superior del archivo, encima de la apertura  [ de la matriz
  • Mueva la matriz restante de nodos dentro del nodo de definición de subflujo como una nueva propiedad llamada «flujo».
  • Asegúrese de ordenar las comas finales entre las entradas movidas.
  • Guarde este archivo como subflow.json
{
    "id": "8c0f41ecebfd3445",
    "type": "subflow",
    "name": "charcount2",
    "info": "",
    "category": "",
    "in": [
        {
            "x": 80,
            "y": 40,
            "wires": [
                {
                    "id": "59bf81d1faddd2e9"
                }
            ]
        }
    ],
    "out": [
        {
            "x": 320,
            "y": 40,
            "wires": [
                {
                    "id": "59bf81d1faddd2e9",
                    "port": 0
                }
            ]
        }
    ],
    "env": [],
    "meta": {
        "module": "charcount2",
        "version": "1.0.0",
        "author": "jecrespo",
        "desc": "count char in payload",
        "keywords": "node-red"
    },
    "color": "#DDAA99",
    "flow": [
        {
            "id": "59bf81d1faddd2e9",
            "type": "function",
            "z": "8c0f41ecebfd3445",
            "name": "",
            "func": "var newMsg = { \"payload\": Number(msg.payload.length)};\nreturn newMsg;",
            "outputs": 1,
            "noerr": 0,
            "initialize": "",
            "finalize": "",
            "libs": [],
            "x": 200,
            "y": 40,
            "wires": [
                []
            ]
        }
    ]
}  

Actualizar package.json: La tarea final es actualizar el package.json para que Node-RED sepa qué contiene su módulo. Agrega una sección «node-red», con una sección de «nodes» que contiene una entrada para tu archivo .js:

{
    "name": "node-red-example-subflow",
    ...
    "node-red": {
        "nodes": {
            "charcount2": "example.js"
        }
    }
}

Añadir dependencias: Si el subflujo utiliza nodos no predeterminados, debe asegurarse de que el archivo package.json los enumera como dependencias. Esto asegurará que se instalen junto con su módulo.

Los módulos se enumeran en la sección «dependencies» de nivel superior estándar y una sección «dependencies» en la sección «node-red».

{
    "name": "node-red-example-subflow",
    ...
    "node-red": {
        "nodes": {
            "example-node": "example.js"
        },
        "dependencies": [
            "node-red-node-random"
        ]
    },
    "dependencies": {
        "node-red-node-random": "1.2.3"
    }
}

Una vez que haya creado un módulo de nodo básico, puede instalarlo en su runtime de Node-RED. Para probar un módulo de nodo localmente, se puede usar el comando npm install <carpeta>. Esto le permite desarrollar el nodo en un directorio local y vincularlo a una instalación local de node-red durante el desarrollo.

En su directorio de usuario de node-red, normalmente ~/node-red, ejecutar: npm install ../nr_node_development/node-red-example-charcount2/

Esto crea un enlace simbólico al directorio del proyecto del módulo de nodo en ~/.node-red/node_modules para que Node-RED detecte el nodo cuando se inicie. Cualquier cambio en el archivo del nodo se puede recoger simplemente reiniciando Node-RED. 

Reiniciar Node-RED y comprobar que aparece el nuevo nodo (subflow y nodo): 

Más información: https://nodered.org/docs/creating-nodes/subflow-modules 

Empaquetado de los Módulos

Los nodos se pueden empaquetar como módulos y publicar en el repositorio npm. Esto lo hace fácil de instalar junto con las dependencias que puedan tener.

Si desea usar node-red en el nombre del nodo, use node-red-contrib- como prefijo al nombre para dejar en claro que no son mantenidos por el proyecto Node-RED. Alternativamente, se puede usar cualquier nombre que no use node-red como prefijo.

Esta es la típica estructura de directorios. No hay requisitos estrictos sobre la estructura de directorios utilizada dentro del paquete. Si un paquete contiene varios nodos, todos podrían existir en el mismo directorio o cada uno podría colocarse en su propio subdirectorio.

├── LICENSE
├── README.md
├── package.json
└── sample
    ├── examples
    │   ├── example-1.json
    │   └── example-2.json
    ├── icons
    │   └── my-icon.svg
    ├── sample.html
    └── sample.js

Junto con las entradas habituales, el archivo package.json debe contener una entrada “node-red” que enumere los archivos .js que contienen nodos para que se cargue el tiempo de ejecución. Si tiene varios nodos en un solo archivo, solo tiene que listar el archivo una vez. Si alguno de los nodos tiene dependencias de otros módulos npm, debe incluirse en la propiedad de dependencias.

Para ayudar a que los nodos sean detectables dentro del repositorio npm, el archivo debe incluir node-red en su propiedad de palabras clave. Esto asegurará que el paquete aparezca al buscar por palabra clave.

El archivo README.md debe describir las capacidades del nodo y enumerar los requisitos previos necesarios para que funcione. También puede ser útil incluir cualquier instrucción adicional que no esté incluida en la parte de la pestaña de información del archivo html del nodo, y tal vez incluso un pequeño flujo de ejemplo que demuestre su uso.

Una vez hecho, publicar en npm. Una guía para ello es: https://docs.npmjs.com/cli/v8/using-npm/developers 

Añadir a flows.nodered.org

A partir de abril de 2020, la biblioteca de flujo Node-RED ya no puede indexar y actualizar automáticamente los nodos publicados en npm con la palabra clave node-red. En cambio, una solicitud de envío debe realizarse manualmente.

Para hacerlo, asegúrese de que se cumplan todas os requisitos de packaging. Para agregar un nuevo nodo a la biblioteca, haga clic en el botón + en la parte superior de la página de la biblioteca https://flows.nodered.org/ y seleccione la opción «nodo». Este botón lo lleva a la página Agregar un nodo. Aquí, la lista de requisitos se repite y describe los pasos para agregarla a la biblioteca.

Para actualizar un nodo existente, puede volver a enviarlo de la misma manera que lo haría para un nuevo nodo o solicitar una actualización desde la página del nodo en la biblioteca de flujo a través del enlace «solicitar actualización». Esto solo es visible para los usuarios registrados.
Más información: https://nodered.org/docs/creating-nodes/packaging

Ejercicios Subflows y Funciones Node-RED

Ejercicio 01

Hacer un dashboard para controlar los wemos D1 Mini con el firmware instalado en el curso: https://aprendiendoarduino.wordpress.com/2021/02/20/mqtt-y-esp8266/

El Dashboard tendrá:

  • Un gauge y una gráfica indicando la temperatura de la sonda DS18B20
  • Un pulsador para encender y apagar el led del Wemos D1 Mini
  • Un pulsador para encender y apagar el relé
  • Un Input text o un formulario para mandar mensajes a la pantalla oled
  • Un slider para encender los 8 niveles de la matriz led.

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

Flujo:

Dashboard:

Ejercicio 2

Configura Node-RED para que se vea un dashboard “Home” con tres grupos:

  • Suscripciones: las 3 suscripciones y botón que resete los valores
  • Pulsadores: dos botones para encender led y relé de nodo remoto
  • RPi: un pulsador que maneje el LED de la RPi y un gauge con el estado del pulsador.

Además al pulsar el pulsador encender el led detectando flaco, una pulsación enciende y otra pulsación apaga.

La configuración en Node-RED es:

https://github.com/jecrespo/Curso-Node-RED/blob/master/04-Ejercicio%20Final/Ejercicio_1/Dashboard_1.png?raw=true

Código: https://github.com/jecrespo/Curso-Node-RED/blob/master/04-Ejercicio%20Final/Ejercicio_1/Ejercicio_1-1.json

Código: https://github.com/jecrespo/Curso-Node-RED/blob/master/04-Ejercicio%20Final/Ejercicio_1/Ejercicio_1-2.json

Ejercicio 03

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 

Ejercicio 04

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

JSONata para Node-RED

JSONata es un lenguaje de consulta y transformación de objetos JSON

Las estructuras de datos son uno de los elementos más importantes de cualquier lenguaje de programación. Nos permite estructurar los datos de manera consistente y realizar operaciones que requieren algunos patrones. Sin un patrón, es difícil para cualquier desarrollador encontrar una forma eficiente de extraer o manipular los datos. JSON es un formato muy conocido que la mayoría de nosotros usamos todos los días. Al trabajar con él, hay operaciones que hacemos habitualmente, como consultar, filtrar, ordenar, agrupar y otras.

JSONata es un lenguaje ligero de consulta y transformación para datos JSON. Inspirado en la semántica de ‘location path’ de XPath 3.1, permite expresar consultas sofisticadas en una notación compacta e intuitiva. Se proporciona un rico complemento de operadores y funciones incorporados para manipular y combinar datos extraídos, y los resultados de las consultas se pueden formatear en cualquier estructura de salida JSON utilizando un objeto JSON familiar y una sintaxis de array. Junto con la posibilidad de crear funciones definidas por el usuario, se pueden crear expresiones avanzadas para abordar cualquier tarea de transformación y consulta JSON.

Web: https://jsonata.org/

Documentación: http://docs.jsonata.org/overview.html

Más información:

Para admitir la extracción de valores de una estructura JSON, se define una sintaxis de ruta de ubicación. Al igual que XPath, esto seleccionará todos los valores posibles en el documento que coincidan con la ruta de ubicación especificada. Las dos construcciones estructurales de JSON son objetos y arrays.

Un objeto JSON es una matriz asociativa. La sintaxis de la ruta de ubicación para navegar en una estructura profundamente anidada de objetos JSON comprende los nombres de campo separados por un punto ‘.’ delimitadores. La expresión devuelve el valor JSON al que se hace referencia después de navegar al último paso en la ruta de ubicación. Si durante la navegación de la ruta de ubicación, no se encuentra un campo, la expresión no devuelve nada (representado por Javascript undefined). No se producen errores como resultado de datos no existentes en el documento de entrada.

Ejemplos en:

Los arrays JSON se utilizan cuando se requiere una colección ordenada de valores. Cada valor de la matriz está asociado con un índice (posición) en lugar de un nombre, por lo que para abordar valores individuales en una matriz, se requiere sintaxis adicional para especificar el índice. Esto se hace usando corchetes después del nombre de campo de la matriz. Si los corchetes contienen un número o una expresión que se evalúa como un número, entonces el número representa el índice del valor a seleccionar. Los índices tienen un desplazamiento cero, es decir, el primer valor de una matriz arr es arr [0]. Si el número no es un número entero, entonces se redondea a un número entero. Si la expresión entre corchetes no es numérica o es una expresión que no se evalúa como un número, entonces se trata como un predicado.

Los índices negativos cuentan desde el final de la matriz, por ejemplo, arr [-1] seleccionará el último valor, arr [-2] el penúltimo, etc. Si se especifica un índice que excede el tamaño de la matriz, entonces no se selecciona nada.

Si no se especifica ningún índice para una matriz (es decir, no hay corchetes después de la referencia del campo), se selecciona toda la matriz. Si la matriz contiene objetos y la ruta de ubicación selecciona campos dentro de estos objetos, se consultará cada objeto dentro de la matriz para su selección.

Ejemplos en:

Predicate Expressions

En cualquier paso de una ruta de ubicación, los elementos seleccionados se pueden filtrar utilizando un predicado – [expr] donde expr se evalúa como un valor booleano. Cada elemento de la selección se compara con la expresión, si se evalúa como verdadero, entonces el elemento se mantiene; si es falso, se elimina de la selección. La expresión se evalúa en relación con el elemento actual (contexto) que se está probando, por lo que si la expresión de predicado realiza la navegación, entonces es relativa a este elemento de contexto.

Ejemplos:

  • Account.Order.Product.[ProductID=858383]
  • Account.Order.Product[ProductID=858383].`Product Name`

Ejemplos en:

Comodines

Uso de * en lugar del nombre del campo para seleccionar todos los campos de un objeto.

El comodín descendiente ** en lugar de * atravesará todos los descendientes (comodín de varios niveles).

Al procesar el valor de retorno de una expresión JSONata, podría ser conveniente tener los resultados en un formato coherente, independientemente de cuántos valores coinciden, es decir, forzar a que devuelva un array de valores. La expresión se puede modificar para que devuelva un array incluso si solo se coincide con un valor único. Esto se hace agregando corchetes vacíos [] a un paso dentro de la ruta de ubicación.

Funciones y Expresiones

Strings

Las expresiones de ruta que apuntan a un valor de cadena (string) devolverán ese valor.

Los literales de strings también se pueden crear encerrando la secuencia de caracteres entre comillas. Se pueden utilizar comillas dobles » o comillas simples ‘, siempre que se utilice el mismo tipo de comillas para el inicio y el final del literal de cadena. Los caracteres de comillas simples se pueden incluir dentro de una cadena de comillas dobles y viceversa sin escapar. Caracteres dentro del string se puede escapar utilizando el mismo formato que las cadenas JSON.

Las cadenas se pueden combinar utilizando el operador de concatenación &. Este operador unirá las dos cadenas devueltas por las expresiones a cada lado. Este es el único operador que intentará forzar sus operandos al tipo esperado (cadena).

Numéricos

Los literales numéricos también se pueden crear utilizando la misma sintaxis que los números JSON. Los números se pueden combinar utilizando los operadores matemáticos habituales para producir un número resultante. Operadores admitidos:

  • + suma
  • – resta
  • * multiplicación
  • / división
  • % resto (módulo)

$number pasa a número el dato y poder operar en JSONata

Comparación

Se utiliza a menudo en predicados, para comparar dos valores. Devuelve un valor booleano verdadero o falso. Operadores admitidos:

  • = igual
  • ! = no es igual
  • < menor que
  • <= menor o igual que
  • > mayor que
  • > = mayor o igual que
  • in valor está contenido en una matriz

Booleanos

Se utiliza para combinar resultados booleanos, a menudo para admitir expresiones de predicado más sofisticadas. Operadores admitidos:

  • and
  • or

Ejemplo: $[data.age > 16 and data.age < 20]

Tenga en cuenta que not se admite como función, no como operador.

Ejemplos en:

Otras tareas que se pueden realizar:

  • Ordenar
  • Hacer funciones
  • Trabajar con fechas
  • Trabajar con strings
  • etc…

Ejercicios Dashboard Node-RED

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: https://youtu.be/wS0E1j1q7sM  

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

Sobre el ejercicio2, 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 payload 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