node-red-contrib-influxdb3 1.0.2 → 1.0.3
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 +22 -22
- package/README.md +386 -377
- package/__tests__/influxdb3.test.js +240 -0
- package/examples/basic-flow.json +170 -170
- package/examples/mqtt-to-influx.json +134 -134
- package/influxdb3.html +243 -228
- package/influxdb3.js +315 -303
- package/package.json +44 -42
- package/renovate.json +6 -6
package/influxdb3.js
CHANGED
|
@@ -1,303 +1,315 @@
|
|
|
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
|
-
* Normalize host URL to ensure it has trailing slash
|
|
10
|
-
*/
|
|
11
|
-
function normalizeHost(host) {
|
|
12
|
-
if (!host || typeof host !== 'string') {
|
|
13
|
-
return host;
|
|
14
|
-
}
|
|
15
|
-
return host.endsWith('/') ? host : host + '/';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Process a field value and add it to a Point
|
|
20
|
-
*/
|
|
21
|
-
function addFieldToPoint(point, key, value, integerFields) {
|
|
22
|
-
if (value === null || value === undefined) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Handle string with 'i' suffix for integers (e.g., "42i")
|
|
27
|
-
if (typeof value === 'string' && /^-?\d+i$/.test(value)) {
|
|
28
|
-
const intValue = parseInt(value.slice(0, -1), 10);
|
|
29
|
-
// Validate parsed value
|
|
30
|
-
if (!isNaN(intValue) && isFinite(intValue)) {
|
|
31
|
-
point.setIntegerField(key, intValue);
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
} else if (typeof value === 'number') {
|
|
35
|
-
// Validate number
|
|
36
|
-
if (!isFinite(value)) {
|
|
37
|
-
RED.log.warn(`Skipping field '${key}': value is not finite (${value})`);
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Default to float for all numbers (safe default)
|
|
42
|
-
// Use integer only if explicitly marked in 'integers' array
|
|
43
|
-
if (integerFields.has(key)) {
|
|
44
|
-
const intValue = Math.floor(value);
|
|
45
|
-
if (intValue !== value) {
|
|
46
|
-
RED.log.warn(`Field '${key}': truncating ${value} to ${intValue} for integer field`);
|
|
47
|
-
}
|
|
48
|
-
point.setIntegerField(key, intValue);
|
|
49
|
-
} else {
|
|
50
|
-
point.setFloatField(key, value);
|
|
51
|
-
}
|
|
52
|
-
return true;
|
|
53
|
-
} else if (typeof value === 'boolean') {
|
|
54
|
-
point.setBooleanField(key, value);
|
|
55
|
-
return true;
|
|
56
|
-
} else if (typeof value === 'string') {
|
|
57
|
-
point.setStringField(key, value);
|
|
58
|
-
return true;
|
|
59
|
-
} else {
|
|
60
|
-
// Complex types (arrays, objects) are not supported
|
|
61
|
-
RED.log.warn(`Skipping field '${key}': unsupported type ${typeof value}`);
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Configuration node to hold InfluxDB v3 connection details
|
|
70
|
-
*/
|
|
71
|
-
function InfluxDB3ConfigNode(config) {
|
|
72
|
-
RED.nodes.createNode(this, config);
|
|
73
|
-
|
|
74
|
-
this.host = config.host;
|
|
75
|
-
this.database = config.database;
|
|
76
|
-
this.name = config.name;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
node.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
};
|
|
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
|
+
* Normalize host URL to ensure it has trailing slash
|
|
10
|
+
*/
|
|
11
|
+
function normalizeHost(host) {
|
|
12
|
+
if (!host || typeof host !== 'string') {
|
|
13
|
+
return host;
|
|
14
|
+
}
|
|
15
|
+
return host.endsWith('/') ? host : host + '/';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Process a field value and add it to a Point
|
|
20
|
+
*/
|
|
21
|
+
function addFieldToPoint(point, key, value, integerFields) {
|
|
22
|
+
if (value === null || value === undefined) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Handle string with 'i' suffix for integers (e.g., "42i")
|
|
27
|
+
if (typeof value === 'string' && /^-?\d+i$/.test(value)) {
|
|
28
|
+
const intValue = parseInt(value.slice(0, -1), 10);
|
|
29
|
+
// Validate parsed value
|
|
30
|
+
if (!isNaN(intValue) && isFinite(intValue)) {
|
|
31
|
+
point.setIntegerField(key, intValue);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
} else if (typeof value === 'number') {
|
|
35
|
+
// Validate number
|
|
36
|
+
if (!isFinite(value)) {
|
|
37
|
+
RED.log.warn(`Skipping field '${key}': value is not finite (${value})`);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default to float for all numbers (safe default)
|
|
42
|
+
// Use integer only if explicitly marked in 'integers' array
|
|
43
|
+
if (integerFields.has(key)) {
|
|
44
|
+
const intValue = Math.floor(value);
|
|
45
|
+
if (intValue !== value) {
|
|
46
|
+
RED.log.warn(`Field '${key}': truncating ${value} to ${intValue} for integer field`);
|
|
47
|
+
}
|
|
48
|
+
point.setIntegerField(key, intValue);
|
|
49
|
+
} else {
|
|
50
|
+
point.setFloatField(key, value);
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
} else if (typeof value === 'boolean') {
|
|
54
|
+
point.setBooleanField(key, value);
|
|
55
|
+
return true;
|
|
56
|
+
} else if (typeof value === 'string') {
|
|
57
|
+
point.setStringField(key, value);
|
|
58
|
+
return true;
|
|
59
|
+
} else {
|
|
60
|
+
// Complex types (arrays, objects) are not supported
|
|
61
|
+
RED.log.warn(`Skipping field '${key}': unsupported type ${typeof value}`);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Configuration node to hold InfluxDB v3 connection details
|
|
70
|
+
*/
|
|
71
|
+
function InfluxDB3ConfigNode(config) {
|
|
72
|
+
RED.nodes.createNode(this, config);
|
|
73
|
+
|
|
74
|
+
this.host = config.host;
|
|
75
|
+
this.database = config.database;
|
|
76
|
+
this.name = config.name;
|
|
77
|
+
this.tlsRejectUnauthorized = config.tlsRejectUnauthorized !== false;
|
|
78
|
+
this.caCertPath = config.caCertPath;
|
|
79
|
+
|
|
80
|
+
// Store token as a credential
|
|
81
|
+
this.token = this.credentials.token;
|
|
82
|
+
|
|
83
|
+
// Client instance (will be created on demand)
|
|
84
|
+
this.client = null;
|
|
85
|
+
|
|
86
|
+
// Get or create a client instance
|
|
87
|
+
this.getClient = function() {
|
|
88
|
+
if (!this.client) {
|
|
89
|
+
// Validate configuration
|
|
90
|
+
if (!this.host) {
|
|
91
|
+
throw new Error('InfluxDB host is not configured');
|
|
92
|
+
}
|
|
93
|
+
if (!this.token) {
|
|
94
|
+
throw new Error('InfluxDB token is not configured');
|
|
95
|
+
}
|
|
96
|
+
if (!this.database) {
|
|
97
|
+
throw new Error('InfluxDB database is not configured');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const normalizedHost = normalizeHost(this.host);
|
|
102
|
+
|
|
103
|
+
if (!this.tlsRejectUnauthorized) {
|
|
104
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
105
|
+
RED.log.warn('InfluxDB v3: TLS certificate verification is disabled for this process.');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.caCertPath) {
|
|
109
|
+
process.env.NODE_EXTRA_CA_CERTS = this.caCertPath;
|
|
110
|
+
RED.log.info(`InfluxDB v3: Using extra CA certificates from ${this.caCertPath}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
RED.log.info(`InfluxDB v3: Connecting to ${normalizedHost} with database ${this.database}`);
|
|
114
|
+
|
|
115
|
+
this.client = new InfluxDBClient({
|
|
116
|
+
host: normalizedHost,
|
|
117
|
+
token: this.token,
|
|
118
|
+
database: this.database
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
RED.log.info(`InfluxDB v3: Client created successfully`);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
RED.log.error(`InfluxDB v3: Failed to create client - ${error.message}`);
|
|
124
|
+
throw new Error(`Failed to create InfluxDB client: ${error.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return this.client;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Clean up on close
|
|
131
|
+
this.on('close', function() {
|
|
132
|
+
if (this.client) {
|
|
133
|
+
try {
|
|
134
|
+
RED.log.info('InfluxDB v3: Closing client connection');
|
|
135
|
+
this.client.close();
|
|
136
|
+
this.client = null;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
RED.log.warn(`InfluxDB v3: Error closing client - ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
RED.nodes.registerType('influxdb3-config', InfluxDB3ConfigNode, {
|
|
145
|
+
credentials: {
|
|
146
|
+
token: { type: 'password' }
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* InfluxDB v3 Write Node
|
|
152
|
+
*/
|
|
153
|
+
function InfluxDB3WriteNode(config) {
|
|
154
|
+
RED.nodes.createNode(this, config);
|
|
155
|
+
|
|
156
|
+
this.influxdb = RED.nodes.getNode(config.influxdb);
|
|
157
|
+
this.measurement = config.measurement;
|
|
158
|
+
this.database = config.database;
|
|
159
|
+
|
|
160
|
+
const node = this;
|
|
161
|
+
let statusTimeout = null;
|
|
162
|
+
|
|
163
|
+
if (!this.influxdb) {
|
|
164
|
+
this.error('InfluxDB v3 config not set');
|
|
165
|
+
this.status({ fill: 'red', shape: 'dot', text: 'no config' });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Helper to set status with auto-clear
|
|
170
|
+
function setStatus(status, clearAfterMs = 0) {
|
|
171
|
+
if (statusTimeout) {
|
|
172
|
+
clearTimeout(statusTimeout);
|
|
173
|
+
statusTimeout = null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
node.status(status);
|
|
177
|
+
|
|
178
|
+
if (clearAfterMs > 0) {
|
|
179
|
+
statusTimeout = setTimeout(() => {
|
|
180
|
+
node.status({});
|
|
181
|
+
statusTimeout = null;
|
|
182
|
+
}, clearAfterMs);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Process incoming messages
|
|
187
|
+
node.on('input', async function(msg, send, done) {
|
|
188
|
+
// For Node-RED 0.x compatibility
|
|
189
|
+
send = send || function() { node.send.apply(node, arguments); };
|
|
190
|
+
done = done || function(err) {
|
|
191
|
+
if (err) {
|
|
192
|
+
node.error(err, msg);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const client = node.influxdb.getClient();
|
|
198
|
+
|
|
199
|
+
// Determine the database to use
|
|
200
|
+
const targetDatabase = msg.database || node.database || node.influxdb.database;
|
|
201
|
+
|
|
202
|
+
if (!targetDatabase) {
|
|
203
|
+
throw new Error('Database not specified');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let lineProtocol;
|
|
207
|
+
|
|
208
|
+
// Check if msg.payload is already in line protocol format
|
|
209
|
+
if (typeof msg.payload === 'string') {
|
|
210
|
+
lineProtocol = msg.payload.trim();
|
|
211
|
+
if (!lineProtocol) {
|
|
212
|
+
throw new Error('Line protocol string is empty');
|
|
213
|
+
}
|
|
214
|
+
} else if (msg.payload && typeof msg.payload === 'object' && !Array.isArray(msg.payload)) {
|
|
215
|
+
// Build line protocol from payload object
|
|
216
|
+
const measurement = msg.measurement || node.measurement;
|
|
217
|
+
|
|
218
|
+
if (!measurement) {
|
|
219
|
+
throw new Error('Measurement not specified');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const point = new Point(measurement);
|
|
223
|
+
|
|
224
|
+
// Add tags
|
|
225
|
+
if (msg.payload.tags && typeof msg.payload.tags === 'object' && !Array.isArray(msg.payload.tags)) {
|
|
226
|
+
for (const [key, value] of Object.entries(msg.payload.tags)) {
|
|
227
|
+
if (value !== null && value !== undefined) {
|
|
228
|
+
point.setTag(key, String(value));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Get list of fields that should be treated as integers
|
|
234
|
+
const integerFields = new Set(msg.payload.integers || []);
|
|
235
|
+
let fieldCount = 0;
|
|
236
|
+
|
|
237
|
+
// Add fields
|
|
238
|
+
if (msg.payload.fields && typeof msg.payload.fields === 'object' && !Array.isArray(msg.payload.fields)) {
|
|
239
|
+
// Explicit fields object
|
|
240
|
+
for (const [key, value] of Object.entries(msg.payload.fields)) {
|
|
241
|
+
if (addFieldToPoint(point, key, value, integerFields)) {
|
|
242
|
+
fieldCount++;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
// Simplified format: treat all non-reserved properties as fields
|
|
247
|
+
const reservedKeys = new Set(['tags', 'timestamp', 'integers', 'fields']);
|
|
248
|
+
for (const [key, value] of Object.entries(msg.payload)) {
|
|
249
|
+
if (!reservedKeys.has(key)) {
|
|
250
|
+
if (addFieldToPoint(point, key, value, integerFields)) {
|
|
251
|
+
fieldCount++;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (fieldCount === 0) {
|
|
258
|
+
throw new Error('No valid fields to write - at least one field is required');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Add timestamp if provided
|
|
262
|
+
if (msg.payload.timestamp) {
|
|
263
|
+
const ts = msg.payload.timestamp;
|
|
264
|
+
if (ts instanceof Date && !isNaN(ts.getTime())) {
|
|
265
|
+
point.setTimestamp(ts);
|
|
266
|
+
} else if (typeof ts === 'number' && isFinite(ts) && ts > 0) {
|
|
267
|
+
point.setTimestamp(new Date(ts));
|
|
268
|
+
} else {
|
|
269
|
+
node.warn(`Invalid timestamp in payload: ${ts}`);
|
|
270
|
+
}
|
|
271
|
+
} else if (msg.timestamp) {
|
|
272
|
+
const ts = msg.timestamp;
|
|
273
|
+
if (ts instanceof Date && !isNaN(ts.getTime())) {
|
|
274
|
+
point.setTimestamp(ts);
|
|
275
|
+
} else if (typeof ts === 'number' && isFinite(ts) && ts > 0) {
|
|
276
|
+
point.setTimestamp(new Date(ts));
|
|
277
|
+
} else {
|
|
278
|
+
node.warn(`Invalid timestamp in msg: ${ts}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
lineProtocol = point.toLineProtocol();
|
|
283
|
+
|
|
284
|
+
if (!lineProtocol || lineProtocol.trim() === '') {
|
|
285
|
+
throw new Error('Generated line protocol is empty');
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
throw new Error('Invalid payload format. Expected string (line protocol) or object with fields');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Write to InfluxDB
|
|
292
|
+
await client.write(lineProtocol, targetDatabase);
|
|
293
|
+
|
|
294
|
+
setStatus({ fill: 'green', shape: 'dot', text: 'written' }, 3000);
|
|
295
|
+
|
|
296
|
+
send(msg);
|
|
297
|
+
done();
|
|
298
|
+
|
|
299
|
+
} catch (error) {
|
|
300
|
+
setStatus({ fill: 'red', shape: 'dot', text: 'error' });
|
|
301
|
+
done(error);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
node.on('close', function() {
|
|
306
|
+
if (statusTimeout) {
|
|
307
|
+
clearTimeout(statusTimeout);
|
|
308
|
+
statusTimeout = null;
|
|
309
|
+
}
|
|
310
|
+
node.status({});
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
RED.nodes.registerType('influxdb3-write', InfluxDB3WriteNode);
|
|
315
|
+
};
|