├── .gitignore ├── CHANGELOG.md ├── README.md ├── assets ├── screenshotQuery.png └── screenshotWrite.png ├── package.json └── src ├── main.html ├── main.js ├── query.html ├── query.js ├── write.html └── write.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules/ 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.0.4 (2021-01-25) 4 | 5 | - 🌟 Update influxdb-v2 to 1.0.0, escaping special characters in measurement, tags and fields (see https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/#special-characters) 6 | 7 | 8 | ## 1.0.3 (2020-10-12) 9 | 10 | - 🌟 Add support of strings in fields values 11 | 12 | 13 | ## 1.0.2 (2020-05-12) 14 | 15 | - 👩‍⚖️ Add license informations 16 | 17 | 18 | ## 1.0.1 (2020-05-11) 19 | 20 | - Bump to resolve a problem with NPM 21 | 22 | 23 | ## 1.0.0 (2020-05-11) 24 | 25 | - 🎉 First version -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-red-contrib-stackhero-influxdb-v2 2 | 3 | [Node-RED](https://nodered.org) node to read and write to an InfluxDB v2 database. 4 | 5 | **Remember: if you like it, please star it! 🥰** 6 | 7 | Official repository: [https://github.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2](https://github.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2) 8 | 9 | 10 | ## Sponsors 11 | 12 | `node-red-contrib-stackhero-influxdb-v2` is developed by [Stackhero](https://www.stackhero.io/). 13 | If you are looking for powerful managed services, like InfluxDB, you should seriously consider Stackhero 🤓 14 | 15 | 16 | ## Usage 17 | 18 | We have 2 nodes here. A `write` node, to send data to InfluxDB and a `query` node, to retrieve data using the Flux language. 19 | 20 | 21 | ### Write data 22 | 23 | The write node requires a payload object like this one: 24 | ```javascript 25 | msg.payload = { 26 | // You bucket 27 | // Optional (it can be defined in the node credentials settings) 28 | bucket: 'myBucket', 29 | 30 | // Precision of timestamp 31 | // Optional 32 | // Can be `ns` (nanoseconds), 33 | // `us` (microseconds), 34 | // `ms` (milliseconds), 35 | // `s` (seconds). 36 | // The default is `ns` 37 | // Note: if you set the `timestamp` field to `Date.now()`, you have to set the `precision` to `ms` 38 | precision: 'ms', 39 | 40 | // Data to send to InfluxDB 41 | // Can be an array of objects or only one object 42 | data: [ 43 | { 44 | measurement: 'machinerySensor', 45 | 46 | tags: { 47 | deviceId: 'gyh43', 48 | hardwareVersion: '1.0.2', 49 | softwareVersion: '2.5.1', 50 | location: 'factory-1' 51 | }, 52 | 53 | fields: { 54 | temperature: 12, 55 | humidity: 46, 56 | vibrations: 18, 57 | batteryVoltage: 3.6 58 | }, 59 | 60 | timestamp: Date.now() 61 | }, 62 | 63 | // More data can be send here, simply re add an object 64 | // { ... }, 65 | ] 66 | }; 67 | 68 | return msg; 69 | ``` 70 | 71 | ![Example of the write node](https://raw.githubusercontent.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2/master/assets/screenshotWrite.png) 72 | 73 | 74 | ### Query data 75 | 76 | The query node requires a topic string containing a Flux query. 77 | ```javascript 78 | msg.topic = 'from(bucket: "myBucket") |> range(start: -1h)'; 79 | return msg; 80 | ``` 81 | 82 | You can write multiple lines queries like this: 83 | ```javascript 84 | msg.topic = [ 85 | 'from(bucket: "myBucket")', 86 | ' |> range(start: -1d, stop: now)', 87 | ' |> filter(fn: (r) => r._measurement == "machinerySensor")', 88 | ' |> filter(fn: (r) => r._field == "vibrations")', 89 | ' |> aggregateWindow(every: 1h, fn: mean)', 90 | ' |> yield(name: "mean")', 91 | 92 | 'from(bucket: "myBucket")', 93 | ' |> range(start: -1d, stop: now)', 94 | ' |> filter(fn: (r) => r._measurement == "machinerySensor")', 95 | ' |> filter(fn: (r) => r._field == "vibrations")', 96 | ' |> aggregateWindow(every: 1h, fn: max)', 97 | ' |> yield(name: "max")', 98 | ].join('\n'); 99 | 100 | return msg; 101 | ``` 102 | 103 | ![Example of the query node](https://raw.githubusercontent.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2/master/assets/screenshotQuery.png) 104 | 105 | -------------------------------------------------------------------------------- /assets/screenshotQuery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2/2b73d529c6b5fd6ddece3c85cbef752de15ac211/assets/screenshotQuery.png -------------------------------------------------------------------------------- /assets/screenshotWrite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2/2b73d529c6b5fd6ddece3c85cbef752de15ac211/assets/screenshotWrite.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-stackhero-influxdb-v2", 3 | "version": "1.0.4", 4 | "description": "A Node-RED node to connect to an InfluxDB v2 database.", 5 | "dependencies": { 6 | "influxdb-v2": "1.0.0" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/stackhero-io/node-red-contrib-stackhero-influxdb-v2" 11 | }, 12 | "keywords": [ 13 | "node-red", 14 | "stackhero", 15 | "influxdb", 16 | "influxdb-v2" 17 | ], 18 | "node-red": { 19 | "nodes": { 20 | "stackhero-influxdb-v2-main": "src/main.js", 21 | "stackhero-influxdb-v2-query": "src/query.js", 22 | "stackhero-influxdb-v2-write": "src/write.js" 23 | } 24 | }, 25 | "author": { 26 | "name": "Stackhero", 27 | "url": "https://www.stackhero.io/" 28 | }, 29 | "license" : "AGPL-3.0" 30 | } 31 | -------------------------------------------------------------------------------- /src/main.html: -------------------------------------------------------------------------------- 1 | 32 | 33 | 73 | 74 | 75 | 81 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | module.exports = (RED) => { 2 | 'use strict'; 3 | 4 | const Influxdb = require('influxdb-v2'); 5 | 6 | function InfluxDbV2Node(config) { 7 | RED.nodes.createNode(this, config); 8 | 9 | this.connection = async () => { 10 | this.influxdb = new Influxdb({ 11 | host: config.host, 12 | port: config.port, 13 | protocol: config.tls ? 'https' : 'http', 14 | token: this.credentials.token 15 | }); 16 | }; 17 | 18 | this.query = async query => { 19 | await this.connection(); 20 | return await this.influxdb.query( 21 | { org: this.credentials.organization }, 22 | { query } 23 | ); 24 | }; 25 | 26 | this.write = async ({ bucket = this.credentials.bucket, precision = 'ns', data }) => { 27 | await this.connection(); 28 | return await this.influxdb.write( 29 | { 30 | org: this.credentials.organization, 31 | bucket, 32 | precision 33 | }, 34 | data 35 | ); 36 | }; 37 | 38 | this.on('close', async done => { 39 | if (this.stateTimeout) { 40 | clearTimeout(this.stateTimeout); 41 | } 42 | done(); 43 | }); 44 | } 45 | RED.nodes.registerType( 46 | 'Stackhero-InfluxDB-v2-Server', 47 | InfluxDbV2Node, 48 | { 49 | credentials: { 50 | token: { type: 'text' }, 51 | organization: { type: 'text' }, 52 | bucket: { type: 'text' } 53 | } 54 | } 55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /src/query.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 74 | 75 | -------------------------------------------------------------------------------- /src/query.js: -------------------------------------------------------------------------------- 1 | module.exports = (RED) => { 2 | 'use strict'; 3 | 4 | function InfluxDbV2NodeQuery(config) { 5 | RED.nodes.createNode(this, config); 6 | this.serverConfig = RED.nodes.getNode(config.server); 7 | 8 | if (!this.serverConfig) { 9 | this.error('InfluxDB database not configured'); 10 | return; 11 | } 12 | 13 | this.setState = (code, info) => { 14 | if (code === 'doing') { 15 | this.status({ fill: 'grey', shape: 'ring', text: 'doing...' }); 16 | } 17 | else if (code === 'error') { 18 | this.status({ fill: 'red', shape: 'ring', text: info }); 19 | } 20 | else if (code === 'done') { 21 | this.status({ fill: 'blue', shape: 'dot', text: 'done' }); 22 | } 23 | else { 24 | this.status({}); 25 | } 26 | }; 27 | 28 | this.on('input', async msg => { 29 | if (!msg.topic) { 30 | this.error('msg.topic should be a string containing the Flux query.'); 31 | return; 32 | } 33 | 34 | 35 | clearTimeout(this.stateTimeout); 36 | try { 37 | this.setState('doing'); 38 | const result = await this.serverConfig 39 | .query(msg.topic); 40 | msg.payload = result; 41 | this.send(msg); 42 | this.setState('done'); 43 | this.stateTimeout = setTimeout( 44 | () => this.setState(), 45 | 2 * 1000 46 | ); 47 | } 48 | catch (error) { 49 | this.error(error, msg); 50 | this.setState('error', error.toString()); 51 | } 52 | }); 53 | 54 | this.on('close', async () => { 55 | clearTimeout(this.stateTimeout); 56 | this.serverConfig.removeAllListeners(); 57 | this.setState(); 58 | }); 59 | } 60 | RED.nodes.registerType('Stackhero-InfluxDB-v2-query', InfluxDbV2NodeQuery); 61 | }; 62 | -------------------------------------------------------------------------------- /src/write.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 92 | 93 | -------------------------------------------------------------------------------- /src/write.js: -------------------------------------------------------------------------------- 1 | module.exports = (RED) => { 2 | 'use strict'; 3 | 4 | function InfluxDbV2NodeWrite(config) { 5 | RED.nodes.createNode(this, config); 6 | this.serverConfig = RED.nodes.getNode(config.server); 7 | 8 | if (!this.serverConfig) { 9 | this.error('InfluxDB database not configured'); 10 | return; 11 | } 12 | 13 | this.setState = (code, info) => { 14 | if (code === 'doing') { 15 | this.status({ fill: 'grey', shape: 'ring', text: 'doing...' }); 16 | } 17 | else if (code === 'error') { 18 | this.status({ fill: 'red', shape: 'ring', text: info }); 19 | } 20 | else if (code === 'done') { 21 | this.status({ fill: 'blue', shape: 'dot', text: 'done' }); 22 | } 23 | else { 24 | this.status({}); 25 | } 26 | }; 27 | 28 | this.serverConfig.on('stateWrite', (code, info) => this.setState(code, info)); 29 | 30 | this.on('input', async msg => { 31 | if (!msg.payload) { 32 | this.error('msg.payload should be an object containing the data argument, as an object or an array.'); 33 | return; 34 | } 35 | 36 | if (!Array.isArray(msg.payload.data)) { 37 | msg.payload.data = [ msg.payload.data ]; 38 | } 39 | 40 | clearTimeout(this.stateTimeout); 41 | try { 42 | this.setState('doing'); 43 | const { bucket, precision, data } = msg.payload; 44 | const result = await this.serverConfig 45 | .write({ bucket, precision, data }); 46 | msg.payload = result; 47 | this.send(msg); 48 | 49 | this.setState('done'); 50 | this.stateTimeout = setTimeout( 51 | () => this.setState(), 52 | 2 * 1000 53 | ); 54 | } 55 | catch (error) { 56 | this.error(error, msg); 57 | this.setState('error', error.toString()); 58 | } 59 | }); 60 | 61 | this.on('close', async () => { 62 | clearTimeout(this.stateTimeout); 63 | this.serverConfig.removeAllListeners(); 64 | this.setState(); 65 | }); 66 | } 67 | RED.nodes.registerType('Stackhero-InfluxDB-v2-write', InfluxDbV2NodeWrite); 68 | }; 69 | --------------------------------------------------------------------------------