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 | require('events').EventEmitter.prototype._maxListeners = 100;
|
20 | var util = require('util');
|
21 | var utils = require(__dirname + '/../lib/utils');
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | var settings = require(process.env.NODE_RED_HOME + '/red/red').settings;
|
27 | var instance = settings.get('iobrokerInstance') || 0;
|
28 | var config = settings.get('iobrokerConfig');
|
29 | var valueConvert = settings.get('valueConvert');
|
30 | if (typeof config == 'string') {
|
31 | config = JSON.parse(config);
|
32 | }
|
33 |
|
34 | try {
|
35 | var adapter = utils.adapter({name: 'node-red', instance: instance, config: config});
|
36 | } catch(e) {
|
37 | console.log(e);
|
38 | }
|
39 | var nodes = [];
|
40 | var ready = false;
|
41 |
|
42 | adapter.on('ready', function () {
|
43 | ready = true;
|
44 | adapter.subscribeForeignStates('*');
|
45 | while (nodes.length) {
|
46 | var node = nodes.pop();
|
47 | if (node instanceof IOBrokerInNode)
|
48 | adapter.on('stateChange', node.stateChange);
|
49 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
50 | }
|
51 | });
|
52 |
|
53 |
|
54 | function getRegex(pattern) {
|
55 | if (!pattern || pattern === '*') return null;
|
56 | if (pattern.indexOf('*') === -1) return null;
|
57 | if (pattern[pattern.length - 1] !== '*') pattern = pattern + '$';
|
58 | if (pattern[0] !== '*') pattern = '^' + pattern;
|
59 | pattern = pattern.replace(/\*/g, '[a-zA-Z0-9.\s]');
|
60 | return new RegExp(pattern);
|
61 | }
|
62 |
|
63 | function IOBrokerInNode(n) {
|
64 | var node = this;
|
65 | RED.nodes.createNode(node,n);
|
66 | node.topic = (n.topic || '*').replace(/\//g, '.');
|
67 | node.regex = new RegExp('^node-red\\.' + instance + '\\.');
|
68 |
|
69 |
|
70 | if (node.topic && node.topic.indexOf('.') === -1) {
|
71 | node.topic = adapter.namespace + '.' + node.topic;
|
72 | }
|
73 |
|
74 | node.regexTopic = getRegex(this.topic);
|
75 | node.payloadType = n.payloadType;
|
76 | node.onlyack = (n.onlyack == true || false);
|
77 | node.func = n.func || 'all';
|
78 | node.gap = n.gap || '0';
|
79 | node.pc = false;
|
80 | if (node.gap.substr(-1) === '%') {
|
81 | node.pc = true;
|
82 | node.gap = parseFloat(node.gap);
|
83 | }
|
84 | node.g = node.gap;
|
85 |
|
86 | node.previous = {};
|
87 |
|
88 | if (node.topic) {
|
89 | var id = node.topic;
|
90 |
|
91 | if (id.indexOf('*') === -1 && (node.regex.test(id) || id.indexOf('.') !== -1)) {
|
92 | adapter.getObject(id, function (err, obj) {
|
93 | if (!obj) {
|
94 | if (adapter.log) {
|
95 | adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
96 | } else {
|
97 | console.log(('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)));
|
98 | }
|
99 |
|
100 | adapter.setObject(id, {
|
101 | common: {
|
102 | name: id,
|
103 | role: 'info'
|
104 | },
|
105 | native: {},
|
106 | type: 'state'
|
107 | }, function (err, obj) {
|
108 | adapter.setState(id, undefined);
|
109 | });
|
110 | }
|
111 | });
|
112 | }
|
113 | }
|
114 |
|
115 | if (ready) {
|
116 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
117 | } else {
|
118 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
119 | }
|
120 |
|
121 | node.stateChange = function(topic, obj) {
|
122 | if (node.regexTopic) {
|
123 | if (!node.regexTopic.test(topic)) return;
|
124 | } else if (node.topic !== '*' && node.topic != topic) {
|
125 | return;
|
126 | }
|
127 |
|
128 | if (node.onlyack && obj.ack != true) return;
|
129 |
|
130 | var t = topic.replace(/\./g, '/') || '_no_topic';
|
131 |
|
132 |
|
133 | if (node.func === 'rbe') {
|
134 | if (obj.val === node.previous[t]) {
|
135 | return;
|
136 | }
|
137 | }
|
138 | else if (node.func === 'deadband') {
|
139 | var n = parseFloat(obj.val.toString());
|
140 | if (!isNaN(n)) {
|
141 |
|
142 | if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
|
143 | if (!node.previous.hasOwnProperty(t)) { node.previous[t] = n - node.gap; }
|
144 | if (!Math.abs(n - node.previous[t]) >= node.gap) {
|
145 | return;
|
146 | }
|
147 | } else {
|
148 | node.warn('no number found in value');
|
149 | return;
|
150 | }
|
151 | }
|
152 | node.previous[t] = obj.val;
|
153 |
|
154 | node.send({
|
155 | topic: t,
|
156 | payload: (node.payloadType === 'object') ? obj : ((obj.val === null || obj.val === undefined) ? '' : (valueConvert ? obj.val.toString() : obj.val)),
|
157 | acknowledged:obj.ack,
|
158 | timestamp: obj.ts,
|
159 | lastchange: obj.lc,
|
160 | from: obj.from
|
161 | });
|
162 | };
|
163 |
|
164 | node.on('close', function() {
|
165 | adapter.removeListener('stateChange', node.stateChange);
|
166 | });
|
167 |
|
168 | if (ready) {
|
169 | adapter.on('stateChange', node.stateChange);
|
170 | } else {
|
171 | nodes.push(node);
|
172 | }
|
173 | }
|
174 | RED.nodes.registerType('ioBroker in', IOBrokerInNode);
|
175 |
|
176 | function IOBrokerOutNode(n) {
|
177 | var node = this;
|
178 | RED.nodes.createNode(node,n);
|
179 | node.topic = n.topic;
|
180 |
|
181 | node.ack = (n.ack === 'true' || n.ack === true);
|
182 | node.autoCreate = (n.autoCreate === 'true' || n.autoCreate === true);
|
183 | node.regex = new RegExp('^node-red\\.' + instance + '\\.');
|
184 |
|
185 |
|
186 | if (node.autoCreate && node.topic) {
|
187 | var id = node.topic.replace(/\//g, '.');
|
188 |
|
189 | if (id.indexOf('*') === -1 && (node.regex.test(id) || id.indexOf('.') !== -1)) {
|
190 | adapter.getObject(node.topic, function (err, obj) {
|
191 | if (!obj) {
|
192 | if (adapter.log) {
|
193 | adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
194 | } else {
|
195 | console.log('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
196 | }
|
197 |
|
198 | adapter.setObject(id, {
|
199 | common: {
|
200 | name: id,
|
201 | role: 'info'
|
202 | },
|
203 | native: {},
|
204 | type: 'state'
|
205 | }, function (err, obj) {
|
206 | adapter.setState(id, undefined);
|
207 | node.idChecked = true;
|
208 | });
|
209 | } else {
|
210 | node.idChecked = true;
|
211 | }
|
212 | });
|
213 | }
|
214 | }
|
215 |
|
216 | if (ready) {
|
217 | node.status({fill: 'green', shape: 'dot', text: 'connected'});
|
218 | } else {
|
219 | node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
|
220 | }
|
221 |
|
222 | function setState(id, val, ack) {
|
223 | if (id == node.topic && node.idChecked) {
|
224 | if (val != '__create__') {
|
225 | adapter.setState(id, {val: val, ack: ack});
|
226 | }
|
227 | } else {
|
228 | adapter.getObject(id, function (err, obj) {
|
229 | if (!obj) {
|
230 | if (!node.autoCreate) {
|
231 | if (adapter.log) {
|
232 | adapter.log.warn('State "' + id + '" does not exist in the ioBroker.');
|
233 | } else {
|
234 | console.log('State "' + id + '" does not exist in the ioBroker.');
|
235 | }
|
236 | return;
|
237 | }
|
238 | if (adapter.log) {
|
239 | adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
240 | } else {
|
241 | console.log('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
242 | }
|
243 |
|
244 | adapter.setObject(id, {
|
245 | common: {
|
246 | name: id,
|
247 | role: 'info'
|
248 | },
|
249 | native: {},
|
250 | type: 'state'
|
251 | }, function (err, obj) {
|
252 | if (val != '__create__') {
|
253 | adapter.setState(id, {val: val, ack: ack});
|
254 | } else {
|
255 | adapter.setState(id, {val: undefined, ack: ack});
|
256 | }
|
257 | });
|
258 | } else {
|
259 | if (val != '__create__') {
|
260 | adapter.setState(id, {val: val, ack: ack});
|
261 | }
|
262 | }
|
263 | });
|
264 | }
|
265 | }
|
266 |
|
267 | node.on('input', function(msg) {
|
268 | var id = node.topic || msg.topic;
|
269 | if (id) {
|
270 | id = id.replace(/\//g, '.');
|
271 |
|
272 | if (!node.regex.test(id) && id.indexOf('.') !== -1) {
|
273 |
|
274 | adapter.getForeignState(id, function (err, state) {
|
275 | if (!err && state) {
|
276 | adapter.setForeignState(id, {val: msg.payload, ack: node.ack});
|
277 | } else {
|
278 | if (adapter.log) {
|
279 | adapter.log.warn('State "' + id + '" does not exist in the ioBroker');
|
280 | } else {
|
281 | console.log('State "' + id + '" does not exist in the ioBroker')
|
282 | }
|
283 | }
|
284 | });
|
285 | } else {
|
286 | if (id.indexOf('*') !== -1) {
|
287 | if (adapter.log) {
|
288 | adapter.log.warn('Invalid topic name "' + id + '" for ioBroker');
|
289 | } else {
|
290 | console.log('Invalid topic name "' + id + '" for ioBroker');
|
291 | }
|
292 | } else {
|
293 | setState(id, msg.payload, node.ack);
|
294 | }
|
295 | }
|
296 | } else {
|
297 | node.warn('No key or topic set');
|
298 | }
|
299 | });
|
300 | if (!ready) {
|
301 | nodes.push(node);
|
302 | }
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 | }
|
309 | RED.nodes.registerType('ioBroker out', IOBrokerOutNode);
|
310 |
|
311 | function IOBrokerGetNode(n) {
|
312 | var node = this;
|
313 | RED.nodes.createNode(node,n);
|
314 | node.topic = (typeof n.topic=== 'string' && n.topic.length > 0 ? n.topic.replace(/\//g, '.') : null) ;
|
315 |
|
316 |
|
317 | if (node.topic && node.topic.indexOf('.') === -1) {
|
318 | node.topic = adapter.namespace + '.' + node.topic;
|
319 | }
|
320 |
|
321 | node.regex = new RegExp('^node-red\\.' + instance + '\\.');
|
322 |
|
323 | node.payloadType = n.payloadType;
|
324 | node.attrname = n.attrname;
|
325 |
|
326 | if (node.topic) {
|
327 | var id = node.topic;
|
328 |
|
329 | if (id.indexOf('*') === -1 && (node.regex.test(id) || id.indexOf('.') !== -1)) {
|
330 | adapter.getObject(id, function (err, obj) {
|
331 | if (!obj) {
|
332 | if (adapter.log) {
|
333 | adapter.log.warn('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id));
|
334 | } else {
|
335 | console.log(('State "' + id + '" was created in the ioBroker as ' + adapter._fixId(id)));
|
336 | }
|
337 |
|
338 | adapter.setObject(id, {
|
339 | common: {
|
340 | name: id,
|
341 | role: 'info'
|
342 | },
|
343 | native: {},
|
344 | type: 'state'
|
345 | }, function (err, obj) {
|
346 | adapter.setState(id, undefined);
|
347 | });
|
348 | }
|
349 | });
|
350 | }
|
351 | }
|
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 | node.getStateValue = function (err, state) {
|
360 | if (!err && state) {
|
361 | node.msg [node.attrname]= (node.payloadType === 'object') ? state : ((state.val === null || state.val === undefined) ? '' : (valueConvert ? state.val.toString() : state.val));
|
362 | node.msg.acknowledged=state.ack;
|
363 | node.msg.timestamp=state.ts;
|
364 | node.msg.lastchange=state.lc;
|
365 | node.send (node.msg);
|
366 |
|
367 | } else {
|
368 | if (adapter.log) {
|
369 | adapter.log.warn('State "' + id + '" does not exist in the ioBroker');
|
370 | } else {
|
371 | console.log('State "' + id + '" does not exist in the ioBroker')
|
372 | }
|
373 |
|
374 | }
|
375 | };
|
376 |
|
377 | node.on('input', function(msg) {
|
378 | var id = node.topic || msg.topic;
|
379 | node.msg = msg;
|
380 | if (id) {
|
381 | id = id.replace(/\//g, '.');
|
382 |
|
383 | if (!node.regex.test(id) && id.indexOf('.') != -1) {
|
384 |
|
385 |
|
386 | adapter.getForeignState(id, node.getStateValue);
|
387 | } else {
|
388 | if (id.indexOf('*') != -1) {
|
389 | if (adapter.log) {
|
390 | adapter.log.warn('Invalid topic name "' + id + '" for ioBroker');
|
391 | } else {
|
392 | console.log('Invalid topic name "' + id + '" for ioBroker');
|
393 | }
|
394 | } else {
|
395 | adapter.getState(id, node.getStateValue);
|
396 | }
|
397 | }
|
398 | } else {
|
399 | node.warn('No key or topic set');
|
400 | }
|
401 | });
|
402 |
|
403 | if (!ready) {
|
404 | nodes.push(node);
|
405 | }
|
406 |
|
407 | }
|
408 | RED.nodes.registerType('ioBroker get', IOBrokerGetNode);
|
409 | };
|
410 |
|