UNPKG

33.4 kBJavaScriptView Raw
1/**
2 * Copyright 2014-2020 bluefox <dogafox@gmail.com>.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 **/
16
17module.exports = function(RED) {
18 'use strict';
19 // patch event emitter
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 // name is like system.state, pattern is like "*.state" or "*" or "*system*"
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 // check if object exists and sets its value if provided
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 // If not exists
130 if (!obj) {
131 if (common) {
132 log('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
133 // Create object
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 // If no adapter prefix, add own adapter prefix
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 // Create ID if not exits
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 //node.log ("Function: " + node.func);
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 //node.log('Old Value: ' + node.previous[t] + ' New Value: ' + n);
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 // If not this adapter state
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 // if not starts with adapter.instance.
387 if (id && !isValidID.test(id) && !id.startsWith(adapter.namespace)) {
388 id = adapter.namespace + '.' + id;
389 }
390
391 if (!ready) {
392 //log('Message for "' + id + '" queued because ioBroker connection not initialized');
393 nodeSets.push({node, msg});
394 } else if (id) {
395 id = id.replace(/\//g, '.');
396 // Create variable if not exists
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 // If not this adapter state
418 if (isForeignState(id)) {
419 // Check if state exists
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 // If no adapter prefix, add own adapter prefix
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 // Create ID if not exits
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 //log('Message for "' + id + '" queued because ioBroker connection not initialized');
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 // If not this adapter state
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 // If no adapter prefix, add own adapter prefix
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 // Create ID if not exits
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 //log('Message for "' + id + '" queued because ioBroker connection not initialized');
615 } else if (id) {
616 if (id.includes('*')) {
617 log('Invalid topic name "' + id + '" for ioBroker');
618 } else {
619 id = id.replace(/\//g, '.');
620 // If not this adapter state
621 if (isForeignState(id)) {
622 // Check if state exists
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 // If no adapter prefix, add own adapter prefix
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 // Adds result rows to the return object
687 /** @param {any[] | undefined} rows */
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 /* ignore, we'll return what we get till now */
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 /* ignore, we'll return what we get till now */
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 /* ignore, we'll return what we get till now */
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 /* ignore, we'll return what we get till now */
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 /* ignore, we'll return what we get till now */
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 /* ignore, we'll return what we get till now */
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 /* ignore, we'll return what we get till now */
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 // Add states values if required
788 node.withValues && newList.forEach(el => Object.assign(el, values[el._id] || {}));
789 msg.payload = newList;
790 }
791 node.send(msg);
792 } else {
793 // every ID as one message
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 // Add states values if required
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};