diff --git a/.gitignore b/.gitignore index a36b36e..714f407 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ -js/node_modules/ +**/node_modules/ credentials.py *.pyc -package*.json +#package*.json +package-lock.json node-red/docker/data/node_modules/ .npm .config* .flows* +.package* *.tgz *.tar.gz +js/package.json diff --git a/js/gpio-basic/README.md b/js/gpio-basic/README.md index 3d35005..7cbfb00 100644 --- a/js/gpio-basic/README.md +++ b/js/gpio-basic/README.md @@ -1,6 +1,268 @@ +# Raspberry PI GPIO Basic control with Javascript / Node.js using Arduino Cloud +This project shows how to interact with the Raspberry Pi GPIOs from a dashboard created using Arduino Cloud with an application programmed in Node.js. It can serve as an example to create your own applications that require access to RPI GPIOs and that can be ultimately controlled using a dashboard. + +## The setup + +In this project I have used a Raspberry Pi 5 connected to an LED and a push button, both inserted in a breadboard. This is the diagram + +![alt text](../../assets/RPI-GPIO-Basic-Diagram.png) + +> Note: This project should work with any Raspberry Pi version and actually with any Linux-based machine that supports libgpiod. Please, drop your comments in **Issues** if it does not work with your board in order to review it. + +## How to create your project + +The process to use this project is very straighforward: +1. Create the Device and Thing in the Arduino Cloud and get the Arduino Cloud API Key +2. Set up the Node.js environment +3. Develop the Node.js application +4. Create the Arduino Cloud dashboard +5. Test everything + +## 1. Create the Device and Thing in the Arduino Cloud + +> Note: Before getting started, make sure that you have an [Arduino Cloud account](https://cloud.arduino.cc/home/?get-started=true) + +### Create the Device +Go to the [Devices](https://app.arduino.cc/devices) section of the Arduino IoT Cloud and click on **+ DEVICE**. + +Select **Any Device** and follow the instructions on the wizard. + +> Note: Save your `Device ID` and `Secret Key` as they will be used in your code. + +### Create the Thing +In your recently created device page, go to the Associated Thing section, click on **Create Thing** and rename it. + +> Note: You can also create the Thing from the [Things list](https://app.arduino.cc/things) and associate it later. + +### Create the Variables +Add the variables by clicking on the ADD button. At the end of the process, your list of variables should look like this. + +| Name | Type | Description | +|---------------------|------------|-------------| +| button | Boolean | It will hold the status of the physical button | +| led | Boolean | The variable that we will use to act over the physical LED | +| test_value | Integer | This is a value that will change periodically in the application | + +> Note: All the variables have to be READ-WRITE. You can define the periodicity you wish or set them with the policy ON-CHANGE. + +This is a screenshot for reference. + +![Arduino Cloud variables](../../assets/RPI-GPIO-Basic-Thing_Variables2.png) + +## 2. Set up your Node.js environment + +### Install the system libraries and required NPM packages + +Now it is time to install the Node.js dependencies in order to use Arduino Cloud. You have a [full tutorial](https://docs.arduino.cc/arduino-cloud/guides/javascript/) that describes the process in detail. + +It can be summarized as follows: + +1. Install GPIOD library in the system +2. Install the Node.js GPIOD package +3. Install the Node.js Arduino Cloud packages + +### Install the GPIOD library in the system +First, you have to install library in the system. If you are using Ubuntu or any other Debian-based machine, you can follow these instructions. + +``` +sudo apt install gpiod libgpiod2 libgpiod-dev libnode-dev +``` + +### Install the GPIOD node.js package using NPM +Next, you have to install the node package. + +> Tip: The NPM modules can be installed globally or locally in the working folder. My suggestion is to work in the `js/` folder and install the modules there. Once installed, you should see a folder `js/node_modules/`. + +``` +npm install --save node-libgpiod node-fetch +``` + +### Install Arduino Cloud Node.js packages +To install the Arduino IoT Cloud client library, you only have to do the following: + +``` +npm install arduino-iot-js +``` + +## 3. Develop the Node.js application + +Use your favourite programming environment and edit the `gpio-basic.js` file. + +Create a file called `credentials.js` inside the `js/gpio-basic/` folder with the following content + +``` +module.exports = { + DEVICE_ID: 'YOUR_DEVICE_ID', + SECRET_KEY: 'YOUR_SECRET_KEY' +}; +``` + +If you are using a different Raspberry Pi flavour or any other machine, you should check which is the right chipset (in this example we use `gpiochip4`) and which are the lines that you want to use. Then, modify the following lines accordingly: + +``` +const GPIOCHIP = 'gpiochip4'; +const LED = 14; // GPIO14, Pin 8 +const BUTTON = 15; // GPIO15, Pin 10 +``` + +> Note: In the code, make sure that all the variables are global (chip, ledLine, buttonLine, ...). Otherwise, any timed operation will not work properly. This is mainly an important tip if you are going to modify or adapt the code for your own purposes. + +If you want to learn more about GPIOs, Raspberry Pi and libraries, check the [Annex](README.md#notes) at the end of this doc. + +## 4. Create the Arduino Cloud dashboard + +The dashboard that we are going to build will look like this + +![alt text](../../assets/RPI-GPIO-Basic-Dashboard.png) + +There are 2 ways to create the dashboard: +1. Create it manually. Replicate the one shown above following the instructions in [this guide](https://docs.arduino.cc/arduino-cloud/cloud-interface/dashboard-widgets/). + - The LED widgets should be linked to the variable led + - The Value widgets should be linked to the variable test_value + - The Button widgets should be linked to the variable button +2. Clone the one provided by this tutorial following the instructions in the [Annex](README.md#clone-the-dashboard-using-cloud-cli) + +## 5. Test everything + +Run your code + +``` +~/rpi-arduino-cloud/js$ node ./gpio-basic/gpio-basic.js +``` + +You should see the following: +* Every time push the button, the button widget should be updated +* Every time you change the LED widget in the dashboard, the physical LED should switch to ON or OFF +* The value of `test_value` should change randomly every 10s and the value updated in the dashboard + +Enjoy! + +## Additional information +### Arduino Cloud +[Arduino Cloud](https://cloud.arduino.cc/) is a platform that simplifies the process of developing, deploying, and managing IoT devices. It supports various hardware, including Arduino boards, ESP boards and any device programmed with Python, Javascript or Node-RED. It makes it easy for makers, IoT enthusiasts, and professionals to build connected projects without high programming skills. + +The platform allows for easy management and monitoring of connected devices through customizable dashboards, which provide real-time visualisations of the device's data. The dashboards can be accessed remotely through the mobile app Arduino IoT Cloud Remote, which is available for both Android and iOS devices, allowing users to manage their devices from anywhere. + +#### Clone the dashboard using Cloud CLI + +As described in the tutorial, you can create the dashboard on your own, but here I will show you a very handy trick so that you can just make a copy of a template that I have created. For that, you need to use [Arduino Cloud CLI](https://docs.arduino.cc/arduino-cloud/arduino-cloud-cli/getting-started/). + +The steps are the following: +1. Download and extract the latest release. +Download it from [here](https://github.com/arduino/arduino-cloud-cli/releases) +Make sure it is in your machine's PATH, so that it can be used globally. +After installation, check that it is working by opening a terminal, and type: + +2. Set your credentials +To authenticate with the Arduino Cloud, we will need to first set our credentials, using our clientId and clientSecret which are obtained from the Arduino Cloud [API keys section](https://app.arduino.cc/api-keys). Run the following command and introduce the credentials: +``` +arduino-cloud-cli credentials init +``` + +3. Create the dashboard + +``` +arduino-cloud-cli dashboard create \ + --name \ + --template rpi-gpio-basic-dashboard.yaml \ + --override Raspberry-Basic-GPIO= +``` +Replace *\* and *\* with your actual data. + +### GPIOs and Raspberry Pi +There are many GPIO libraries that can be used with Raspberry Pis. Among some of the most popular, we can find: onoff, jonhny-five, rpi-gpio, gpiozero, .... One of the issues that I found is that some of the libraries only work for certain versions of RPI. For instance, the new RPI 5, has a brand new chipset for managing GPIOs, and not all the libraries work for it. + +After a lot of googling and searching, I ended up using *gpiod* as it is the one supported by the Linux kernel team directly and it can be used across all the RPI flavours. + +#### **GPIOD** library + +##### Installation +First, you have to install library in the system. +If you are using Ubuntu or any other Debian-based machine, you can follow these instructions + +``` sudo apt install gpiod libgpiod2 libgpiod-dev libnode-dev +``` + +Next, you have to install the NPM library. + +``` npm install --save node-libgpiod node-fetch +``` + +##### Notes +The GPIOD API has evolved quite a lot over time. The versions before 2.0.0 have a different API than the newer ones. This project is using the library [node-libgpiod](https://github.com/sombriks/node-libgpiod) which uses libgpiod v2.x. It's important to use version v0.4.0 or above of `node-libgpiod`. + +If you want to check what is the gpiochip and line that you have to use in the code, you can use the following command line commands. + +This is an example of the output in a Raspberry PI 5 +``` +$ sudo gpiodetect +gpiochip0 [gpio-brcmstb@107d508500] (32 lines) +gpiochip1 [gpio-brcmstb@107d508520] (4 lines) +gpiochip2 [gpio-brcmstb@107d517c00] (17 lines) +gpiochip3 [gpio-brcmstb@107d517c20] (6 lines) +gpiochip4 [pinctrl-rp1] (54 lines) +$ +$ sudo gpioinfo gpiochip4 +gpiochip4 - 54 lines: + line 0: "ID_SD" unused input active-high + line 1: "ID_SC" unused input active-high + line 2: "PIN3" unused input active-high + line 3: "PIN5" unused input active-high + line 4: "PIN7" unused input active-high + line 5: "PIN29" unused input active-high + line 6: "PIN31" unused input active-high + line 7: "PIN26" "spi0 CS1" output active-low [used] + line 8: "PIN24" "spi0 CS0" output active-low [used] + line 9: "PIN21" unused input active-high + line 10: "PIN19" unused input active-high + line 11: "PIN23" unused input active-high + line 12: "PIN32" unused input active-high + line 13: "PIN33" unused input active-high + line 14: "PIN8" "rpi-acloud-gpio-basic" output active-high [used] + line 15: "PIN10" unused input active-high + line 16: "PIN36" unused input active-high + line 17: "PIN11" unused input active-high + line 18: "PIN12" unused input active-high + line 19: "PIN35" unused input active-high + line 20: "PIN38" unused input active-high + line 21: "PIN40" unused input active-high + line 22: "PIN15" unused input active-high + line 23: "PIN16" unused input active-high + line 24: "PIN18" unused input active-high + line 25: "PIN22" unused input active-high + line 26: "PIN37" unused input active-high + line 27: "PIN13" unused input active-high + line 28: "PCIE_RP1_WAKE" unused input active-high + line 29: "FAN_TACH" unused input active-high + line 30: "HOST_SDA" unused input active-high + line 31: "HOST_SCL" unused input active-high + line 32: "ETH_RST_N" "phy-reset" output active-low [used] + line 33: "-" unused input active-high + line 34: "CD0_IO0_MICCLK" "cam0_reg" output active-high [used] + line 35: "CD0_IO0_MICDAT0" unused input active-high + line 36: "RP1_PCIE_CLKREQ_N" unused input active-high + line 37: "-" unused input active-high + line 38: "CD0_SDA" unused input active-high + line 39: "CD0_SCL" unused input active-high + line 40: "CD1_SDA" unused input active-high + line 41: "CD1_SCL" unused input active-high + line 42: "USB_VBUS_EN" unused output active-high + line 43: "USB_OC_N" unused input active-high + line 44: "RP1_STAT_LED" "PWR" output active-low [used] + line 45: "FAN_PWM" unused output active-high + line 46: "CD1_IO0_MICCLK" "cam1_reg" output active-high [used] + line 47: "2712_WAKE" unused input active-high + line 48: "CD1_IO1_MICDAT1" unused input active-high + line 49: "EN_MAX_USB_CUR" unused output active-high + line 50: "-" unused input active-high + line 51: "-" unused input active-high + line 52: "-" unused input active-high + line 53: "-" unused input active-high + +``` -Make sure that all the variables are global (chip, ledLine, buttonLine, ...). Otherwise, any timed operation will not work properly. \ No newline at end of file +Check the full documentation of libgpiod and the command line tools [here](https://github.com/brgl/libgpiod). diff --git a/js/gpio-basic/gpio-basic.js b/js/gpio-basic/gpio-basic.js index 92bb2e8..3540c84 100755 --- a/js/gpio-basic/gpio-basic.js +++ b/js/gpio-basic/gpio-basic.js @@ -1,15 +1,15 @@ const gpiod = require('node-libgpiod'); const { ArduinoIoTCloud } = require('arduino-iot-js'); +const { DEVICE_ID, SECRET_KEY } = require('./credentials'); +// Modify these lines according to your board setup +const GPIOCHIP = 'gpiochip4'; const LED = 14; // GPIO14, Pin 8 const BUTTON = 15; // GPIO15, Pin 10 -const DEVICE_ID = "09d3a634-e1ad-4927-9da0-dde663f8e5c6"; -const SECRET_KEY = "IXD3U1S37QPJOJXLZMP5"; - // Make sure these variables are global. Otherwise, they will not // work properly inside the timers -chip = new gpiod.Chip('gpiochip4'); +chip = new gpiod.Chip(GPIOCHIP); ledLine = chip.getLine(LED); buttonLine = chip.getLine(BUTTON); diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..2a8da26 --- /dev/null +++ b/js/package.json @@ -0,0 +1,18 @@ +{ + "name": "js", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "arduino-iot-js": "^0.11.0", + "jsonwebtoken": "^9.0.2", + "node-fetch": "^2.6.6", + "node-libgpiod": "^0.4.6" + } +} diff --git a/node-red/docker/README.md b/node-red/docker/README.md new file mode 100644 index 0000000..8640b2d --- /dev/null +++ b/node-red/docker/README.md @@ -0,0 +1,8 @@ +Create the folder + +sudo usermod -aG docker $USER +mkdir data +chmod a+w data + +docker-compose up -d + diff --git a/node-red/docker/data/flows.json b/node-red/docker/data/flows.json new file mode 100644 index 0000000..6c9c5b1 --- /dev/null +++ b/node-red/docker/data/flows.json @@ -0,0 +1,247 @@ +[ + { + "id": "77a698253f4e0f3b", + "type": "tab", + "label": "Flow 1", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "34c8e003d4fea847", + "type": "ioplugin", + "name": "", + "username": "", + "password": "", + "boardType": "raspi-io", + "serialportName": "", + "connectionType": "local", + "mqttServer": "", + "pubTopic": "", + "subTopic": "", + "tcpHost": "", + "tcpPort": "", + "sparkId": "", + "sparkToken": "", + "beanId": "", + "impId": "", + "uuid": "", + "token": "", + "sendUuid": "", + "samplingInterval": "500" + }, + { + "id": "fa9fb9892431c6d7", + "type": "rpi-gpio in", + "z": "77a698253f4e0f3b", + "d": true, + "name": "", + "pin": "4", + "intype": "up", + "debounce": "25", + "read": true, + "bcm": true, + "x": 150, + "y": 100, + "wires": [ + [ + "24cfd9cc1c1afa32" + ] + ] + }, + { + "id": "24cfd9cc1c1afa32", + "type": "debug", + "z": "77a698253f4e0f3b", + "name": "debug 1", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 440, + "y": 100, + "wires": [] + }, + { + "id": "36dbd4758116032e", + "type": "rpi-gpio out", + "z": "77a698253f4e0f3b", + "d": true, + "name": "", + "pin": "17", + "set": "", + "level": "0", + "freq": "", + "out": "out", + "bcm": true, + "x": 440, + "y": 180, + "wires": [] + }, + { + "id": "c867832c3d1e4d7b", + "type": "inject", + "z": "77a698253f4e0f3b", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "0", + "payloadType": "num", + "x": 150, + "y": 180, + "wires": [ + [ + "36dbd4758116032e", + "27aa95c08b8e9a65", + "b0e39184ca5c33dd" + ] + ] + }, + { + "id": "72088a8b307e6580", + "type": "inject", + "z": "77a698253f4e0f3b", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "payload": "1", + "payloadType": "num", + "x": 150, + "y": 240, + "wires": [ + [ + "36dbd4758116032e", + "27aa95c08b8e9a65", + "b0e39184ca5c33dd" + ] + ] + }, + { + "id": "75578141b3837bc3", + "type": "pi-gpiod in", + "z": "77a698253f4e0f3b", + "name": "", + "host": "localhost", + "port": 8888, + "pin": "4", + "intype": "PUD_UP", + "debounce": "25", + "read": true, + "x": 150, + "y": 320, + "wires": [ + [ + "2ed4ef3e5a6259bd" + ] + ] + }, + { + "id": "27aa95c08b8e9a65", + "type": "pi-gpiod out", + "z": "77a698253f4e0f3b", + "name": "", + "host": "localhost", + "port": 8888, + "pin": "17", + "set": "", + "level": "0", + "out": "out", + "sermin": "1000", + "sermax": "2000", + "freq": "800", + "x": 440, + "y": 240, + "wires": [] + }, + { + "id": "2ed4ef3e5a6259bd", + "type": "debug", + "z": "77a698253f4e0f3b", + "name": "debug 2", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 440, + "y": 320, + "wires": [] + }, + { + "id": "ae6b72d97a7bb25e", + "type": "gpio in", + "z": "77a698253f4e0f3b", + "name": "", + "state": "PULLUP", + "pin": "7", + "board": "34c8e003d4fea847", + "x": 150, + "y": 420, + "wires": [ + [ + "95d46a2905253ec6" + ] + ] + }, + { + "id": "b0e39184ca5c33dd", + "type": "gpio out", + "z": "77a698253f4e0f3b", + "name": "", + "state": "OUTPUT", + "pin": "11", + "i2cDelay": "0", + "i2cAddress": "", + "i2cRegister": "", + "outputs": 0, + "board": "34c8e003d4fea847", + "x": 430, + "y": 280, + "wires": [] + }, + { + "id": "95d46a2905253ec6", + "type": "debug", + "z": "77a698253f4e0f3b", + "name": "debug 3", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 440, + "y": 420, + "wires": [] + } +] \ No newline at end of file diff --git a/node-red/docker/data/package.json b/node-red/docker/data/package.json new file mode 100644 index 0000000..de1d8b3 --- /dev/null +++ b/node-red/docker/data/package.json @@ -0,0 +1,11 @@ +{ + "name": "node-red-project", + "description": "A Node-RED Project", + "version": "0.0.1", + "private": true, + "dependencies": { + "node-red-contrib-gpio": "~0.51.0", + "node-red-node-pi-gpio": "~2.0.6", + "node-red-node-pi-gpiod": "~0.4.0" + } +} diff --git a/node-red/docker/data/settings.js b/node-red/docker/data/settings.js new file mode 100644 index 0000000..80b5590 --- /dev/null +++ b/node-red/docker/data/settings.js @@ -0,0 +1,560 @@ +/** + * This is the default settings file provided by Node-RED. + * + * It can contain any valid JavaScript code that will get run when Node-RED + * is started. + * + * Lines that start with // are commented out. + * Each entry should be separated from the entries above and below by a comma ',' + * + * For more information about individual settings, refer to the documentation: + * https://nodered.org/docs/user-guide/runtime/configuration + * + * The settings are split into the following sections: + * - Flow File and User Directory Settings + * - Security + * - Server Settings + * - Runtime Settings + * - Editor Settings + * - Node Settings + * + **/ + +module.exports = { + +/******************************************************************************* + * Flow File and User Directory Settings + * - flowFile + * - credentialSecret + * - flowFilePretty + * - userDir + * - nodesDir + ******************************************************************************/ + + /** The file containing the flows. If not set, defaults to flows_.json **/ + flowFile: 'flows.json', + + /** By default, credentials are encrypted in storage using a generated key. To + * specify your own secret, set the following property. + * If you want to disable encryption of credentials, set this property to false. + * Note: once you set this property, do not change it - doing so will prevent + * node-red from being able to decrypt your existing credentials and they will be + * lost. + */ + //credentialSecret: "a-secret-key", + + /** By default, the flow JSON will be formatted over multiple lines making + * it easier to compare changes when using version control. + * To disable pretty-printing of the JSON set the following property to false. + */ + flowFilePretty: true, + + /** By default, all user data is stored in a directory called `.node-red` under + * the user's home directory. To use a different location, the following + * property can be used + */ + //userDir: '/home/nol/.node-red/', + + /** Node-RED scans the `nodes` directory in the userDir to find local node files. + * The following property can be used to specify an additional directory to scan. + */ + //nodesDir: '/home/nol/.node-red/nodes', + +/******************************************************************************* + * Security + * - adminAuth + * - https + * - httpsRefreshInterval + * - requireHttps + * - httpNodeAuth + * - httpStaticAuth + ******************************************************************************/ + + /** To password protect the Node-RED editor and admin API, the following + * property can be used. See https://nodered.org/docs/security.html for details. + */ + //adminAuth: { + // type: "credentials", + // users: [{ + // username: "admin", + // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", + // permissions: "*" + // }] + //}, + + /** The following property can be used to enable HTTPS + * This property can be either an object, containing both a (private) key + * and a (public) certificate, or a function that returns such an object. + * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener + * for details of its contents. + */ + + /** Option 1: static object */ + //https: { + // key: require("fs").readFileSync('privkey.pem'), + // cert: require("fs").readFileSync('cert.pem') + //}, + + /** Option 2: function that returns the HTTP configuration object */ + // https: function() { + // // This function should return the options object, or a Promise + // // that resolves to the options object + // return { + // key: require("fs").readFileSync('privkey.pem'), + // cert: require("fs").readFileSync('cert.pem') + // } + // }, + + /** If the `https` setting is a function, the following setting can be used + * to set how often, in hours, the function will be called. That can be used + * to refresh any certificates. + */ + //httpsRefreshInterval : 12, + + /** The following property can be used to cause insecure HTTP connections to + * be redirected to HTTPS. + */ + //requireHttps: true, + + /** To password protect the node-defined HTTP endpoints (httpNodeRoot), + * including node-red-dashboard, or the static content (httpStatic), the + * following properties can be used. + * The `pass` field is a bcrypt hash of the password. + * See https://nodered.org/docs/security.html#generating-the-password-hash + */ + //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, + //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, + +/******************************************************************************* + * Server Settings + * - uiPort + * - uiHost + * - apiMaxLength + * - httpServerOptions + * - httpAdminRoot + * - httpAdminMiddleware + * - httpNodeRoot + * - httpNodeCors + * - httpNodeMiddleware + * - httpStatic + * - httpStaticRoot + ******************************************************************************/ + + /** the tcp port that the Node-RED web server is listening on */ + uiPort: process.env.PORT || 1880, + + /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. + * To listen on all IPv6 addresses, set uiHost to "::", + * The following property can be used to listen on a specific interface. For + * example, the following would only allow connections from the local machine. + */ + //uiHost: "127.0.0.1", + + /** The maximum size of HTTP request that will be accepted by the runtime api. + * Default: 5mb + */ + //apiMaxLength: '5mb', + + /** The following property can be used to pass custom options to the Express.js + * server used by Node-RED. For a full list of available options, refer + * to http://expressjs.com/en/api.html#app.settings.table + */ + //httpServerOptions: { }, + + /** By default, the Node-RED UI is available at http://localhost:1880/ + * The following property can be used to specify a different root path. + * If set to false, this is disabled. + */ + //httpAdminRoot: '/admin', + + /** The following property can be used to add a custom middleware function + * in front of all admin http routes. For example, to set custom http + * headers. It can be a single function or an array of middleware functions. + */ + // httpAdminMiddleware: function(req,res,next) { + // // Set the X-Frame-Options header to limit where the editor + // // can be embedded + // //res.set('X-Frame-Options', 'sameorigin'); + // next(); + // }, + + + /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. + * By default, these are served relative to '/'. The following property + * can be used to specify a different root path. If set to false, this is + * disabled. + */ + //httpNodeRoot: '/red-nodes', + + /** The following property can be used to configure cross-origin resource sharing + * in the HTTP nodes. + * See https://github.com/troygoode/node-cors#configuration-options for + * details on its contents. The following is a basic permissive set of options: + */ + //httpNodeCors: { + // origin: "*", + // methods: "GET,PUT,POST,DELETE" + //}, + + /** If you need to set an http proxy please set an environment variable + * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. + * For example - http_proxy=http://myproxy.com:8080 + * (Setting it here will have no effect) + * You may also specify no_proxy (or NO_PROXY) to supply a comma separated + * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk + */ + + /** The following property can be used to add a custom middleware function + * in front of all http in nodes. This allows custom authentication to be + * applied to all http in nodes, or any other sort of common request processing. + * It can be a single function or an array of middleware functions. + */ + //httpNodeMiddleware: function(req,res,next) { + // // Handle/reject the request, or pass it on to the http in node by calling next(); + // // Optionally skip our rawBodyParser by setting this to true; + // //req.skipRawBodyParser = true; + // next(); + //}, + + /** When httpAdminRoot is used to move the UI to a different root path, the + * following property can be used to identify a directory of static content + * that should be served at http://localhost:1880/. + * When httpStaticRoot is set differently to httpAdminRoot, there is no need + * to move httpAdminRoot + */ + //httpStatic: '/home/nol/node-red-static/', //single static source + /** + * OR multiple static sources can be created using an array of objects... + * Each object can also contain an options object for further configuration. + * See https://expressjs.com/en/api.html#express.static for available options. + */ + //httpStatic: [ + // {path: '/home/nol/pics/', root: "/img/"}, + // {path: '/home/nol/reports/', root: "/doc/"}, + // {path: '/home/nol/videos/', root: "/vid/", options: {maxAge: '1d'}} + //], + + /** + * All static routes will be appended to httpStaticRoot + * e.g. if httpStatic = "/home/nol/docs" and httpStaticRoot = "/static/" + * then "/home/nol/docs" will be served at "/static/" + * e.g. if httpStatic = [{path: '/home/nol/pics/', root: "/img/"}] + * and httpStaticRoot = "/static/" + * then "/home/nol/pics/" will be served at "/static/img/" + */ + //httpStaticRoot: '/static/', + +/******************************************************************************* + * Runtime Settings + * - lang + * - runtimeState + * - diagnostics + * - logging + * - contextStorage + * - exportGlobalContextKeys + * - externalModules + ******************************************************************************/ + + /** Uncomment the following to run node-red in your preferred language. + * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko + * Some languages are more complete than others. + */ + // lang: "de", + + /** Configure diagnostics options + * - enabled: When `enabled` is `true` (or unset), diagnostics data will + * be available at http://localhost:1880/diagnostics + * - ui: When `ui` is `true` (or unset), the action `show-system-info` will + * be available to logged in users of node-red editor + */ + diagnostics: { + /** enable or disable diagnostics endpoint. Must be set to `false` to disable */ + enabled: true, + /** enable or disable diagnostics display in the node-red editor. Must be set to `false` to disable */ + ui: true, + }, + /** Configure runtimeState options + * - enabled: When `enabled` is `true` flows runtime can be Started/Stopped + * by POSTing to available at http://localhost:1880/flows/state + * - ui: When `ui` is `true`, the action `core:start-flows` and + * `core:stop-flows` will be available to logged in users of node-red editor + * Also, the deploy menu (when set to default) will show a stop or start button + */ + runtimeState: { + /** enable or disable flows/state endpoint. Must be set to `false` to disable */ + enabled: false, + /** show or hide runtime stop/start options in the node-red editor. Must be set to `false` to hide */ + ui: false, + }, + /** Configure the logging output */ + logging: { + /** Only console logging is currently supported */ + console: { + /** Level of logging to be recorded. Options are: + * fatal - only those errors which make the application unusable should be recorded + * error - record errors which are deemed fatal for a particular request + fatal errors + * warn - record problems which are non fatal + errors + fatal errors + * info - record information about the general running of the application + warn + error + fatal errors + * debug - record information which is more verbose than info + info + warn + error + fatal errors + * trace - record very detailed logging + debug + info + warn + error + fatal errors + * off - turn off all logging (doesn't affect metrics or audit) + */ + level: "info", + /** Whether or not to include metric events in the log output */ + metrics: false, + /** Whether or not to include audit events in the log output */ + audit: false + } + }, + + /** Context Storage + * The following property can be used to enable context storage. The configuration + * provided here will enable file-based context that flushes to disk every 30 seconds. + * Refer to the documentation for further options: https://nodered.org/docs/api/context/ + */ + //contextStorage: { + // default: { + // module:"localfilesystem" + // }, + //}, + + /** `global.keys()` returns a list of all properties set in global context. + * This allows them to be displayed in the Context Sidebar within the editor. + * In some circumstances it is not desirable to expose them to the editor. The + * following property can be used to hide any property set in `functionGlobalContext` + * from being list by `global.keys()`. + * By default, the property is set to false to avoid accidental exposure of + * their values. Setting this to true will cause the keys to be listed. + */ + exportGlobalContextKeys: false, + + /** Configure how the runtime will handle external npm modules. + * This covers: + * - whether the editor will allow new node modules to be installed + * - whether nodes, such as the Function node are allowed to have their + * own dynamically configured dependencies. + * The allow/denyList options can be used to limit what modules the runtime + * will install/load. It can use '*' as a wildcard that matches anything. + */ + externalModules: { + // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ + // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ + // palette: { /** Configuration for the Palette Manager */ + // allowInstall: true, /** Enable the Palette Manager in the editor */ + // allowUpdate: true, /** Allow modules to be updated in the Palette Manager */ + // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ + // allowList: ['*'], + // denyList: [], + // allowUpdateList: ['*'], + // denyUpdateList: [] + // }, + // modules: { /** Configuration for node-specified modules */ + // allowInstall: true, + // allowList: [], + // denyList: [] + // } + }, + + +/******************************************************************************* + * Editor Settings + * - disableEditor + * - editorTheme + ******************************************************************************/ + + /** The following property can be used to disable the editor. The admin API + * is not affected by this option. To disable both the editor and the admin + * API, use either the httpRoot or httpAdminRoot properties + */ + //disableEditor: false, + + /** Customising the editor + * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes + * for all available options. + */ + editorTheme: { + /** The following property can be used to set a custom theme for the editor. + * See https://github.com/node-red-contrib-themes/theme-collection for + * a collection of themes to chose from. + */ + //theme: "", + + /** To disable the 'Welcome to Node-RED' tour that is displayed the first + * time you access the editor for each release of Node-RED, set this to false + */ + //tours: false, + + palette: { + /** The following property can be used to order the categories in the editor + * palette. If a node's category is not in the list, the category will get + * added to the end of the palette. + * If not set, the following default order is used: + */ + //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], + }, + + projects: { + /** To enable the Projects feature, set this value to true */ + enabled: false, + workflow: { + /** Set the default projects workflow mode. + * - manual - you must manually commit changes + * - auto - changes are automatically committed + * This can be overridden per-user from the 'Git config' + * section of 'User Settings' within the editor + */ + mode: "manual" + } + }, + + codeEditor: { + /** Select the text editor component used by the editor. + * As of Node-RED V3, this defaults to "monaco", but can be set to "ace" if desired + */ + lib: "monaco", + options: { + /** The follow options only apply if the editor is set to "monaco" + * + * theme - must match the file name of a theme in + * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme + * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" + */ + // theme: "vs", + /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. + * for the full list, see https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html + */ + //fontSize: 14, + //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", + //fontLigatures: true, + } + }, + + markdownEditor: { + mermaid: { + /** enable or disable mermaid diagram in markdown document + */ + enabled: true + } + }, + + }, + +/******************************************************************************* + * Node Settings + * - fileWorkingDirectory + * - functionGlobalContext + * - functionExternalModules + * - functionTimeout + * - nodeMessageBufferMaxLength + * - ui (for use with Node-RED Dashboard) + * - debugUseColors + * - debugMaxLength + * - execMaxBufferSize + * - httpRequestTimeout + * - mqttReconnectTime + * - serialReconnectTime + * - socketReconnectTime + * - socketTimeout + * - tcpMsgQueueSize + * - inboundWebSocketTimeout + * - tlsConfigDisableLocalFiles + * - webSocketNodeVerifyClient + ******************************************************************************/ + + /** The working directory to handle relative file paths from within the File nodes + * defaults to the working directory of the Node-RED process. + */ + //fileWorkingDirectory: "", + + /** Allow the Function node to load additional npm modules directly */ + functionExternalModules: true, + + /** Default timeout, in seconds, for the Function node. 0 means no timeout is applied */ + functionTimeout: 0, + + /** The following property can be used to set predefined values in Global Context. + * This allows extra node modules to be made available with in Function node. + * For example, the following: + * functionGlobalContext: { os:require('os') } + * will allow the `os` module to be accessed in a Function node using: + * global.get("os") + */ + functionGlobalContext: { + // os:require('os'), + }, + + /** The maximum number of messages nodes will buffer internally as part of their + * operation. This applies across a range of nodes that operate on message sequences. + * defaults to no limit. A value of 0 also means no limit is applied. + */ + //nodeMessageBufferMaxLength: 0, + + /** If you installed the optional node-red-dashboard you can set it's path + * relative to httpNodeRoot + * Other optional properties include + * readOnly:{boolean}, + * middleware:{function or array}, (req,res,next) - http middleware + * ioMiddleware:{function or array}, (socket,next) - socket.io middleware + */ + //ui: { path: "ui" }, + + /** Colourise the console output of the debug node */ + //debugUseColors: true, + + /** The maximum length, in characters, of any message sent to the debug sidebar tab */ + debugMaxLength: 1000, + + /** Maximum buffer size for the exec node. Defaults to 10Mb */ + //execMaxBufferSize: 10000000, + + /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ + //httpRequestTimeout: 120000, + + /** Retry time in milliseconds for MQTT connections */ + mqttReconnectTime: 15000, + + /** Retry time in milliseconds for Serial port connections */ + serialReconnectTime: 15000, + + /** Retry time in milliseconds for TCP socket connections */ + //socketReconnectTime: 10000, + + /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ + //socketTimeout: 120000, + + /** Maximum number of messages to wait in queue while attempting to connect to TCP socket + * defaults to 1000 + */ + //tcpMsgQueueSize: 2000, + + /** Timeout in milliseconds for inbound WebSocket connections that do not + * match any configured node. Defaults to 5000 + */ + //inboundWebSocketTimeout: 5000, + + /** To disable the option for using local files for storing keys and + * certificates in the TLS configuration node, set this to true. + */ + //tlsConfigDisableLocalFiles: true, + + /** The following property can be used to verify WebSocket connection attempts. + * This allows, for example, the HTTP request headers to be checked to ensure + * they include valid authentication information. + */ + //webSocketNodeVerifyClient: function(info) { + // /** 'info' has three properties: + // * - origin : the value in the Origin header + // * - req : the HTTP request + // * - secure : true if req.connection.authorized or req.connection.encrypted is set + // * + // * The function should return true if the connection should be accepted, false otherwise. + // * + // * Alternatively, if this function is defined to accept a second argument, callback, + // * it can be used to verify the client asynchronously. + // * The callback takes three arguments: + // * - result : boolean, whether to accept the connection or not + // * - code : if result is false, the HTTP error status to return + // * - reason: if result is false, the HTTP reason string to return + // */ + //}, +} diff --git a/node-red/docker/docker-compose.yml b/node-red/docker/docker-compose.yml new file mode 100644 index 0000000..ae199d6 --- /dev/null +++ b/node-red/docker/docker-compose.yml @@ -0,0 +1,24 @@ +################################################################################ +# Node-RED Stack or Compose +################################################################################ +# docker stack deploy node-red --compose-file docker-compose-node-red.yml +# docker-compose -f docker-compose-node-red.yml -p myNoderedProject up +################################################################################ +version: "3.7" + +services: + node-red: + image: nodered/node-red:latest + privileged: true + restart: unless-stopped + environment: + - TZ=Europe/Amsterdam + ports: + - "11880:1880" + networks: + - node-red-net + volumes: + - ./data:/data + +networks: + node-red-net: diff --git a/node-red/gpio-basic/flows.json b/node-red/gpio-basic/flows.json index 7f774a9..f1eccbb 100644 --- a/node-red/gpio-basic/flows.json +++ b/node-red/gpio-basic/flows.json @@ -16,23 +16,24 @@ "id": "24cfd9cc1c1afa32", "type": "debug", "z": "77a698253f4e0f3b", - "name": "debug 1", + "name": "DBG: Button", "active": true, "tosidebar": true, "console": false, "tostatus": false, - "complete": "false", + "complete": "payload", + "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 680, - "y": 60, + "x": 750, + "y": 180, "wires": [] }, { "id": "c867832c3d1e4d7b", "type": "inject", "z": "77a698253f4e0f3b", - "name": "", + "name": "TEST: LED Off", "props": [ { "p": "payload" @@ -49,11 +50,10 @@ "topic": "", "payload": "0", "payloadType": "num", - "x": 150, - "y": 180, + "x": 530, + "y": 380, "wires": [ [ - "74096cf67e153bb0", "45efe7b0aee2a4a1" ] ] @@ -62,7 +62,7 @@ "id": "72088a8b307e6580", "type": "inject", "z": "77a698253f4e0f3b", - "name": "", + "name": "TEST: LED On", "props": [ { "p": "payload" @@ -79,11 +79,10 @@ "topic": "", "payload": "1", "payloadType": "num", - "x": 150, - "y": 240, + "x": 530, + "y": 420, "wires": [ [ - "74096cf67e153bb0", "45efe7b0aee2a4a1" ] ] @@ -92,46 +91,19 @@ "id": "2ed4ef3e5a6259bd", "type": "debug", "z": "77a698253f4e0f3b", - "name": "debug 2", + "name": "DBG: LED", "active": true, "tosidebar": true, "console": false, "tostatus": false, - "complete": "false", + "complete": "payload", + "targetType": "msg", "statusVal": "", "statusType": "auto", - "x": 440, + "x": 750, "y": 300, "wires": [] }, - { - "id": "63c94b31249472ac", - "type": "gpio-in", - "z": "77a698253f4e0f3b", - "name": "", - "state": "INPUT", - "device": "gpiochip4", - "pin": "4", - "x": 320, - "y": 100, - "wires": [ - [] - ] - }, - { - "id": "74096cf67e153bb0", - "type": "gpio-out", - "z": "77a698253f4e0f3b", - "name": "", - "state": "OUTPUT", - "device": "gpiochip4", - "pin": "17", - "x": 450, - "y": 180, - "wires": [ - [] - ] - }, { "id": "1732b73c9a35b80c", "type": "inject", @@ -149,7 +121,7 @@ "onceDelay": 0.1, "topic": "", "x": 150, - "y": 100, + "y": 220, "wires": [ [ "244b9558c4ea408c" @@ -168,8 +140,8 @@ "septopics": true, "property": "payload", "topi": "topic", - "x": 490, - "y": 100, + "x": 550, + "y": 220, "wires": [ [ "24cfd9cc1c1afa32", @@ -187,15 +159,112 @@ "name": "button", "propname": "button", "defaultname": true, - "sendasdevice": false, + "sendasdevice": true, + "device": "09d3a634-e1ad-4927-9da0-dde663f8e5c6", + "x": 730, + "y": 220, + "wires": [] + }, + { + "id": "244b9558c4ea408c", + "type": "gpio-in", + "z": "77a698253f4e0f3b", + "name": "", + "state": "INPUT", + "device": "gpiochip4", + "pin": "15", + "x": 350, + "y": 220, + "wires": [ + [ + "65724da039f208af" + ] + ] + }, + { + "id": "45efe7b0aee2a4a1", + "type": "gpio-out", + "z": "77a698253f4e0f3b", + "name": "", + "state": "OUTPUT", + "device": "gpiochip4", + "pin": "14", + "x": 750, + "y": 340, + "wires": [ + [] + ] + }, + { + "id": "507fac4bd8e10d79", + "type": "comment", + "z": "77a698253f4e0f3b", + "name": "Link to Arduino Cloud Dashboard", + "info": "https://app.arduino.cc/dashboards/25b90b8d-aba5-4414-9b4f-0a0f43778749", + "x": 210, + "y": 40, + "wires": [] + }, + { + "id": "5c21bfb74aed47b3", + "type": "property out", + "z": "77a698253f4e0f3b", + "connection": "75cf75b699400a70", + "thing": "74c49bf8-3844-4239-a663-76927d173cb2", + "property": "b345af0c-1c93-4b11-9e1d-4cdeec85176c", + "name": "test_value", + "propname": "test_value", + "defaultname": true, + "sendasdevice": true, "device": "09d3a634-e1ad-4927-9da0-dde663f8e5c6", - "x": 670, + "x": 740, "y": 100, "wires": [] }, { - "id": "2a01b1b47921fcfd", - "type": "property in", + "id": "64702fb2038dc742", + "type": "cpu", + "z": "77a698253f4e0f3b", + "name": "", + "msgCore": false, + "msgOverall": true, + "msgArray": false, + "msgTemp": false, + "x": 350, + "y": 100, + "wires": [ + [ + "438e4aebbe61ba5c" + ] + ] + }, + { + "id": "eb4ff1221ff6a07d", + "type": "inject", + "z": "77a698253f4e0f3b", + "name": "", + "props": [ + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "5", + "crontab": "", + "once": false, + "onceDelay": 0.1, + "topic": "", + "x": 150, + "y": 100, + "wires": [ + [ + "64702fb2038dc742" + ] + ] + }, + { + "id": "fe7856e69f6d61b2", + "type": "property in poll", "z": "77a698253f4e0f3b", "connection": "75cf75b699400a70", "thing": "74c49bf8-3844-4239-a663-76927d173cb2", @@ -203,44 +272,62 @@ "name": "led", "propname": "led", "defaultname": true, - "variableName": "led", - "x": 130, - "y": 300, + "timeWindowCount": "5", + "timeWindowUnit": "1", + "x": 150, + "y": 340, "wires": [ [ - "2ed4ef3e5a6259bd", - "45efe7b0aee2a4a1" + "1db3872dd8744c41" ] ] }, { - "id": "244b9558c4ea408c", - "type": "gpio-in", + "id": "438e4aebbe61ba5c", + "type": "change", "z": "77a698253f4e0f3b", - "name": "", - "state": "INPUT", - "device": "gpiochip4", - "pin": "15", - "x": 330, - "y": 60, + "name": "x 100", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "payload * 100", + "tot": "jsonata" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 550, + "y": 100, "wires": [ [ - "65724da039f208af" + "5c21bfb74aed47b3" ] ] }, { - "id": "45efe7b0aee2a4a1", - "type": "gpio-out", + "id": "1db3872dd8744c41", + "type": "rbe", "z": "77a698253f4e0f3b", "name": "", - "state": "OUTPUT", - "device": "gpiochip4", - "pin": "14", - "x": 450, - "y": 240, + "func": "rbe", + "gap": "", + "start": "", + "inout": "out", + "septopics": true, + "property": "payload", + "topi": "topic", + "x": 550, + "y": 340, "wires": [ - [] + [ + "2ed4ef3e5a6259bd", + "45efe7b0aee2a4a1" + ] ] } ] \ No newline at end of file diff --git a/node-red/gpio-basic/flows_cred.json b/node-red/gpio-basic/flows_cred.json new file mode 100644 index 0000000..047ed93 --- /dev/null +++ b/node-red/gpio-basic/flows_cred.json @@ -0,0 +1,3 @@ +{ + "$": "05ed1bc1815786b48ff52abb8881cbd7rmc4zMMJhRiijCeP6NaoVminSq8B4PHtzk6j6EH7knMWKmNa26AMmbqhAhj3PcSODySzHJq0UdiK4Tw2nFKXhq2pFiEs3aCWhCY6RB6kHGM6hY/VuZedLIId8253Q9kOT67GtoVuFGnqyTadyPqkvs7wUFh7XRVE2pLfJsxZYU5ozxxwx2xtpyJcVQx3DtASl467gTT9" +} \ No newline at end of file diff --git a/node-red/gpio-basic/package.json b/node-red/gpio-basic/package.json new file mode 100644 index 0000000..35eb7de --- /dev/null +++ b/node-red/gpio-basic/package.json @@ -0,0 +1,11 @@ +{ + "name": "node-red-project", + "description": "A Node-RED Project", + "version": "0.0.1", + "private": true, + "dependencies": { + "@arduino/node-red-contrib-arduino-iot-cloud": "~1.0.10", + "node-red-contrib-cpu": "~0.0.4", + "node-red-contrib-libgpiod": "~0.0.1" + } +} diff --git a/node-red/gpio-basic/pm2.json b/node-red/gpio-basic/pm2.json new file mode 100644 index 0000000..726dd39 --- /dev/null +++ b/node-red/gpio-basic/pm2.json @@ -0,0 +1,13 @@ +{ + "apps": [ + { + "name": "node-red", + "script": "node-red", + // Change the directory to your own one + "args": ["--userDir", "/home/dbeamon/projects/python/arduino-cloud/rpi-arduino-cloud/node-red/gpio-basic"], + "node_args": ["--max-old-space-size=128"], + // Other configurations... + } + ] +} + diff --git a/node-red/gpio-basic/settings.js b/node-red/gpio-basic/settings.js new file mode 100644 index 0000000..80b5590 --- /dev/null +++ b/node-red/gpio-basic/settings.js @@ -0,0 +1,560 @@ +/** + * This is the default settings file provided by Node-RED. + * + * It can contain any valid JavaScript code that will get run when Node-RED + * is started. + * + * Lines that start with // are commented out. + * Each entry should be separated from the entries above and below by a comma ',' + * + * For more information about individual settings, refer to the documentation: + * https://nodered.org/docs/user-guide/runtime/configuration + * + * The settings are split into the following sections: + * - Flow File and User Directory Settings + * - Security + * - Server Settings + * - Runtime Settings + * - Editor Settings + * - Node Settings + * + **/ + +module.exports = { + +/******************************************************************************* + * Flow File and User Directory Settings + * - flowFile + * - credentialSecret + * - flowFilePretty + * - userDir + * - nodesDir + ******************************************************************************/ + + /** The file containing the flows. If not set, defaults to flows_.json **/ + flowFile: 'flows.json', + + /** By default, credentials are encrypted in storage using a generated key. To + * specify your own secret, set the following property. + * If you want to disable encryption of credentials, set this property to false. + * Note: once you set this property, do not change it - doing so will prevent + * node-red from being able to decrypt your existing credentials and they will be + * lost. + */ + //credentialSecret: "a-secret-key", + + /** By default, the flow JSON will be formatted over multiple lines making + * it easier to compare changes when using version control. + * To disable pretty-printing of the JSON set the following property to false. + */ + flowFilePretty: true, + + /** By default, all user data is stored in a directory called `.node-red` under + * the user's home directory. To use a different location, the following + * property can be used + */ + //userDir: '/home/nol/.node-red/', + + /** Node-RED scans the `nodes` directory in the userDir to find local node files. + * The following property can be used to specify an additional directory to scan. + */ + //nodesDir: '/home/nol/.node-red/nodes', + +/******************************************************************************* + * Security + * - adminAuth + * - https + * - httpsRefreshInterval + * - requireHttps + * - httpNodeAuth + * - httpStaticAuth + ******************************************************************************/ + + /** To password protect the Node-RED editor and admin API, the following + * property can be used. See https://nodered.org/docs/security.html for details. + */ + //adminAuth: { + // type: "credentials", + // users: [{ + // username: "admin", + // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", + // permissions: "*" + // }] + //}, + + /** The following property can be used to enable HTTPS + * This property can be either an object, containing both a (private) key + * and a (public) certificate, or a function that returns such an object. + * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener + * for details of its contents. + */ + + /** Option 1: static object */ + //https: { + // key: require("fs").readFileSync('privkey.pem'), + // cert: require("fs").readFileSync('cert.pem') + //}, + + /** Option 2: function that returns the HTTP configuration object */ + // https: function() { + // // This function should return the options object, or a Promise + // // that resolves to the options object + // return { + // key: require("fs").readFileSync('privkey.pem'), + // cert: require("fs").readFileSync('cert.pem') + // } + // }, + + /** If the `https` setting is a function, the following setting can be used + * to set how often, in hours, the function will be called. That can be used + * to refresh any certificates. + */ + //httpsRefreshInterval : 12, + + /** The following property can be used to cause insecure HTTP connections to + * be redirected to HTTPS. + */ + //requireHttps: true, + + /** To password protect the node-defined HTTP endpoints (httpNodeRoot), + * including node-red-dashboard, or the static content (httpStatic), the + * following properties can be used. + * The `pass` field is a bcrypt hash of the password. + * See https://nodered.org/docs/security.html#generating-the-password-hash + */ + //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, + //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, + +/******************************************************************************* + * Server Settings + * - uiPort + * - uiHost + * - apiMaxLength + * - httpServerOptions + * - httpAdminRoot + * - httpAdminMiddleware + * - httpNodeRoot + * - httpNodeCors + * - httpNodeMiddleware + * - httpStatic + * - httpStaticRoot + ******************************************************************************/ + + /** the tcp port that the Node-RED web server is listening on */ + uiPort: process.env.PORT || 1880, + + /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. + * To listen on all IPv6 addresses, set uiHost to "::", + * The following property can be used to listen on a specific interface. For + * example, the following would only allow connections from the local machine. + */ + //uiHost: "127.0.0.1", + + /** The maximum size of HTTP request that will be accepted by the runtime api. + * Default: 5mb + */ + //apiMaxLength: '5mb', + + /** The following property can be used to pass custom options to the Express.js + * server used by Node-RED. For a full list of available options, refer + * to http://expressjs.com/en/api.html#app.settings.table + */ + //httpServerOptions: { }, + + /** By default, the Node-RED UI is available at http://localhost:1880/ + * The following property can be used to specify a different root path. + * If set to false, this is disabled. + */ + //httpAdminRoot: '/admin', + + /** The following property can be used to add a custom middleware function + * in front of all admin http routes. For example, to set custom http + * headers. It can be a single function or an array of middleware functions. + */ + // httpAdminMiddleware: function(req,res,next) { + // // Set the X-Frame-Options header to limit where the editor + // // can be embedded + // //res.set('X-Frame-Options', 'sameorigin'); + // next(); + // }, + + + /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. + * By default, these are served relative to '/'. The following property + * can be used to specify a different root path. If set to false, this is + * disabled. + */ + //httpNodeRoot: '/red-nodes', + + /** The following property can be used to configure cross-origin resource sharing + * in the HTTP nodes. + * See https://github.com/troygoode/node-cors#configuration-options for + * details on its contents. The following is a basic permissive set of options: + */ + //httpNodeCors: { + // origin: "*", + // methods: "GET,PUT,POST,DELETE" + //}, + + /** If you need to set an http proxy please set an environment variable + * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. + * For example - http_proxy=http://myproxy.com:8080 + * (Setting it here will have no effect) + * You may also specify no_proxy (or NO_PROXY) to supply a comma separated + * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk + */ + + /** The following property can be used to add a custom middleware function + * in front of all http in nodes. This allows custom authentication to be + * applied to all http in nodes, or any other sort of common request processing. + * It can be a single function or an array of middleware functions. + */ + //httpNodeMiddleware: function(req,res,next) { + // // Handle/reject the request, or pass it on to the http in node by calling next(); + // // Optionally skip our rawBodyParser by setting this to true; + // //req.skipRawBodyParser = true; + // next(); + //}, + + /** When httpAdminRoot is used to move the UI to a different root path, the + * following property can be used to identify a directory of static content + * that should be served at http://localhost:1880/. + * When httpStaticRoot is set differently to httpAdminRoot, there is no need + * to move httpAdminRoot + */ + //httpStatic: '/home/nol/node-red-static/', //single static source + /** + * OR multiple static sources can be created using an array of objects... + * Each object can also contain an options object for further configuration. + * See https://expressjs.com/en/api.html#express.static for available options. + */ + //httpStatic: [ + // {path: '/home/nol/pics/', root: "/img/"}, + // {path: '/home/nol/reports/', root: "/doc/"}, + // {path: '/home/nol/videos/', root: "/vid/", options: {maxAge: '1d'}} + //], + + /** + * All static routes will be appended to httpStaticRoot + * e.g. if httpStatic = "/home/nol/docs" and httpStaticRoot = "/static/" + * then "/home/nol/docs" will be served at "/static/" + * e.g. if httpStatic = [{path: '/home/nol/pics/', root: "/img/"}] + * and httpStaticRoot = "/static/" + * then "/home/nol/pics/" will be served at "/static/img/" + */ + //httpStaticRoot: '/static/', + +/******************************************************************************* + * Runtime Settings + * - lang + * - runtimeState + * - diagnostics + * - logging + * - contextStorage + * - exportGlobalContextKeys + * - externalModules + ******************************************************************************/ + + /** Uncomment the following to run node-red in your preferred language. + * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko + * Some languages are more complete than others. + */ + // lang: "de", + + /** Configure diagnostics options + * - enabled: When `enabled` is `true` (or unset), diagnostics data will + * be available at http://localhost:1880/diagnostics + * - ui: When `ui` is `true` (or unset), the action `show-system-info` will + * be available to logged in users of node-red editor + */ + diagnostics: { + /** enable or disable diagnostics endpoint. Must be set to `false` to disable */ + enabled: true, + /** enable or disable diagnostics display in the node-red editor. Must be set to `false` to disable */ + ui: true, + }, + /** Configure runtimeState options + * - enabled: When `enabled` is `true` flows runtime can be Started/Stopped + * by POSTing to available at http://localhost:1880/flows/state + * - ui: When `ui` is `true`, the action `core:start-flows` and + * `core:stop-flows` will be available to logged in users of node-red editor + * Also, the deploy menu (when set to default) will show a stop or start button + */ + runtimeState: { + /** enable or disable flows/state endpoint. Must be set to `false` to disable */ + enabled: false, + /** show or hide runtime stop/start options in the node-red editor. Must be set to `false` to hide */ + ui: false, + }, + /** Configure the logging output */ + logging: { + /** Only console logging is currently supported */ + console: { + /** Level of logging to be recorded. Options are: + * fatal - only those errors which make the application unusable should be recorded + * error - record errors which are deemed fatal for a particular request + fatal errors + * warn - record problems which are non fatal + errors + fatal errors + * info - record information about the general running of the application + warn + error + fatal errors + * debug - record information which is more verbose than info + info + warn + error + fatal errors + * trace - record very detailed logging + debug + info + warn + error + fatal errors + * off - turn off all logging (doesn't affect metrics or audit) + */ + level: "info", + /** Whether or not to include metric events in the log output */ + metrics: false, + /** Whether or not to include audit events in the log output */ + audit: false + } + }, + + /** Context Storage + * The following property can be used to enable context storage. The configuration + * provided here will enable file-based context that flushes to disk every 30 seconds. + * Refer to the documentation for further options: https://nodered.org/docs/api/context/ + */ + //contextStorage: { + // default: { + // module:"localfilesystem" + // }, + //}, + + /** `global.keys()` returns a list of all properties set in global context. + * This allows them to be displayed in the Context Sidebar within the editor. + * In some circumstances it is not desirable to expose them to the editor. The + * following property can be used to hide any property set in `functionGlobalContext` + * from being list by `global.keys()`. + * By default, the property is set to false to avoid accidental exposure of + * their values. Setting this to true will cause the keys to be listed. + */ + exportGlobalContextKeys: false, + + /** Configure how the runtime will handle external npm modules. + * This covers: + * - whether the editor will allow new node modules to be installed + * - whether nodes, such as the Function node are allowed to have their + * own dynamically configured dependencies. + * The allow/denyList options can be used to limit what modules the runtime + * will install/load. It can use '*' as a wildcard that matches anything. + */ + externalModules: { + // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ + // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ + // palette: { /** Configuration for the Palette Manager */ + // allowInstall: true, /** Enable the Palette Manager in the editor */ + // allowUpdate: true, /** Allow modules to be updated in the Palette Manager */ + // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ + // allowList: ['*'], + // denyList: [], + // allowUpdateList: ['*'], + // denyUpdateList: [] + // }, + // modules: { /** Configuration for node-specified modules */ + // allowInstall: true, + // allowList: [], + // denyList: [] + // } + }, + + +/******************************************************************************* + * Editor Settings + * - disableEditor + * - editorTheme + ******************************************************************************/ + + /** The following property can be used to disable the editor. The admin API + * is not affected by this option. To disable both the editor and the admin + * API, use either the httpRoot or httpAdminRoot properties + */ + //disableEditor: false, + + /** Customising the editor + * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes + * for all available options. + */ + editorTheme: { + /** The following property can be used to set a custom theme for the editor. + * See https://github.com/node-red-contrib-themes/theme-collection for + * a collection of themes to chose from. + */ + //theme: "", + + /** To disable the 'Welcome to Node-RED' tour that is displayed the first + * time you access the editor for each release of Node-RED, set this to false + */ + //tours: false, + + palette: { + /** The following property can be used to order the categories in the editor + * palette. If a node's category is not in the list, the category will get + * added to the end of the palette. + * If not set, the following default order is used: + */ + //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], + }, + + projects: { + /** To enable the Projects feature, set this value to true */ + enabled: false, + workflow: { + /** Set the default projects workflow mode. + * - manual - you must manually commit changes + * - auto - changes are automatically committed + * This can be overridden per-user from the 'Git config' + * section of 'User Settings' within the editor + */ + mode: "manual" + } + }, + + codeEditor: { + /** Select the text editor component used by the editor. + * As of Node-RED V3, this defaults to "monaco", but can be set to "ace" if desired + */ + lib: "monaco", + options: { + /** The follow options only apply if the editor is set to "monaco" + * + * theme - must match the file name of a theme in + * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme + * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" + */ + // theme: "vs", + /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. + * for the full list, see https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html + */ + //fontSize: 14, + //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", + //fontLigatures: true, + } + }, + + markdownEditor: { + mermaid: { + /** enable or disable mermaid diagram in markdown document + */ + enabled: true + } + }, + + }, + +/******************************************************************************* + * Node Settings + * - fileWorkingDirectory + * - functionGlobalContext + * - functionExternalModules + * - functionTimeout + * - nodeMessageBufferMaxLength + * - ui (for use with Node-RED Dashboard) + * - debugUseColors + * - debugMaxLength + * - execMaxBufferSize + * - httpRequestTimeout + * - mqttReconnectTime + * - serialReconnectTime + * - socketReconnectTime + * - socketTimeout + * - tcpMsgQueueSize + * - inboundWebSocketTimeout + * - tlsConfigDisableLocalFiles + * - webSocketNodeVerifyClient + ******************************************************************************/ + + /** The working directory to handle relative file paths from within the File nodes + * defaults to the working directory of the Node-RED process. + */ + //fileWorkingDirectory: "", + + /** Allow the Function node to load additional npm modules directly */ + functionExternalModules: true, + + /** Default timeout, in seconds, for the Function node. 0 means no timeout is applied */ + functionTimeout: 0, + + /** The following property can be used to set predefined values in Global Context. + * This allows extra node modules to be made available with in Function node. + * For example, the following: + * functionGlobalContext: { os:require('os') } + * will allow the `os` module to be accessed in a Function node using: + * global.get("os") + */ + functionGlobalContext: { + // os:require('os'), + }, + + /** The maximum number of messages nodes will buffer internally as part of their + * operation. This applies across a range of nodes that operate on message sequences. + * defaults to no limit. A value of 0 also means no limit is applied. + */ + //nodeMessageBufferMaxLength: 0, + + /** If you installed the optional node-red-dashboard you can set it's path + * relative to httpNodeRoot + * Other optional properties include + * readOnly:{boolean}, + * middleware:{function or array}, (req,res,next) - http middleware + * ioMiddleware:{function or array}, (socket,next) - socket.io middleware + */ + //ui: { path: "ui" }, + + /** Colourise the console output of the debug node */ + //debugUseColors: true, + + /** The maximum length, in characters, of any message sent to the debug sidebar tab */ + debugMaxLength: 1000, + + /** Maximum buffer size for the exec node. Defaults to 10Mb */ + //execMaxBufferSize: 10000000, + + /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ + //httpRequestTimeout: 120000, + + /** Retry time in milliseconds for MQTT connections */ + mqttReconnectTime: 15000, + + /** Retry time in milliseconds for Serial port connections */ + serialReconnectTime: 15000, + + /** Retry time in milliseconds for TCP socket connections */ + //socketReconnectTime: 10000, + + /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ + //socketTimeout: 120000, + + /** Maximum number of messages to wait in queue while attempting to connect to TCP socket + * defaults to 1000 + */ + //tcpMsgQueueSize: 2000, + + /** Timeout in milliseconds for inbound WebSocket connections that do not + * match any configured node. Defaults to 5000 + */ + //inboundWebSocketTimeout: 5000, + + /** To disable the option for using local files for storing keys and + * certificates in the TLS configuration node, set this to true. + */ + //tlsConfigDisableLocalFiles: true, + + /** The following property can be used to verify WebSocket connection attempts. + * This allows, for example, the HTTP request headers to be checked to ensure + * they include valid authentication information. + */ + //webSocketNodeVerifyClient: function(info) { + // /** 'info' has three properties: + // * - origin : the value in the Origin header + // * - req : the HTTP request + // * - secure : true if req.connection.authorized or req.connection.encrypted is set + // * + // * The function should return true if the connection should be accepted, false otherwise. + // * + // * Alternatively, if this function is defined to accept a second argument, callback, + // * it can be used to verify the client asynchronously. + // * The callback takes three arguments: + // * - result : boolean, whether to accept the connection or not + // * - code : if result is false, the HTTP error status to return + // * - reason: if result is false, the HTTP reason string to return + // */ + //}, +}