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