1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | module.exports = function(RED) {
|
18 | 'use strict';
|
19 |
|
20 | require('events').EventEmitter.prototype._maxListeners = 10000;
|
21 |
|
22 | const utils = require('@iobroker/adapter-core');
|
23 | const settings = require(process.env.NODE_RED_HOME + '/lib/red').settings;
|
24 |
|
25 | const instance = settings.get('iobrokerInstance') || 0;
|
26 | let config = settings.get('iobrokerConfig');
|
27 | const valueConvert = settings.get('valueConvert');
|
28 | const allowCreationOfForeignObjects = settings.get('allowCreationOfForeignObjects');
|
29 | if (typeof config === 'string') {
|
30 | config = JSON.parse(config);
|
31 | }
|
32 | let adapter;
|
33 | const existingNodes = [];
|
34 |
|
35 | try {
|
36 | adapter = utils.Adapter({name: 'node-red', instance, config});
|
37 | } catch(e) {
|
38 | console.log(e);
|
39 | }
|
40 | if (typeof adapter.setMaxListeners === 'function') {
|
41 | adapter.setMaxListeners(10000);
|
42 | }
|
43 | const nodeSets = [];
|
44 | const checkStates = [];
|
45 | const isValidID = new RegExp('^[_A-Za-z0-9ÄÖÜäöüа-яА-Я][-_A-Za-z0-9ÄÖÜäöüа-яА-Я]*\\.\\d+\\.');
|
46 | let ready = false;
|
47 | const log = adapter && adapter.log && adapter.log.warn ? adapter.log.warn : console.log;
|
48 |
|
49 | adapter.on('ready', () => {
|
50 | function checkQueuedStates(callback) {
|
51 | if (!checkStates.length) {
|
52 | return callback && callback();
|
53 | }
|
54 | const check = checkStates.shift();
|
55 | checkState(check.node, check.id, check.common, check.val, () => {
|
56 | check.callback && check.callback();
|
57 | setImmediate(() => checkQueuedStates(callback));
|
58 | });
|
59 | }
|
60 |
|
61 | ready = true;
|
62 | checkQueuedStates(() => {
|
63 | existingNodes.forEach(node => {
|
64 | if (node instanceof IOBrokerInNode) {
|
65 | adapter.on('stateChange', node.stateChange);
|
66 | if (node.fireOnStart && !node.topic.includes('*')) {
|
67 | adapter.getForeignState(node.topic, (err, state) =>
|
68 | node.stateChange(node.topic, state));
|
69 | }
|
70 | }
|
71 | node.subscribePattern && adapter.subscribeForeignStates(node.subscribePattern);
|
72 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
73 | });
|
74 |
|
75 | let count = 0;
|
76 |
|
77 | while (nodeSets.length) {
|
78 | const nodeSetData = nodeSets.pop();
|
79 | nodeSetData.node.emit('input', nodeSetData.msg);
|
80 | count++;
|
81 | }
|
82 | count && log(count + ' queued state values set in ioBroker');
|
83 | });
|
84 | });
|
85 |
|
86 | function isForeignState(id) {
|
87 | return isValidID.test(id) && !id.startsWith(adapter.namespace + '.');
|
88 | }
|
89 |
|
90 |
|
91 | function getRegex(pattern) {
|
92 | if (!pattern || pattern === '*') {
|
93 | return null;
|
94 | }
|
95 | if (!pattern.includes('*')) {
|
96 | return null;
|
97 | }
|
98 | if (pattern[pattern.length - 1] !== '*') {
|
99 | pattern = pattern + '$';
|
100 | }
|
101 | if (pattern[0] !== '*') {
|
102 | pattern = '^' + pattern;
|
103 | }
|
104 | pattern = pattern.replace(/\./g, '\\.');
|
105 | pattern = pattern.replace(/\*/g, '.*');
|
106 | return new RegExp(pattern);
|
107 | }
|
108 |
|
109 |
|
110 | function checkState(node, id, common, val, callback) {
|
111 | if (node.idChecked) {
|
112 | return callback && callback();
|
113 | }
|
114 | if (!ready) {
|
115 | checkStates.push({node, id, common, val, callback});
|
116 | return;
|
117 | }
|
118 | if (node.topic) {
|
119 | node.idChecked = true;
|
120 | }
|
121 |
|
122 | if (val === null || val === '__create__') {
|
123 | val = undefined;
|
124 | }
|
125 |
|
126 | adapter.getObject(id, (err, obj) => {
|
127 | if (!obj) {
|
128 | adapter.getForeignObject(id, (err, obj) => {
|
129 |
|
130 | if (!obj) {
|
131 | if (common) {
|
132 | log('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
133 |
|
134 | const data = {
|
135 | common,
|
136 | native: {},
|
137 | type: 'state'
|
138 | };
|
139 |
|
140 | if (isForeignState(id)) {
|
141 | if (allowCreationOfForeignObjects) {
|
142 | adapter.setForeignObject(id, data, _ => adapter.setForeignState(id, val, () => callback && callback(true)));
|
143 | } else {
|
144 | adapter.log.info('Creation of foreign objects is not enabled. You can enable it in the configuration');
|
145 | callback && callback(false);
|
146 | }
|
147 | } else {
|
148 | adapter.setObject(id, data, _ => adapter.setState(id, val, () => callback && callback(true)));
|
149 | }
|
150 | } else {
|
151 | adapter.log.info('Automatic objects creation is not enabled. You can enable it in the node configuration');
|
152 | callback && callback(false);
|
153 | }
|
154 | } else {
|
155 | node._id = obj._id;
|
156 | if (val !== undefined) {
|
157 | adapter.setForeignState(obj._id, val, () => callback && callback(true));
|
158 | } else {
|
159 | callback && callback(true);
|
160 | }
|
161 | }
|
162 | });
|
163 | } else {
|
164 | if (val !== undefined) {
|
165 | adapter.setForeignState(obj._id, val, () => callback && callback(true));
|
166 | } else {
|
167 | callback && callback(true);
|
168 | }
|
169 | }
|
170 | });
|
171 | }
|
172 |
|
173 | function assembleCommon(node, msg, id) {
|
174 | msg = msg || {};
|
175 | const common = {
|
176 | read: true,
|
177 | write: node.objectPreDefinedReadonly,
|
178 | desc: 'Created by Node-Red',
|
179 | role: node.objectPreDefinedRole || msg.stateRole || 'state',
|
180 | name: node.objectPreDefinedName || msg.stateName || id,
|
181 | type: node.objectPreDefinedType || msg.stateType || typeof msg.payload || 'string'
|
182 | };
|
183 | if (msg.stateReadonly !== undefined) {
|
184 | common.write = (msg.stateReadonly === false || msg.stateReadonly === 'false');
|
185 | }
|
186 |
|
187 | if (node.objectPreDefinedUnit || msg.stateUnit) {
|
188 | common.unit = node.objectPreDefinedUnit || msg.stateUnit;
|
189 | }
|
190 | if (node.objectPreDefinedMax || node.objectPreDefinedMax === 0 || msg.stateMax || msg.stateMax === 0) {
|
191 | if (node.objectPreDefinedMax || node.objectPreDefinedMax === 0) {
|
192 | common.max = node.objectPreDefinedMax;
|
193 | } else {
|
194 | common.max = msg.stateMax;
|
195 | }
|
196 | }
|
197 | if (node.objectPreDefinedMin || node.objectPreDefinedMin === 0 || msg.stateMin || msg.stateMin === 0) {
|
198 | if (node.objectPreDefinedMin || node.objectPreDefinedMin === 0) {
|
199 | common.max = node.objectPreDefinedMin;
|
200 | } else {
|
201 | common.max = msg.stateMin;
|
202 | }
|
203 | }
|
204 | return common;
|
205 | }
|
206 |
|
207 | function defineCommon(node, n) {
|
208 | node.autoCreate = n.autoCreate === 'true' || n.autoCreate === true;
|
209 |
|
210 | if (node.autoCreate) {
|
211 | node.objectPreDefinedRole = n.role;
|
212 | node.objectPreDefinedType = n.payloadType;
|
213 | node.objectPreDefinedName = n.stateName || '';
|
214 | node.objectPreDefinedReadonly = (n.readonly === 'false' || n.readonly === false);
|
215 | node.objectPreDefinedUnit = n.stateUnit;
|
216 | node.objectPreDefinedMin = n.stateMin;
|
217 | node.objectPreDefinedMax = n.stateMax;
|
218 | }
|
219 | }
|
220 |
|
221 | function onClose(node) {
|
222 | const pos = existingNodes.indexOf(node);
|
223 | if (pos !== -1) {
|
224 | existingNodes.splice(pos, 1);
|
225 | }
|
226 | node.subscribePattern && adapter.unsubscribeForeignStates(node.subscribePattern);
|
227 | }
|
228 |
|
229 | function IOBrokerInNode(n) {
|
230 | const node = this;
|
231 | RED.nodes.createNode(node, n);
|
232 | node.topic = (n.topic || '*').replace(/\//g, '.');
|
233 |
|
234 | defineCommon(node, n);
|
235 |
|
236 |
|
237 | if (node.topic && !isValidID.test(node.topic) && !node.topic.startsWith(adapter.namespace)) {
|
238 | node.topic = adapter.namespace + '.' + node.topic;
|
239 | }
|
240 | node.subscribePattern = node.topic;
|
241 |
|
242 | node.regexTopic = getRegex(node.topic);
|
243 | node.payloadType = n.payloadType;
|
244 | node.onlyack = n.onlyack === true || n.onlyack === 'true' || false;
|
245 | node.func = n.func || 'all';
|
246 | node.gap = n.gap || '0';
|
247 | node.gap = n.gap || '0';
|
248 | node.fireOnStart = n.fireOnStart === true || n.fireOnStart === 'true' || false;
|
249 |
|
250 | if (node.gap.substr(-1) === '%') {
|
251 | node.pc = true;
|
252 | node.gap = parseFloat(node.gap);
|
253 | }
|
254 | node.g = node.gap;
|
255 |
|
256 | node.previous = {};
|
257 |
|
258 |
|
259 | if (node.topic && !node.topic.includes('*')) {
|
260 | checkState(node, node.topic);
|
261 | }
|
262 |
|
263 | if (ready) {
|
264 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
265 | } else {
|
266 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
267 | }
|
268 |
|
269 | node.stateChange = function (topic, state) {
|
270 | if (node.regexTopic) {
|
271 | if (!node.regexTopic.test(topic)) {
|
272 | return;
|
273 | }
|
274 | } else if (node.topic !== '*' && node.topic !== topic) {
|
275 | return;
|
276 | }
|
277 |
|
278 | if (node.onlyack && state && !state.ack) {
|
279 | return;
|
280 | }
|
281 |
|
282 | const t = topic.replace(/\./g, '/') || '_no_topic';
|
283 |
|
284 |
|
285 | if (node.func === 'rbe') {
|
286 | if (state && state.val === node.previous[t]) {
|
287 | return;
|
288 | }
|
289 | } else if (state && node.func === 'deadband') {
|
290 | const n = parseFloat(state.val.toString());
|
291 | if (!isNaN(n)) {
|
292 |
|
293 | if (node.pc) {
|
294 | node.gap = (node.previous[t] * node.g / 100) || 0;
|
295 | }
|
296 | if (!node.previous.hasOwnProperty(t)) {
|
297 | node.previous[t] = n - node.gap;
|
298 | }
|
299 | if (!Math.abs(n - node.previous[t]) >= node.gap) {
|
300 | return;
|
301 | }
|
302 | } else {
|
303 | node.warn('no number found in value');
|
304 | return;
|
305 | }
|
306 | }
|
307 | node.previous[t] = state ? state.val : null;
|
308 |
|
309 | node.send({
|
310 | topic: t,
|
311 | payload: node.payloadType === 'object' ? state : (!state || state.val === null || state.val === undefined ? '' : (valueConvert ? state.val.toString() : state.val)),
|
312 | acknowledged: state ? state.ack : false,
|
313 | timestamp: state ? state.ts : Date.now(),
|
314 | lastchange: state ? state.lc : Date.now(),
|
315 | from: state ? state.from : ''
|
316 | });
|
317 |
|
318 | if (!state) {
|
319 | node.status({
|
320 | fill: 'red',
|
321 | shape: 'ring',
|
322 | text: 'not exists'
|
323 | });
|
324 | } else {
|
325 | node.status({
|
326 | fill: 'green',
|
327 | shape: 'dot',
|
328 | text: node.payloadType === 'object' ? JSON.stringify(state) : (!state || state.val === null || state.val === undefined ? '' : state.val.toString())
|
329 | });
|
330 | }
|
331 | };
|
332 |
|
333 | if (ready) {
|
334 | adapter.on('stateChange', node.stateChange);
|
335 | node.subscribePattern && adapter.subscribeForeignStates(node.subscribePattern);
|
336 |
|
337 | if (node.fireOnStart && !node.topic.includes('*')) {
|
338 | adapter.getForeignState(node.topic, (err, state) =>
|
339 | node.stateChange(node.topic, state));
|
340 | }
|
341 | }
|
342 |
|
343 | node.on('close', () => {
|
344 | adapter.removeListener('stateChange', node.stateChange);
|
345 | onClose(node);
|
346 | });
|
347 | existingNodes.push(node);
|
348 | }
|
349 | RED.nodes.registerType('ioBroker in', IOBrokerInNode);
|
350 |
|
351 | function IOBrokerOutNode(n) {
|
352 | const node = this;
|
353 | RED.nodes.createNode(node, n);
|
354 | node.topic = n.topic;
|
355 |
|
356 | node.ack = n.ack === 'true' || n.ack === true;
|
357 |
|
358 | defineCommon(node, n);
|
359 |
|
360 | if (ready) {
|
361 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
362 | } else {
|
363 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
364 | }
|
365 |
|
366 | function setState(id, val, ack, callback) {
|
367 | if (node.idChecked) {
|
368 | if (val !== undefined && val !== '__create__') {
|
369 |
|
370 | if (isForeignState(id)) {
|
371 | adapter.setForeignState(id, {val, ack}, callback);
|
372 | } else {
|
373 | adapter.setState(id, {val, ack}, callback);
|
374 | }
|
375 | }
|
376 | } else {
|
377 | checkState(node, id, null, {val, ack}, isOk => callback && callback(!isOk));
|
378 | }
|
379 | }
|
380 |
|
381 | node.on('input', (msg, send, done) => {
|
382 | let id = node.topic;
|
383 | if (!id) {
|
384 | id = msg.topic;
|
385 | }
|
386 |
|
387 | if (id && !isValidID.test(id) && !id.startsWith(adapter.namespace)) {
|
388 | id = adapter.namespace + '.' + id;
|
389 | }
|
390 |
|
391 | if (!ready) {
|
392 |
|
393 | nodeSets.push({node, msg});
|
394 | } else if (id) {
|
395 | id = id.replace(/\//g, '.');
|
396 |
|
397 | if (node.autoCreate && !node.idChecked) {
|
398 | if (!id.includes('*') && isValidID.test(id)) {
|
399 | return checkState(node, id, assembleCommon(node, msg, id), {val: msg.payload, ack: node.ack}, isOk => {
|
400 | if (isOk) {
|
401 | node.status({
|
402 | fill: 'green',
|
403 | shape: 'dot',
|
404 | text: msg.payload === null || msg.payload === undefined ? '' : msg.payload.toString()
|
405 | });
|
406 | } else {
|
407 | node.status({
|
408 | fill: 'red',
|
409 | shape: 'ring',
|
410 | text: 'Cannot set state'
|
411 | });
|
412 | }
|
413 | done();
|
414 | });
|
415 | }
|
416 | }
|
417 |
|
418 | if (isForeignState(id)) {
|
419 |
|
420 | adapter.getForeignObject(id, (err, obj) => {
|
421 | if (!err && obj) {
|
422 | adapter.setForeignState(id, {val: msg.payload, ack: node.ack}, (err, _id) => {
|
423 | if (err) {
|
424 | node.status({
|
425 | fill: 'red',
|
426 | shape: 'ring',
|
427 | text: 'Error on setForeignState. See Log'
|
428 | });
|
429 | log('Error on setState for ' + id + ': ' + err);
|
430 | } else {
|
431 | node.status({
|
432 | fill: 'green',
|
433 | shape: 'dot',
|
434 | text: _id + ': ' + (msg.payload === null || msg.payload === undefined ? '' : msg.payload.toString())
|
435 | });
|
436 | }
|
437 | done();
|
438 | });
|
439 | } else {
|
440 | log('State "' + id + '" does not exist in the ioBroker');
|
441 | node.status({
|
442 | fill: 'red',
|
443 | shape: 'ring',
|
444 | text: 'State "' + id + '" does not exist in the ioBroker'
|
445 | });
|
446 | done();
|
447 | }
|
448 | });
|
449 | } else {
|
450 | if (id.includes('*')) {
|
451 | log('Invalid topic name "' + id + '" for ioBroker');
|
452 | node.status({
|
453 | fill: 'red',
|
454 | shape: 'ring',
|
455 | text: 'Invalid topic name "' + id + '" for ioBroker'
|
456 | });
|
457 | done();
|
458 | } else {
|
459 | setState(id, msg.payload, node.ack, (err, _id) => {
|
460 | if (err) {
|
461 | node.status({
|
462 | fill: 'red',
|
463 | shape: 'ring',
|
464 | text: 'Error on setState. See Log'
|
465 | });
|
466 | log('Error on setState for ' + id + ': ' + err);
|
467 | } else {
|
468 | node.status({
|
469 | fill: 'green',
|
470 | shape: 'dot',
|
471 | text: _id + ': ' + (msg.payload === null || msg.payload === undefined ? '' : msg.payload.toString())
|
472 | });
|
473 | }
|
474 | done();
|
475 | });
|
476 | }
|
477 | }
|
478 | } else {
|
479 | node.warn('No key or topic set');
|
480 | node.status({
|
481 | fill: 'red',
|
482 | shape: 'ring',
|
483 | text: 'No key or topic set'
|
484 | });
|
485 | done();
|
486 | }
|
487 | });
|
488 |
|
489 | node.on('close', () => onClose(node));
|
490 | existingNodes.push(node);
|
491 | }
|
492 | RED.nodes.registerType('ioBroker out', IOBrokerOutNode);
|
493 |
|
494 | function IOBrokerGetNode(n) {
|
495 | const node = this;
|
496 | RED.nodes.createNode(node, n);
|
497 | node.topic = typeof n.topic === 'string' && n.topic.length > 0 ? n.topic.replace(/\//g, '.') : null;
|
498 |
|
499 | defineCommon(node, n);
|
500 |
|
501 |
|
502 | if (node.topic && !isValidID.test(node.topic) && !node.topic.startsWith(adapter.namespace)) {
|
503 | node.topic = adapter.namespace + '.' + node.topic;
|
504 | }
|
505 |
|
506 | node.payloadType = n.payloadType;
|
507 | node.attrname = n.attrname;
|
508 |
|
509 |
|
510 | if (node.topic && !node.topic.includes('*')) {
|
511 | checkState(node, node.topic);
|
512 | }
|
513 |
|
514 | if (ready) {
|
515 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
516 | } else {
|
517 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
518 | }
|
519 |
|
520 | node.getStateValue = function (msg, id) {
|
521 | return function (err, state) {
|
522 | if (!err && state) {
|
523 | msg[node.attrname] = (node.payloadType === 'object') ? state : ((state.val === null || state.val === undefined) ? '' : (valueConvert ? state.val.toString() : state.val));
|
524 | msg.acknowledged = state.ack;
|
525 | msg.timestamp = state.ts;
|
526 | msg.lastchange = state.lc;
|
527 | msg.topic = node.topic || msg.topic;
|
528 | node.status({
|
529 | fill: 'green',
|
530 | shape: 'dot',
|
531 | text: (node.payloadType === 'object') ? JSON.stringify(state) : ((state.val === null || state.val === undefined) ? '' : state.val.toString())
|
532 | });
|
533 | node.send(msg);
|
534 | } else {
|
535 | log('State "' + id + '" does not exist in the ioBroker');
|
536 | }
|
537 | };
|
538 | };
|
539 |
|
540 | node.on('input', msg => {
|
541 | let id = node.topic || msg.topic;
|
542 | if (!ready) {
|
543 | nodeSets.push({node, msg});
|
544 |
|
545 | return;
|
546 | }
|
547 | if (id) {
|
548 | if (id.includes('*')) {
|
549 | log('Invalid topic name "' + id + '" for ioBroker');
|
550 | } else {
|
551 | id = id.replace(/\//g, '.');
|
552 |
|
553 | if (isForeignState(id)) {
|
554 | return adapter.getForeignState(id, node.getStateValue(msg, id));
|
555 | } else {
|
556 | return adapter.getState(id, node.getStateValue(msg, id));
|
557 | }
|
558 | }
|
559 | } else {
|
560 | node.warn('No key or topic set');
|
561 | }
|
562 | });
|
563 |
|
564 | node.on('close', () => onClose(node));
|
565 | existingNodes.push(node);
|
566 | }
|
567 | RED.nodes.registerType('ioBroker get', IOBrokerGetNode);
|
568 |
|
569 | function IOBrokerGetObjectNode(n) {
|
570 | const node = this;
|
571 | RED.nodes.createNode(node, n);
|
572 | node.topic = typeof n.topic === 'string' && n.topic.length > 0 ? n.topic.replace(/\//g, '.') : null;
|
573 |
|
574 | defineCommon(node, n);
|
575 |
|
576 |
|
577 | if (node.topic && !isValidID.test(node.topic) && !node.topic.startsWith(adapter.namespace)) {
|
578 | node.topic = adapter.namespace + '.' + node.topic;
|
579 | }
|
580 | node.attrname = n.attrname;
|
581 |
|
582 |
|
583 | if (node.topic && !node.topic.includes('*')) {
|
584 | checkState(node, node.topic);
|
585 | }
|
586 |
|
587 | if (ready) {
|
588 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
589 | } else {
|
590 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
591 | }
|
592 |
|
593 | node.getObject = function (msg) {
|
594 | return function (err, state) {
|
595 | if (!err && state) {
|
596 | msg[node.attrname] = state;
|
597 | msg.topic = node.topic || msg.topic;
|
598 | node.status({
|
599 | fill: 'green',
|
600 | shape: 'dot',
|
601 | text: JSON.stringify(state)
|
602 | });
|
603 | node.send(msg);
|
604 | } else {
|
605 | log('Object "' + id + '" does not exist in the ioBroker');
|
606 | }
|
607 | };
|
608 | };
|
609 |
|
610 | node.on('input', msg => {
|
611 | let id = node.topic || msg.topic;
|
612 | if (!ready) {
|
613 | nodeSets.push({node, msg});
|
614 |
|
615 | } else if (id) {
|
616 | if (id.includes('*')) {
|
617 | log('Invalid topic name "' + id + '" for ioBroker');
|
618 | } else {
|
619 | id = id.replace(/\//g, '.');
|
620 |
|
621 | if (isForeignState(id)) {
|
622 |
|
623 | return adapter.getForeignObject(id, node.getObject(msg));
|
624 | } else {
|
625 | return adapter.getObject(id, node.getObject(msg));
|
626 | }
|
627 | }
|
628 | } else {
|
629 | node.warn('No key or topic set');
|
630 | }
|
631 | });
|
632 |
|
633 | node.on('close', () => onClose(node));
|
634 | existingNodes.push(node);
|
635 | }
|
636 | RED.nodes.registerType('ioBroker get object', IOBrokerGetObjectNode);
|
637 |
|
638 | function IOBrokerListNode(n) {
|
639 | const node = this;
|
640 | RED.nodes.createNode(node, n);
|
641 | node.topic = typeof n.topic === 'string' && n.topic.length > 0 ? n.topic.replace(/\//g, '.') : null;
|
642 |
|
643 |
|
644 | if (node.topic && !isValidID.test(node.topic) && !node.topic.startsWith(adapter.namespace)) {
|
645 | node.topic = adapter.namespace + '.' + node.topic;
|
646 | }
|
647 | node.objType = n.objType;
|
648 | node.regex = n.regex;
|
649 | node.asArray = n.asArray === 'true' || n.asArray === true;
|
650 | node.onlyIDs = n.onlyIDs === 'true' || n.onlyIDs === true;
|
651 | node.withValues = n.withValues === 'true' || n.withValues === true;
|
652 | if (node.regex) {
|
653 | node.regex = new RegExp(node.regex.replace('\\', '\\\\'));
|
654 | }
|
655 |
|
656 | if (ready) {
|
657 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
658 | } else {
|
659 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
660 | }
|
661 |
|
662 | node.getObject = function (msg) {
|
663 | return function (err, state) {
|
664 | if (!err && state) {
|
665 | msg[node.attrname] = state;
|
666 | node.status({
|
667 | fill: 'green',
|
668 | shape: 'dot',
|
669 | text: JSON.stringify(state)
|
670 | });
|
671 | node.send(msg);
|
672 | } else {
|
673 | log('Object "' + id + '" does not exist in the ioBroker');
|
674 | }
|
675 | };
|
676 | };
|
677 |
|
678 | node.on('input', async msg => {
|
679 | let pattern = node.topic || msg.topic;
|
680 | if (!ready) {
|
681 | nodeSets.push({node, msg});
|
682 | } else if (pattern) {
|
683 | pattern = pattern.replace(/\//g, '.');
|
684 |
|
685 | let list = {};
|
686 |
|
687 |
|
688 | const addRows = rows => {
|
689 | if (rows) {
|
690 | for (let id in rows) {
|
691 | list[id] = rows[id];
|
692 | }
|
693 | }
|
694 | };
|
695 |
|
696 | try {
|
697 | if (!node.objType || node.objType === 'folder') {
|
698 | const folders = await adapter.getForeignObjectsAsync(pattern, 'folder');
|
699 | addRows(folders);
|
700 | }
|
701 | } catch (err) {
|
702 |
|
703 | log('Error while requesting folders: ' + err);
|
704 | }
|
705 | try {
|
706 | if (!node.objType || node.objType === 'device') {
|
707 | const devices = await adapter.getForeignObjectsAsync(pattern, 'device');
|
708 | addRows(devices);
|
709 | }
|
710 | } catch (err) {
|
711 |
|
712 | log('Error while requesting devices: ' + err);
|
713 | }
|
714 | try {
|
715 | if (!node.objType || node.objType === 'channel') {
|
716 | const channels = await adapter.getForeignObjectsAsync(pattern, 'channel');
|
717 | addRows(channels);
|
718 | }
|
719 | } catch (err) {
|
720 |
|
721 | log('Error while requesting channels: ' + err);
|
722 | }
|
723 | try {
|
724 | if (!node.objType || node.objType === 'state') {
|
725 | const states = await adapter.getForeignObjectsAsync(pattern, 'state');
|
726 | addRows(states);
|
727 | }
|
728 | } catch (err) {
|
729 |
|
730 | log('Error while requesting states: ' + err);
|
731 | }
|
732 | try {
|
733 | if (!node.objType || node.objType === 'meta') {
|
734 | const metas = await adapter.getForeignObjectsAsync(pattern, 'meta');
|
735 | addRows(metas);
|
736 | }
|
737 | } catch (err) {
|
738 |
|
739 | log('Error while requesting metas: ' + err);
|
740 | }
|
741 | try {
|
742 | if (!node.objType || node.objType === 'instance') {
|
743 | const instances = await adapter.getForeignObjectsAsync(pattern, 'instance');
|
744 | addRows(instances);
|
745 | }
|
746 | } catch (err) {
|
747 |
|
748 | log('Error while requesting instances: ' + err);
|
749 | }
|
750 | try {
|
751 | if (!node.objType || node.objType === 'adapter') {
|
752 | const adapters = await adapter.getForeignObjectsAsync(pattern, 'adapter');
|
753 | addRows(adapters);
|
754 | }
|
755 | } catch (err) {
|
756 |
|
757 | log('Error while requesting adapters: ' + err);
|
758 | }
|
759 |
|
760 | if (node.regex) {
|
761 | const newList = {};
|
762 | Object.keys(list).forEach(id => {
|
763 | if (node.regex.test(id)) {
|
764 | newList[id] = list[id];
|
765 | }
|
766 | });
|
767 | list = newList;
|
768 | }
|
769 |
|
770 | const ids = Object.keys(list);
|
771 |
|
772 | return adapter.getForeignStatesAsync(!node.withValues ? [] : ids)
|
773 | .then(values => {
|
774 | if (node.asArray) {
|
775 | if (node.onlyIDs) {
|
776 | msg.payload = ids;
|
777 | if (node.withValues) {
|
778 | msg.payload = msg.payload.map(id => {
|
779 | values[id] = values[id] || {};
|
780 | values[id]._id = id;
|
781 | return values[id];
|
782 | });
|
783 | }
|
784 | } else {
|
785 | let newList = [];
|
786 | ids.forEach(id => newList.push(list[id]));
|
787 |
|
788 | node.withValues && newList.forEach(el => Object.assign(el, values[el._id] || {}));
|
789 | msg.payload = newList;
|
790 | }
|
791 | node.send(msg);
|
792 | } else {
|
793 |
|
794 | const _msg = JSON.parse(JSON.stringify(msg));
|
795 | ids.forEach((id, i) => {
|
796 | const __msg = !i ? msg : JSON.parse(JSON.stringify(_msg));
|
797 | __msg.topic = id;
|
798 | if (!node.onlyIDs) {
|
799 | __msg.payload = list[id];
|
800 | }
|
801 |
|
802 | if (node.withValues) {
|
803 | if (typeof __msg.payload !== 'object' || __msg.payload === null) {
|
804 | __msg.payload = {};
|
805 | }
|
806 | Object.assign(__msg.payload, values[id]);
|
807 | }
|
808 | node.send(__msg);
|
809 | });
|
810 | }
|
811 | });
|
812 | } else {
|
813 | node.warn('No pattern set');
|
814 | }
|
815 | });
|
816 |
|
817 | node.on('close', () => onClose(node));
|
818 | existingNodes.push(node);
|
819 | }
|
820 | RED.nodes.registerType('ioBroker list', IOBrokerListNode);
|
821 | };
|