UNPKG

16.4 kBJavaScriptView Raw
1/**
2 * Copyright 2013,2014 IBM Corp.
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 require('events').EventEmitter.prototype._maxListeners = 100;
20 var util = require('util');
21 var utils = require(__dirname + '/../lib/utils');
22 //var redis = require("redis");
23 //var hashFieldRE = /^([^=]+)=(.*)$/;
24 // Get the redis address
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 // name is like system.state, pattern is like "*.state" or "*" or "*system*"
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 // If no adapter prefix, add own adapter prefix
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 // If no wildchars and belongs to this adapter
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 // Create object
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 //node.log ("Function: " + node.func);
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 //node.log('Old Value: ' + node.previous[t] + ' New Value: ' + n);
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 // Create variable if not exists
186 if (node.autoCreate && node.topic) {
187 var id = node.topic.replace(/\//g, '.');
188 // If no wildchars and belongs to this adapter
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 // Create object
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 // Create object
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 // If not this adapter state
272 if (!node.regex.test(id) && id.indexOf('.') !== -1) {
273 // Check if state exists
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 //node.on("close", function() {
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 // If no adapter prefix, add own adapter prefix
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 //node.regex = getRegex(this.topic);
323 node.payloadType = n.payloadType;
324 node.attrname = n.attrname;
325
326 if (node.topic) {
327 var id = node.topic;
328 // If no wildchars and belongs to this adapter
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 // Create object
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 // If not this adapter state
383 if (!node.regex.test(id) && id.indexOf('.') != -1) {
384 // Check if state exists
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