node-red-contrib-influxdb3 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,339 @@
1
+ # node-red-contrib-influxdb3
2
+
3
+ [![npm version](https://badge.fury.io/js/node-red-contrib-influxdb3.svg)](https://www.npmjs.com/package/node-red-contrib-influxdb3)
4
+ [![npm downloads](https://img.shields.io/npm/dm/node-red-contrib-influxdb3.svg)](https://www.npmjs.com/package/node-red-contrib-influxdb3)
5
+ [![Node-RED](https://img.shields.io/badge/Node--RED-contrib-red.svg)](https://flows.nodered.org/node/node-red-contrib-influxdb3)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ Node-RED nodes for writing data to InfluxDB v3.
9
+
10
+ This package provides Node-RED integration with InfluxDB v3 using the official [@influxdata/influxdb3-client](https://github.com/InfluxCommunity/influxdb3-js) JavaScript library.
11
+
12
+ ## Installation
13
+
14
+ ### From npm (when published)
15
+
16
+ ```bash
17
+ cd ~/.node-red
18
+ npm install node-red-contrib-influxdb3
19
+ ```
20
+
21
+ ### From local directory
22
+
23
+ ```bash
24
+ cd ~/.node-red
25
+ npm install /path/to/node-red-contrib-influxdb3
26
+ ```
27
+
28
+ ### Development Installation
29
+
30
+ 1. Clone this repository
31
+ 2. Install dependencies:
32
+ ```bash
33
+ npm install
34
+ ```
35
+ 3. Link to your Node-RED installation:
36
+ ```bash
37
+ cd ~/.node-red
38
+ npm install /path/to/node-red-contrib-influxdb3
39
+ ```
40
+ 4. Restart Node-RED
41
+
42
+ ## Nodes
43
+
44
+ This package includes two nodes:
45
+
46
+ ### InfluxDB v3 Config Node
47
+
48
+ A configuration node that stores connection details for your InfluxDB v3 instance.
49
+
50
+ **Configuration:**
51
+ - **Name**: A friendly name for the connection
52
+ - **Host**: Your InfluxDB v3 host URL (e.g., `https://us-east-1-1.aws.cloud2.influxdata.com`)
53
+ - **Token**: Your InfluxDB v3 authentication token
54
+ - **Database**: The default database (bucket) name
55
+
56
+ ### InfluxDB v3 Write Node
57
+
58
+ Writes data points to InfluxDB v3.
59
+
60
+ **Configuration:**
61
+ - **Connection**: Select an InfluxDB v3 config node
62
+ - **Name**: Optional node name
63
+ - **Measurement**: Default measurement name (can be overridden by `msg.measurement`)
64
+ - **Database**: Optional database override (uses connection default if not set)
65
+
66
+ ## Usage
67
+
68
+ ### Input Message Formats
69
+
70
+ The write node accepts data in multiple formats:
71
+
72
+ #### 1. Line Protocol String
73
+
74
+ Send data as a string in InfluxDB line protocol format:
75
+
76
+ ```javascript
77
+ msg.payload = "temperature,location=room1 value=21.5";
78
+ return msg;
79
+ ```
80
+
81
+ #### 2. Object with Fields and Tags
82
+
83
+ Send an object with explicit `fields` and `tags` properties:
84
+
85
+ ```javascript
86
+ msg.measurement = "temperature";
87
+ msg.payload = {
88
+ fields: {
89
+ value: 21.5,
90
+ humidity: 65
91
+ },
92
+ tags: {
93
+ location: "room1",
94
+ sensor: "dht22"
95
+ },
96
+ timestamp: Date.now() // optional
97
+ };
98
+ return msg;
99
+ ```
100
+
101
+ #### 3. Simplified Object Format
102
+
103
+ Send an object where all properties (except 'tags' and 'timestamp') are treated as fields:
104
+
105
+ ```javascript
106
+ msg.measurement = "environment";
107
+ msg.payload = {
108
+ temperature: 21.5,
109
+ humidity: 65,
110
+ pressure: 1013.25,
111
+ tags: {
112
+ room: "bedroom",
113
+ floor: "2"
114
+ }
115
+ };
116
+ return msg;
117
+ ```
118
+
119
+ ### Data Types
120
+
121
+ When using object format, the node automatically handles data types:
122
+ - **Numbers**:
123
+ - Integers are written as integer fields
124
+ - Floats are written as float fields
125
+ - **Booleans**: Written as boolean fields
126
+ - **Strings**: Written as string fields
127
+ - **Tags**: Always converted to strings
128
+
129
+ ### Message Properties
130
+
131
+ The following message properties can be used to override node configuration:
132
+
133
+ - `msg.measurement` - Override the measurement name
134
+ - `msg.database` - Override the database name
135
+ - `msg.timestamp` - Set the timestamp for the data point (Date object or milliseconds)
136
+
137
+ ## Examples
138
+
139
+ ### Example 1: Temperature Sensor
140
+
141
+ ```javascript
142
+ // Function node
143
+ msg.measurement = "temperature";
144
+ msg.payload = {
145
+ value: 21.5,
146
+ tags: {
147
+ location: "living_room",
148
+ sensor_id: "temp_001"
149
+ }
150
+ };
151
+ return msg;
152
+ ```
153
+
154
+ ### Example 2: Multi-Sensor Data
155
+
156
+ ```javascript
157
+ // Function node
158
+ msg.measurement = "environment";
159
+ msg.payload = {
160
+ fields: {
161
+ temperature: 22.3,
162
+ humidity: 58,
163
+ co2: 412,
164
+ light: 850
165
+ },
166
+ tags: {
167
+ room: "office",
168
+ floor: "3",
169
+ building: "A"
170
+ }
171
+ };
172
+ return msg;
173
+ ```
174
+
175
+ ### Example 3: MQTT to InfluxDB
176
+
177
+ ```
178
+ [MQTT In] --> [JSON Parse] --> [Function] --> [InfluxDB v3 Write]
179
+ ```
180
+
181
+ Function node:
182
+ ```javascript
183
+ // Parse MQTT topic for location
184
+ const location = msg.topic.split('/')[1];
185
+
186
+ msg.measurement = "sensor_data";
187
+ msg.payload = {
188
+ temperature: msg.payload.temp,
189
+ humidity: msg.payload.hum,
190
+ tags: {
191
+ location: location
192
+ }
193
+ };
194
+ return msg;
195
+ ```
196
+
197
+ ### Example 4: Using Line Protocol
198
+
199
+ ```javascript
200
+ // Function node - direct line protocol
201
+ const location = "warehouse";
202
+ const temp = 18.5;
203
+ const humidity = 72;
204
+
205
+ msg.payload = `climate,location=${location} temperature=${temp},humidity=${humidity}`;
206
+ return msg;
207
+ ```
208
+
209
+ ### Example 5: Multiple Databases
210
+
211
+ ```javascript
212
+ // Write to different databases based on data type
213
+ if (msg.payload.type === "critical") {
214
+ msg.database = "critical-events";
215
+ } else {
216
+ msg.database = "general-logs";
217
+ }
218
+
219
+ msg.measurement = "events";
220
+ msg.payload = {
221
+ severity: msg.payload.severity,
222
+ message: msg.payload.msg,
223
+ tags: {
224
+ type: msg.payload.type
225
+ }
226
+ };
227
+ return msg;
228
+ ```
229
+
230
+ ## Sample Flow
231
+
232
+ Import this flow into Node-RED to get started:
233
+
234
+ ```json
235
+ [
236
+ {
237
+ "id": "influxdb3-config-node",
238
+ "type": "influxdb3-config",
239
+ "name": "My InfluxDB v3",
240
+ "host": "https://us-east-1-1.aws.cloud2.influxdata.com",
241
+ "database": "my-database"
242
+ },
243
+ {
244
+ "id": "inject-node",
245
+ "type": "inject",
246
+ "name": "Generate Data",
247
+ "props": [{"p": "payload"}],
248
+ "repeat": "5",
249
+ "topic": "",
250
+ "payload": "",
251
+ "payloadType": "date",
252
+ "x": 140,
253
+ "y": 100,
254
+ "wires": [["function-node"]]
255
+ },
256
+ {
257
+ "id": "function-node",
258
+ "type": "function",
259
+ "name": "Format Data",
260
+ "func": "msg.measurement = 'temperature';\nmsg.payload = {\n value: 20 + Math.random() * 10,\n tags: {\n location: 'office'\n }\n};\nreturn msg;",
261
+ "x": 320,
262
+ "y": 100,
263
+ "wires": [["influxdb3-write-node"]]
264
+ },
265
+ {
266
+ "id": "influxdb3-write-node",
267
+ "type": "influxdb3-write",
268
+ "name": "Write to InfluxDB",
269
+ "influxdb": "influxdb3-config-node",
270
+ "measurement": "",
271
+ "database": "",
272
+ "x": 530,
273
+ "y": 100,
274
+ "wires": [["debug-node"]]
275
+ },
276
+ {
277
+ "id": "debug-node",
278
+ "type": "debug",
279
+ "name": "Debug",
280
+ "x": 730,
281
+ "y": 100,
282
+ "wires": []
283
+ }
284
+ ]
285
+ ```
286
+
287
+ ## Configuration with Environment Variables
288
+
289
+ You can use environment variables in the configuration node:
290
+
291
+ - `INFLUX_HOST` - InfluxDB v3 host URL
292
+ - `INFLUX_TOKEN` - Authentication token
293
+ - `INFLUX_DATABASE` - Default database name
294
+
295
+ Simply reference them in the Node-RED UI using `${INFLUX_HOST}` syntax (if using Node-RED environment variable substitution).
296
+
297
+ ## Troubleshooting
298
+
299
+ ### Connection Issues
300
+
301
+ - Verify your host URL is correct and includes `https://`
302
+ - Check that your token has write permissions for the database
303
+ - Ensure the database name exists in your InfluxDB v3 instance
304
+
305
+ ### Data Not Appearing
306
+
307
+ - Check the node status - it should show "written" briefly after successful writes
308
+ - Verify at least one field is provided (InfluxDB requires at least one field)
309
+ - Check that field values are not null or undefined
310
+
311
+ ### Error Messages
312
+
313
+ The node will display error status and log details to the Node-RED debug panel:
314
+ - **"no config"** - The InfluxDB v3 config node is not selected
315
+ - **"error"** - Check the debug panel for details
316
+
317
+ ## Requirements
318
+
319
+ - Node-RED v2.0.0 or higher
320
+ - InfluxDB v3 instance (Cloud or Edge)
321
+
322
+ ## License
323
+
324
+ MIT
325
+
326
+ ## Author
327
+
328
+ Your Name
329
+
330
+ ## Links
331
+
332
+ - [InfluxDB v3 JavaScript Client](https://github.com/InfluxCommunity/influxdb3-js)
333
+ - [InfluxDB v3 Documentation](https://docs.influxdata.com/influxdb/v3/)
334
+ - [Node-RED](https://nodered.org/)
335
+
336
+ ## Contributing
337
+
338
+ Contributions are welcome! Please feel free to submit a Pull Request.
339
+
@@ -0,0 +1,170 @@
1
+ [
2
+ {
3
+ "id": "example-flow",
4
+ "type": "tab",
5
+ "label": "InfluxDB v3 Example",
6
+ "disabled": false,
7
+ "info": "Example flow showing how to write data to InfluxDB v3"
8
+ },
9
+ {
10
+ "id": "inject-temp",
11
+ "type": "inject",
12
+ "z": "example-flow",
13
+ "name": "Generate Temperature Data",
14
+ "props": [
15
+ {
16
+ "p": "payload"
17
+ }
18
+ ],
19
+ "repeat": "5",
20
+ "crontab": "",
21
+ "once": false,
22
+ "onceDelay": 0.1,
23
+ "topic": "",
24
+ "payload": "",
25
+ "payloadType": "date",
26
+ "x": 180,
27
+ "y": 100,
28
+ "wires": [
29
+ [
30
+ "format-temp"
31
+ ]
32
+ ]
33
+ },
34
+ {
35
+ "id": "format-temp",
36
+ "type": "function",
37
+ "z": "example-flow",
38
+ "name": "Format Temperature",
39
+ "func": "// Generate random temperature between 18 and 28 degrees\nconst temp = 18 + Math.random() * 10;\nconst humidity = 40 + Math.random() * 30;\n\nmsg.measurement = 'environment';\nmsg.payload = {\n temperature: parseFloat(temp.toFixed(2)),\n humidity: parseFloat(humidity.toFixed(2)),\n tags: {\n location: 'office',\n sensor: 'dht22_01'\n }\n};\n\nreturn msg;",
40
+ "outputs": 1,
41
+ "noerr": 0,
42
+ "initialize": "",
43
+ "finalize": "",
44
+ "libs": [],
45
+ "x": 420,
46
+ "y": 100,
47
+ "wires": [
48
+ [
49
+ "write-influx",
50
+ "debug-payload"
51
+ ]
52
+ ]
53
+ },
54
+ {
55
+ "id": "write-influx",
56
+ "type": "influxdb3-write",
57
+ "z": "example-flow",
58
+ "influxdb": "influxdb3-config",
59
+ "name": "Write to InfluxDB v3",
60
+ "measurement": "",
61
+ "database": "",
62
+ "x": 660,
63
+ "y": 100,
64
+ "wires": [
65
+ [
66
+ "debug-result"
67
+ ]
68
+ ]
69
+ },
70
+ {
71
+ "id": "debug-payload",
72
+ "type": "debug",
73
+ "z": "example-flow",
74
+ "name": "Debug Payload",
75
+ "active": true,
76
+ "tosidebar": true,
77
+ "console": false,
78
+ "tostatus": false,
79
+ "complete": "payload",
80
+ "targetType": "msg",
81
+ "statusVal": "",
82
+ "statusType": "auto",
83
+ "x": 660,
84
+ "y": 60,
85
+ "wires": []
86
+ },
87
+ {
88
+ "id": "debug-result",
89
+ "type": "debug",
90
+ "z": "example-flow",
91
+ "name": "Debug Result",
92
+ "active": true,
93
+ "tosidebar": true,
94
+ "console": false,
95
+ "tostatus": false,
96
+ "complete": "true",
97
+ "targetType": "full",
98
+ "statusVal": "",
99
+ "statusType": "auto",
100
+ "x": 870,
101
+ "y": 100,
102
+ "wires": []
103
+ },
104
+ {
105
+ "id": "inject-line-protocol",
106
+ "type": "inject",
107
+ "z": "example-flow",
108
+ "name": "Line Protocol Example",
109
+ "props": [
110
+ {
111
+ "p": "payload"
112
+ }
113
+ ],
114
+ "repeat": "",
115
+ "crontab": "",
116
+ "once": false,
117
+ "onceDelay": 0.1,
118
+ "topic": "",
119
+ "payload": "weather,location=garden temperature=18.5,humidity=75,pressure=1013.25",
120
+ "payloadType": "str",
121
+ "x": 180,
122
+ "y": 200,
123
+ "wires": [
124
+ [
125
+ "write-influx-lp"
126
+ ]
127
+ ]
128
+ },
129
+ {
130
+ "id": "write-influx-lp",
131
+ "type": "influxdb3-write",
132
+ "z": "example-flow",
133
+ "influxdb": "influxdb3-config",
134
+ "name": "Write Line Protocol",
135
+ "measurement": "",
136
+ "database": "",
137
+ "x": 450,
138
+ "y": 200,
139
+ "wires": [
140
+ [
141
+ "debug-lp"
142
+ ]
143
+ ]
144
+ },
145
+ {
146
+ "id": "debug-lp",
147
+ "type": "debug",
148
+ "z": "example-flow",
149
+ "name": "Debug LP",
150
+ "active": true,
151
+ "tosidebar": true,
152
+ "console": false,
153
+ "tostatus": false,
154
+ "complete": "true",
155
+ "targetType": "full",
156
+ "statusVal": "",
157
+ "statusType": "auto",
158
+ "x": 660,
159
+ "y": 200,
160
+ "wires": []
161
+ },
162
+ {
163
+ "id": "influxdb3-config",
164
+ "type": "influxdb3-config",
165
+ "name": "My InfluxDB v3",
166
+ "host": "https://us-east-1-1.aws.cloud2.influxdata.com",
167
+ "database": "my-database"
168
+ }
169
+ ]
170
+
@@ -0,0 +1,134 @@
1
+ [
2
+ {
3
+ "id": "mqtt-flow",
4
+ "type": "tab",
5
+ "label": "MQTT to InfluxDB v3",
6
+ "disabled": false,
7
+ "info": "Example flow showing how to capture MQTT data and write to InfluxDB v3"
8
+ },
9
+ {
10
+ "id": "mqtt-in",
11
+ "type": "mqtt in",
12
+ "z": "mqtt-flow",
13
+ "name": "MQTT Sensor Data",
14
+ "topic": "sensors/+/data",
15
+ "qos": "0",
16
+ "datatype": "json",
17
+ "broker": "mqtt-broker",
18
+ "nl": false,
19
+ "rap": false,
20
+ "inputs": 0,
21
+ "x": 140,
22
+ "y": 100,
23
+ "wires": [
24
+ [
25
+ "parse-mqtt"
26
+ ]
27
+ ]
28
+ },
29
+ {
30
+ "id": "parse-mqtt",
31
+ "type": "function",
32
+ "z": "mqtt-flow",
33
+ "name": "Parse MQTT Data",
34
+ "func": "// Extract sensor ID from topic (sensors/SENSOR_ID/data)\nconst topicParts = msg.topic.split('/');\nconst sensorId = topicParts[1];\n\n// Prepare data for InfluxDB\nmsg.measurement = 'sensor_readings';\nmsg.payload = {\n fields: {\n temperature: msg.payload.temperature,\n humidity: msg.payload.humidity,\n battery: msg.payload.battery\n },\n tags: {\n sensor_id: sensorId,\n location: msg.payload.location || 'unknown'\n }\n};\n\nreturn msg;",
35
+ "outputs": 1,
36
+ "noerr": 0,
37
+ "initialize": "",
38
+ "finalize": "",
39
+ "libs": [],
40
+ "x": 360,
41
+ "y": 100,
42
+ "wires": [
43
+ [
44
+ "influx-write",
45
+ "debug-mqtt"
46
+ ]
47
+ ]
48
+ },
49
+ {
50
+ "id": "influx-write",
51
+ "type": "influxdb3-write",
52
+ "z": "mqtt-flow",
53
+ "influxdb": "influxdb3-config",
54
+ "name": "Store in InfluxDB",
55
+ "measurement": "",
56
+ "database": "",
57
+ "x": 590,
58
+ "y": 100,
59
+ "wires": [
60
+ [
61
+ "debug-success"
62
+ ]
63
+ ]
64
+ },
65
+ {
66
+ "id": "debug-mqtt",
67
+ "type": "debug",
68
+ "z": "mqtt-flow",
69
+ "name": "MQTT Data",
70
+ "active": true,
71
+ "tosidebar": true,
72
+ "console": false,
73
+ "tostatus": false,
74
+ "complete": "payload",
75
+ "targetType": "msg",
76
+ "statusVal": "",
77
+ "statusType": "auto",
78
+ "x": 570,
79
+ "y": 60,
80
+ "wires": []
81
+ },
82
+ {
83
+ "id": "debug-success",
84
+ "type": "debug",
85
+ "z": "mqtt-flow",
86
+ "name": "Write Result",
87
+ "active": true,
88
+ "tosidebar": true,
89
+ "console": false,
90
+ "tostatus": false,
91
+ "complete": "true",
92
+ "targetType": "full",
93
+ "statusVal": "",
94
+ "statusType": "auto",
95
+ "x": 790,
96
+ "y": 100,
97
+ "wires": []
98
+ },
99
+ {
100
+ "id": "mqtt-broker",
101
+ "type": "mqtt-broker",
102
+ "name": "Local MQTT Broker",
103
+ "broker": "localhost",
104
+ "port": "1883",
105
+ "clientid": "",
106
+ "autoConnect": true,
107
+ "usetls": false,
108
+ "protocolVersion": "4",
109
+ "keepalive": "60",
110
+ "cleansession": true,
111
+ "birthTopic": "",
112
+ "birthQos": "0",
113
+ "birthPayload": "",
114
+ "birthMsg": {},
115
+ "closeTopic": "",
116
+ "closeQos": "0",
117
+ "closePayload": "",
118
+ "closeMsg": {},
119
+ "willTopic": "",
120
+ "willQos": "0",
121
+ "willPayload": "",
122
+ "willMsg": {},
123
+ "userProps": "",
124
+ "sessionExpiry": ""
125
+ },
126
+ {
127
+ "id": "influxdb3-config",
128
+ "type": "influxdb3-config",
129
+ "name": "My InfluxDB v3",
130
+ "host": "https://us-east-1-1.aws.cloud2.influxdata.com",
131
+ "database": "my-database"
132
+ }
133
+ ]
134
+
package/influxdb3.html ADDED
@@ -0,0 +1,204 @@
1
+ <!--
2
+ InfluxDB v3 Configuration Node
3
+ -->
4
+ <script type="text/javascript">
5
+ RED.nodes.registerType('influxdb3-config', {
6
+ category: 'config',
7
+ defaults: {
8
+ name: { value: '' },
9
+ host: { value: '', required: true },
10
+ database: { value: '', required: true }
11
+ },
12
+ credentials: {
13
+ token: { type: 'password' }
14
+ },
15
+ label: function() {
16
+ return this.name || this.host || 'InfluxDB v3';
17
+ }
18
+ });
19
+ </script>
20
+
21
+ <script type="text/html" data-template-name="influxdb3-config">
22
+ <div class="form-row">
23
+ <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
24
+ <input type="text" id="node-config-input-name" placeholder="Connection name">
25
+ </div>
26
+ <div class="form-row">
27
+ <label for="node-config-input-host"><i class="fa fa-server"></i> Host</label>
28
+ <input type="text" id="node-config-input-host" placeholder="https://us-east-1-1.aws.cloud2.influxdata.com">
29
+ </div>
30
+ <div class="form-row">
31
+ <label for="node-config-input-token"><i class="fa fa-key"></i> Token</label>
32
+ <input type="password" id="node-config-input-token">
33
+ </div>
34
+ <div class="form-row">
35
+ <label for="node-config-input-database"><i class="fa fa-database"></i> Database</label>
36
+ <input type="text" id="node-config-input-database" placeholder="my-database">
37
+ </div>
38
+ </script>
39
+
40
+ <script type="text/html" data-help-name="influxdb3-config">
41
+ <p>Configuration node for InfluxDB v3 connection.</p>
42
+ <h3>Details</h3>
43
+ <p>This configuration node stores the connection details for an InfluxDB v3 instance.</p>
44
+ <dl class="message-properties">
45
+ <dt>Name <span class="property-type">string</span></dt>
46
+ <dd>A friendly name for this connection</dd>
47
+ <dt>Host <span class="property-type">string</span></dt>
48
+ <dd>The InfluxDB v3 host URL (e.g., https://us-east-1-1.aws.cloud2.influxdata.com)</dd>
49
+ <dt>Token <span class="property-type">string</span></dt>
50
+ <dd>The authentication token for accessing InfluxDB v3</dd>
51
+ <dt>Database <span class="property-type">string</span></dt>
52
+ <dd>The default database (bucket) name to write to</dd>
53
+ </dl>
54
+ </script>
55
+
56
+ <!--
57
+ InfluxDB v3 Write Node
58
+ -->
59
+ <script type="text/javascript">
60
+ RED.nodes.registerType('influxdb3-write', {
61
+ category: 'storage',
62
+ color: '#9ea8db',
63
+ defaults: {
64
+ influxdb: { type: 'influxdb3-config', required: true },
65
+ name: { value: '' },
66
+ measurement: { value: '' },
67
+ database: { value: '' }
68
+ },
69
+ inputs: 1,
70
+ outputs: 1,
71
+ icon: 'db.png',
72
+ label: function() {
73
+ return this.name || 'influxdb3 write';
74
+ },
75
+ labelStyle: function() {
76
+ return this.name ? 'node_label_italic' : '';
77
+ }
78
+ });
79
+ </script>
80
+
81
+ <script type="text/html" data-template-name="influxdb3-write">
82
+ <div class="form-row">
83
+ <label for="node-input-influxdb"><i class="fa fa-server"></i> Connection</label>
84
+ <input type="text" id="node-input-influxdb">
85
+ </div>
86
+ <div class="form-row">
87
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
88
+ <input type="text" id="node-input-name" placeholder="Name">
89
+ </div>
90
+ <div class="form-row">
91
+ <label for="node-input-measurement"><i class="fa fa-chart-line"></i> Measurement</label>
92
+ <input type="text" id="node-input-measurement" placeholder="Leave empty to use msg.measurement">
93
+ </div>
94
+ <div class="form-row">
95
+ <label for="node-input-database"><i class="fa fa-database"></i> Database</label>
96
+ <input type="text" id="node-input-database" placeholder="Override default database (optional)">
97
+ </div>
98
+ </script>
99
+
100
+ <script type="text/html" data-help-name="influxdb3-write">
101
+ <p>Writes data to InfluxDB v3.</p>
102
+
103
+ <h3>Inputs</h3>
104
+ <dl class="message-properties">
105
+ <dt>payload <span class="property-type">string | object</span></dt>
106
+ <dd>The data to write. Can be either:
107
+ <ul>
108
+ <li>A string in line protocol format</li>
109
+ <li>An object with <code>fields</code> (required) and optionally <code>tags</code> and <code>timestamp</code></li>
110
+ </ul>
111
+ </dd>
112
+ <dt class="optional">measurement <span class="property-type">string</span></dt>
113
+ <dd>Override the measurement name configured in the node</dd>
114
+ <dt class="optional">database <span class="property-type">string</span></dt>
115
+ <dd>Override the database configured in the node or connection</dd>
116
+ <dt class="optional">timestamp <span class="property-type">Date | number</span></dt>
117
+ <dd>Timestamp for the data point (if not in payload)</dd>
118
+ </dl>
119
+
120
+ <h3>Outputs</h3>
121
+ <dl class="message-properties">
122
+ <dt>payload <span class="property-type">object</span></dt>
123
+ <dd>The original payload is passed through</dd>
124
+ </dl>
125
+
126
+ <h3>Details</h3>
127
+ <p>This node writes data to InfluxDB v3. The data can be provided in two formats:</p>
128
+
129
+ <h4>Line Protocol Format</h4>
130
+ <p>Send a string in line protocol format:</p>
131
+ <pre>msg.payload = "temperature,location=room1 value=21.5";</pre>
132
+
133
+ <h4>Object Format</h4>
134
+ <p>Send an object with fields and optionally tags:</p>
135
+ <pre>{
136
+ "fields": {
137
+ "temperature": 21.5,
138
+ "humidity": 65
139
+ },
140
+ "tags": {
141
+ "location": "room1",
142
+ "sensor": "dht22"
143
+ },
144
+ "timestamp": Date.now()
145
+ }</pre>
146
+
147
+ <h4>Simplified Object Format</h4>
148
+ <p>Send an object where all properties (except 'tags' and 'timestamp') are treated as fields:</p>
149
+ <pre>{
150
+ "temperature": 21.5,
151
+ "humidity": 65,
152
+ "tags": {
153
+ "location": "room1"
154
+ }
155
+ }</pre>
156
+
157
+ <h3>Configuration</h3>
158
+ <dl class="message-properties">
159
+ <dt>Connection</dt>
160
+ <dd>The InfluxDB v3 connection configuration</dd>
161
+ <dt>Measurement</dt>
162
+ <dd>The measurement name to use. Can be overridden by <code>msg.measurement</code></dd>
163
+ <dt>Database</dt>
164
+ <dd>Optional database override. If not set, uses the database from the connection config</dd>
165
+ </dl>
166
+
167
+ <h3>Examples</h3>
168
+ <h4>Example 1: Simple temperature reading</h4>
169
+ <pre>msg.measurement = "temperature";
170
+ msg.payload = {
171
+ "fields": {
172
+ "value": 21.5
173
+ },
174
+ "tags": {
175
+ "location": "living_room"
176
+ }
177
+ };
178
+ return msg;</pre>
179
+
180
+ <h4>Example 2: Multiple sensor values</h4>
181
+ <pre>msg.measurement = "environment";
182
+ msg.payload = {
183
+ "temperature": 21.5,
184
+ "humidity": 65,
185
+ "pressure": 1013.25,
186
+ "tags": {
187
+ "room": "bedroom",
188
+ "floor": "2"
189
+ }
190
+ };
191
+ return msg;</pre>
192
+
193
+ <h4>Example 3: Line protocol string</h4>
194
+ <pre>msg.payload = "weather,location=garden temperature=18.5,humidity=75";
195
+ return msg;</pre>
196
+
197
+ <h3>References</h3>
198
+ <ul>
199
+ <li><a href="https://github.com/InfluxCommunity/influxdb3-js">InfluxDB v3 JavaScript Client</a></li>
200
+ <li><a href="https://docs.influxdata.com/influxdb/v3/">InfluxDB v3 Documentation</a></li>
201
+ <li><a href="https://docs.influxdata.com/influxdb/v3/reference/syntax/line-protocol/">Line Protocol Reference</a></li>
202
+ </ul>
203
+ </script>
204
+
package/influxdb3.js ADDED
@@ -0,0 +1,207 @@
1
+ /**
2
+ * InfluxDB v3 nodes for Node-RED
3
+ */
4
+
5
+ module.exports = function(RED) {
6
+ const { InfluxDBClient, Point } = require('@influxdata/influxdb3-client');
7
+
8
+ /**
9
+ * Configuration node to hold InfluxDB v3 connection details
10
+ */
11
+ function InfluxDB3ConfigNode(config) {
12
+ RED.nodes.createNode(this, config);
13
+
14
+ this.host = config.host;
15
+ this.database = config.database;
16
+ this.name = config.name;
17
+
18
+ // Store token as a credential
19
+ this.token = this.credentials.token;
20
+
21
+ // Client instance (will be created on demand)
22
+ this.client = null;
23
+
24
+ // Get or create a client instance
25
+ this.getClient = function() {
26
+ if (!this.client) {
27
+ try {
28
+ this.client = new InfluxDBClient({
29
+ host: this.host,
30
+ token: this.token,
31
+ database: this.database
32
+ });
33
+ } catch (error) {
34
+ throw new Error(`Failed to create InfluxDB client: ${error.message}`);
35
+ }
36
+ }
37
+ return this.client;
38
+ };
39
+
40
+ // Clean up on close
41
+ this.on('close', function() {
42
+ if (this.client) {
43
+ try {
44
+ this.client.close();
45
+ this.client = null;
46
+ } catch (error) {
47
+ // Ignore errors on close
48
+ }
49
+ }
50
+ });
51
+ }
52
+
53
+ RED.nodes.registerType('influxdb3-config', InfluxDB3ConfigNode, {
54
+ credentials: {
55
+ token: { type: 'password' }
56
+ }
57
+ });
58
+
59
+ /**
60
+ * InfluxDB v3 Write Node
61
+ */
62
+ function InfluxDB3WriteNode(config) {
63
+ RED.nodes.createNode(this, config);
64
+
65
+ this.influxdb = RED.nodes.getNode(config.influxdb);
66
+ this.measurement = config.measurement;
67
+ this.database = config.database;
68
+
69
+ const node = this;
70
+
71
+ if (!this.influxdb) {
72
+ this.error('InfluxDB v3 config not set');
73
+ this.status({ fill: 'red', shape: 'dot', text: 'no config' });
74
+ return;
75
+ }
76
+
77
+ // Process incoming messages
78
+ node.on('input', async function(msg, send, done) {
79
+ // For Node-RED 0.x compatibility
80
+ send = send || function() { node.send.apply(node, arguments); };
81
+ done = done || function(err) {
82
+ if (err) {
83
+ node.error(err, msg);
84
+ }
85
+ };
86
+
87
+ try {
88
+ const client = node.influxdb.getClient();
89
+
90
+ // Determine the database to use
91
+ const targetDatabase = msg.database || node.database || node.influxdb.database;
92
+
93
+ if (!targetDatabase) {
94
+ throw new Error('Database not specified');
95
+ }
96
+
97
+ let lineProtocol;
98
+
99
+ // Check if msg.payload is already in line protocol format
100
+ if (typeof msg.payload === 'string') {
101
+ lineProtocol = msg.payload;
102
+ } else if (msg.payload && typeof msg.payload === 'object') {
103
+ // Build line protocol from payload object
104
+ const measurement = msg.measurement || node.measurement;
105
+
106
+ if (!measurement) {
107
+ throw new Error('Measurement not specified');
108
+ }
109
+
110
+ const point = new Point(measurement);
111
+
112
+ // Add tags
113
+ if (msg.payload.tags && typeof msg.payload.tags === 'object') {
114
+ for (const [key, value] of Object.entries(msg.payload.tags)) {
115
+ if (value !== null && value !== undefined) {
116
+ point.setTag(key, String(value));
117
+ }
118
+ }
119
+ }
120
+
121
+ // Add fields
122
+ if (msg.payload.fields && typeof msg.payload.fields === 'object') {
123
+ for (const [key, value] of Object.entries(msg.payload.fields)) {
124
+ if (value !== null && value !== undefined) {
125
+ if (typeof value === 'number') {
126
+ if (Number.isInteger(value)) {
127
+ point.setIntegerField(key, value);
128
+ } else {
129
+ point.setFloatField(key, value);
130
+ }
131
+ } else if (typeof value === 'boolean') {
132
+ point.setBooleanField(key, value);
133
+ } else {
134
+ point.setStringField(key, String(value));
135
+ }
136
+ }
137
+ }
138
+ } else {
139
+ // If no 'fields' property, treat all non-tag properties as fields
140
+ for (const [key, value] of Object.entries(msg.payload)) {
141
+ if (key !== 'tags' && key !== 'timestamp' && value !== null && value !== undefined) {
142
+ if (typeof value === 'number') {
143
+ if (Number.isInteger(value)) {
144
+ point.setIntegerField(key, value);
145
+ } else {
146
+ point.setFloatField(key, value);
147
+ }
148
+ } else if (typeof value === 'boolean') {
149
+ point.setBooleanField(key, value);
150
+ } else if (typeof value !== 'object') {
151
+ point.setStringField(key, String(value));
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ // Add timestamp if provided
158
+ if (msg.payload.timestamp) {
159
+ if (msg.payload.timestamp instanceof Date) {
160
+ point.setTimestamp(msg.payload.timestamp);
161
+ } else if (typeof msg.payload.timestamp === 'number') {
162
+ point.setTimestamp(new Date(msg.payload.timestamp));
163
+ }
164
+ } else if (msg.timestamp) {
165
+ if (msg.timestamp instanceof Date) {
166
+ point.setTimestamp(msg.timestamp);
167
+ } else if (typeof msg.timestamp === 'number') {
168
+ point.setTimestamp(new Date(msg.timestamp));
169
+ }
170
+ }
171
+
172
+ lineProtocol = point.toLineProtocol();
173
+
174
+ if (!lineProtocol) {
175
+ throw new Error('No fields to write - at least one field is required');
176
+ }
177
+ } else {
178
+ throw new Error('Invalid payload format. Expected string (line protocol) or object with fields');
179
+ }
180
+
181
+ // Write to InfluxDB
182
+ await client.write(lineProtocol, targetDatabase);
183
+
184
+ node.status({ fill: 'green', shape: 'dot', text: 'written' });
185
+
186
+ // Clear status after 3 seconds
187
+ setTimeout(() => {
188
+ node.status({});
189
+ }, 3000);
190
+
191
+ send(msg);
192
+ done();
193
+
194
+ } catch (error) {
195
+ node.status({ fill: 'red', shape: 'dot', text: 'error' });
196
+ done(error);
197
+ }
198
+ });
199
+
200
+ node.on('close', function() {
201
+ node.status({});
202
+ });
203
+ }
204
+
205
+ RED.nodes.registerType('influxdb3-write', InfluxDB3WriteNode);
206
+ };
207
+
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "node-red-contrib-influxdb3",
3
+ "version": "1.0.0",
4
+ "description": "Node-RED nodes for InfluxDB v3 integration",
5
+ "main": "influxdb3.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "node-red",
11
+ "influxdb",
12
+ "influxdb3",
13
+ "time-series",
14
+ "database"
15
+ ],
16
+ "author": "Stuart B",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/stuartb55/nodered-influxdb3.git"
21
+ },
22
+ "homepage": "https://github.com/stuartb55/nodered-influxdb3#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/stuartb55/nodered-influxdb3/issues"
25
+ },
26
+ "node-red": {
27
+ "nodes": {
28
+ "influxdb3": "influxdb3.js"
29
+ }
30
+ },
31
+ "dependencies": {
32
+ "@influxdata/influxdb3-client": "^1.4.0"
33
+ },
34
+ "peerDependencies": {
35
+ "node-red": ">=2.0.0"
36
+ }
37
+ }
38
+