You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
15 KiB

/**
* Copyright 2013,2014 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
module.exports = function(RED) {
'use strict';
require('events').EventEmitter.prototype._maxListeners = 100;
var util = require('util');
var utils = require(__dirname + '/../lib/utils');
//var redis = require("redis");
//var hashFieldRE = /^([^=]+)=(.*)$/;
// Get the redis address
var settings = require(process.env.NODE_RED_HOME + '/red/red').settings;
var instance = settings.get('yunkong2Instance') || 0;
var config = settings.get('yunkong2Config');
var valueConvert = settings.get('valueConvert');
if (typeof config == 'string') {
config = JSON.parse(config);
}
var adapter;
try {
adapter = utils.Adapter({name: 'node-red', instance: instance, config: config});
} catch(e) {
console.log(e);
}
var nodes = [];
var nodeSets = [];
var ready = false;
var log = adapter && adapter.log && adapter.log.warn ? adapter.log.warn : console.log;
adapter.on('ready', function () {
ready = true;
adapter.subscribeForeignStates('*');
while (nodes.length) {
var node = nodes.pop();
if (node instanceof yunkong2InNode) {
adapter.on('stateChange', node.stateChange);
}
node.status({fill: 'green', shape: 'dot', text: 'connected'});
}
var count = 0;
while (nodeSets.length) {
var nodeSetData = nodeSets.pop();
nodeSetData.node.emit('input', nodeSetData.msg);
count++;
}
if (count > 0) log(count + ' queued state values set in yunkong2');
});
// name is like system.state, pattern is like "*.state" or "*" or "*system*"
function getRegex(pattern) {
if (!pattern || pattern === '*') return null;
if (pattern.indexOf('*') === -1) return null;
if (pattern[pattern.length - 1] !== '*') pattern = pattern + '$';
if (pattern[0] !== '*') pattern = '^' + pattern;
pattern = pattern.replace(/\*/g, '[a-zA-Z0-9.\s]');
pattern = pattern.replace(/\./g, '\\.');
return new RegExp(pattern);
}
function checkState(node, id, val, callback) {
if (node.idChecked) {
return callback && callback();
}
if (node.topic) {
node.idChecked = true;
}
adapter.getObject(id, function (err, obj) {
if (!obj) {
adapter.getForeignObject(id, function (err, obj) {
if (!obj) {
log('State "' + id + '" was created in the yunkong2 as ' + adapter._fixId(id));
// Create object
adapter.setObject(id, {
common: {
name: id,
role: 'info',
type: 'state',
desc: 'Created by Node-Red'
},
native: {},
type: 'state'
}, function (err) {
if (val !== undefined && val !== null && val !== '__create__') {
adapter.setState(id, val, function () {
callback && callback();
});
} else {
adapter.setState(id, undefined, function () {
callback && callback();
});
}
});
} else {
node._id = obj._id;
if (val !== undefined && val !== null && val !== '__create__') {
adapter.setForeignState(obj._id, val, function () {
callback && callback();
});
} else {
callback && callback();
}
}
});
} else {
if (val !== undefined && val !== null && val !== '__create__') {
adapter.setForeignState(obj._id, val, function () {
callback && callback();
});
} else {
callback && callback();
}
}
});
}
function yunkong2InNode(n) {
var node = this;
RED.nodes.createNode(node,n);
node.topic = (n.topic || '*').replace(/\//g, '.');
node.regex = new RegExp('^node-red\\.' + instance + '\\.');
// If no adapter prefix, add own adapter prefix
if (node.topic && node.topic.indexOf('.') === -1) {
node.topic = adapter.namespace + '.' + node.topic;
}
node.regexTopic = getRegex(this.topic);
node.payloadType = n.payloadType;
node.onlyack = (n.onlyack == true || false);
node.func = n.func || 'all';
node.gap = n.gap || '0';
node.pc = false;
if (node.gap.substr(-1) === '%') {
node.pc = true;
node.gap = parseFloat(node.gap);
}
node.g = node.gap;
node.previous = {};
if (node.topic) {
var id = node.topic;
// If no wildchars and belongs to this adapter
if (id.indexOf('*') === -1 && (node.regex.test(id) || id.indexOf('.') !== -1)) {
checkState(node, id);
}
}
if (ready) {
node.status({fill: 'green', shape: 'dot', text: 'connected'});
} else {
node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
}
node.stateChange = function(topic, obj) {
if (node.regexTopic) {
if (!node.regexTopic.test(topic)) return;
} else if (node.topic !== '*' && node.topic !== topic) {
return;
}
if (node.onlyack && obj.ack != true) return;
var t = topic.replace(/\./g, '/') || '_no_topic';
//node.log ("Function: " + node.func);
if (node.func === 'rbe') {
if (obj.val === node.previous[t]) {
return;
}
} else if (node.func === 'deadband') {
var n = parseFloat(obj.val.toString());
if (!isNaN(n)) {
//node.log('Old Value: ' + node.previous[t] + ' New Value: ' + n);
if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
if (!node.previous.hasOwnProperty(t)) {
node.previous[t] = n - node.gap;
}
if (!Math.abs(n - node.previous[t]) >= node.gap) {
return;
}
} else {
node.warn('no number found in value');
return;
}
}
node.previous[t] = obj.val;
node.send({
topic: t,
payload: (node.payloadType === 'object') ? obj : ((obj.val === null || obj.val === undefined) ? '' : (valueConvert ? obj.val.toString() : obj.val)),
acknowledged:obj.ack,
timestamp: obj.ts,
lastchange: obj.lc,
from: obj.from
});
node.status({fill: 'green', shape: 'dot', text: (node.payloadType === 'object') ? JSON.stringify(obj) : ((obj.val === null || obj.val === undefined) ? '' : obj.val.toString() ) });
};
node.on('close', function() {
adapter.removeListener('stateChange', node.stateChange);
});
if (ready) {
adapter.on('stateChange', node.stateChange);
} else {
nodes.push(node);
}
}
RED.nodes.registerType('yunkong2 in', yunkong2InNode);
function yunkong2OutNode(n) {
var node = this;
RED.nodes.createNode(node,n);
node.topic = n.topic;
node.ack = (n.ack === 'true' || n.ack === true);
node.autoCreate = (n.autoCreate === 'true' || n.autoCreate === true);
node.regex = new RegExp('^node-red\\.' + instance + '\\.');
if (ready) {
node.status({fill: 'green', shape: 'dot', text: 'connected'});
} else {
node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
}
function setState(id, val, ack) {
if (node.idChecked) {
if (val !== '__create__') {
adapter.setState(id, {val: val, ack: ack});
}
} else {
checkState(node, id, {val: val, ack: ack});
}
}
node.on('input', function(msg) {
var id = node.topic || msg.topic;
if (!ready) {
nodeSets.push({'node': node, 'msg': msg});
//log('Message for "' + id + '" queued because yunkong2 connection not initialized');
return;
}
if (id) {
id = id.replace(/\//g, '.');
// Create variable if not exists
if (node.autoCreate && !node.idChecked) {
id = id.replace(/\//g, '.');
// If no wildchars and belongs to this adapter
if (id.indexOf('*') === -1 && (node.regex.test(id) || id.indexOf('.') !== -1)) {
checkState(node, id);
}
}
// If not this adapter state
if (!node.regex.test(id) && id.indexOf('.') !== -1) {
// Check if state exists
adapter.getForeignState(id, function (err, state) {
if (!err && state) {
adapter.setForeignState(id, {val: msg.payload, ack: node.ack});
node.status({fill: 'green', shape: 'dot', text: msg.payload.toString() });
} else {
log('State "' + id + '" does not exist in the yunkong2');
}
});
} else {
if (id.indexOf('*') !== -1) {
log('Invalid topic name "' + id + '" for yunkong2');
} else {
setState(id, msg.payload, node.ack);
node.status({fill: 'green', shape: 'dot', text: msg.payload.toString() });
}
}
} else {
node.warn('No key or topic set');
}
});
if (!ready) {
nodes.push(node);
}
//node.on("close", function() {
//
// });
}
RED.nodes.registerType('yunkong2 out', yunkong2OutNode);
function yunkong2GetNode(n) {
var node = this;
RED.nodes.createNode(node,n);
node.topic = (typeof n.topic=== 'string' && n.topic.length > 0 ? n.topic.replace(/\//g, '.') : null) ;
// If no adapter prefix, add own adapter prefix
if (node.topic && node.topic.indexOf('.') === -1) {
node.topic = adapter.namespace + '.' + node.topic;
}
node.regex = new RegExp('^node-red\\.' + instance + '\\.');
//node.regex = getRegex(this.topic);
node.payloadType = n.payloadType;
node.attrname = n.attrname;
if (node.topic) {
var id = node.topic;
// If no wildchars and belongs to this adapter
if (id.indexOf('*') === -1 && (node.regex.test(id) || id.indexOf('.') !== -1)) {
checkState(node, id);
}
}
if (ready) {
node.status({fill: 'green', shape: 'dot', text: 'connected'});
} else {
node.status({fill: 'red', shape: 'ring', text: 'disconnected'}, true);
}
node.getStateValue = function (msg) {
return function (err, state) {
if (!err && state) {
msg[node.attrname] = (node.payloadType === 'object') ? state : ((state.val === null || state.val === undefined) ? '' : (valueConvert ? state.val.toString() : state.val));
msg.acknowledged = state.ack;
msg.timestamp = state.ts;
msg.lastchange = state.lc;
node.status({
fill: 'green',
shape: 'dot',
text: (node.payloadType === 'object') ? JSON.stringify(state) : ((state.val === null || state.val === undefined) ? '' : state.val.toString())
});
node.send(msg);
} else {
log('State "' + id + '" does not exist in the yunkong2');
}
};
};
node.on('input', function(msg) {
var id = node.topic || msg.topic;
if (!ready) {
nodeSets.push({'node': node, 'msg': msg});
//log('Message for "' + id + '" queued because yunkong2 connection not initialized');
return;
}
if (id) {
id = id.replace(/\//g, '.');
// If not this adapter state
if (!node.regex.test(id) && id.indexOf('.') !== -1) {
// Check if state exists
adapter.getForeignState(id, node.getStateValue(msg));
} else {
if (id.indexOf('*') !== -1) {
log('Invalid topic name "' + id + '" for yunkong2');
} else {
adapter.getState(id, node.getStateValue(msg));
}
}
} else {
node.warn('No key or topic set');
}
});
if (!ready) {
nodes.push(node);
}
}
RED.nodes.registerType('yunkong2 get', yunkong2GetNode);
};