Initial commit

This commit is contained in:
2018-12-20 22:02:07 +08:00
commit 6b4ec16652
32 changed files with 6602 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

+330
View File
@@ -0,0 +1,330 @@
<!--
Copyright 2013-2017 bluefox<dogafox@gmail.com>.
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.
-->
<script type="text/x-red" data-template-name="ioBroker in">
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic" style="width:60%"><input type="button" id="node-input-topic-button" style="display:inline-block;width:10%" value="."/>
</div>
<div id="dialog-select-member" style="display:none"></div>
<div class="form-row">
<label for="node-input-payloadType"><i class="fa fa-envelope"></i> Payload</label>
<select id="node-input-payloadType" style="width:125px !important">
<option value="value">value</option>
<option value="object">object</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-onlyack" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-onlyack" style="width: 70%;">Send only on then ack==true</label>
</div>
<div class="form-row">
<label for="node-input-func"><i class="fa fa-wrench"></i> Mode</label>
<select type="text" id="node-input-func" style="width:74%;">
<option value="all">Send all events</option>
<option value="rbe">block unless value changes</option>
<option value="deadband">block unless changes by more than</option>
</select>
</div>
<div class="form-row" id="node-bandgap">
<label for="node-input-gap">&nbsp;</label>
<input type="text" id="node-input-gap" placeholder="e.g. 10 or 5%" style="width:71%;">
</div>
</script>
<script type="text/x-red" data-help-name="ioBroker in">
<p>ioBroker input node. Connects to a ioBroker and subscribes to the specified topic. The topic may contain redis wildcards (*).</p>
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.timestamp, msg.lastchange and msg.acknowledged.</p>
<p>The checkbox determines whether only States with ack == true or all events are forwarded.</p>
<p>The select box Mode offers further filtering options. The options are the same as for the RBE node.</p>
</script>
<link rel="stylesheet" type="text/css" href="vendor/ui.fancytree.min.css"/>
<style type="text/css">
#dialog-select-member select{
height: 24px !important;
line-height: 24px !important;
}
</style>
<script type="text/javascript" src="vendor/jquery.fancytree-all.min.js"></script>
<script type="text/javascript" src="selectID.js"></script>
<script type="text/javascript">
RED.nodes.registerType('ioBroker in',{
category: 'input',
defaults: {
name: {value:""},
topic: {value:"*",required:true},
payloadType: {value:'value'},
onlyack: {value:""},
func: {value:"all"},
gap: {value:"",validate:RED.validators.regex(/^(\d*[.]*\d*|)(%|)$/)}
},
color:"#a8bfd8",
inputs:0,
outputs:1,
icon: "ioBroker.png",
label: function() {
return this.name||this.topic||"ioBroker";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (!window.__iobroker) {
$.getJSON("iobroker.json", function(data) {
window.__iobroker = data;
$('#dialog-select-member').selectId('init', {
objects: window.__iobroker,
noMultiselect: true,
columns: ['name', 'role', 'enum', 'room'],
states: null,
noImg: true
});
});
} else {
$('#dialog-select-member').selectId('init', {
objects: window.__iobroker,
noMultiselect: true,
columns: ['name', 'role', 'enum', 'room'],
states: null,
noImg: true
});
}
$('#node-input-topic-button').button({
icons: {primary: 'ui-icon-folder-open'},
text: false
}).click(function () {
$('#dialog-select-member').selectId('show', $('#node-input-topic').val(), undefined, function (newId, oldId) {
var oldObj = $('#dialog-select-member').selectId('getInfo', oldId)
var newObj = $('#dialog-select-member').selectId('getInfo', newId)
$('#node-input-topic').val(newId);
var oldName = $('#node-input-name').val();
if (!oldName || !oldObj || (oldObj.common && oldName == oldObj.common.name && newObj.common)) {
$('#node-input-name').val(newObj.common.name);
}
});
});
$("#node-input-func").on("change",function() {
if ($("#node-input-func").val() == null) $("#node-input-func").val ("all");
if ($("#node-input-func").val() === "deadband") {
$("#node-bandgap").show();
} else {
$("#node-bandgap").hide();
}
});
}
});
</script>
<script type="text/x-red" data-template-name="ioBroker out">
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic" style="width:60%"><input type="button" id="node-input-topic-button" style="display:inline-block;width:10%" value="."/>
</div>
<div id="dialog-select-member" style="display:none"></div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-ack"><i class="fa fa-arrow-up"></i> Type</label>
<select id="node-input-ack" style="width:73% !important">
<option value="true">value</option>
<option value="false">command</option>
</select>
</div>
<div class="form-row">
<label for="node-input-autoCreate"><i class="fa fa-arrow-up"></i> Auto create</label>
<select id="node-input-autoCreate" style="width:73% !important">
<option value="true">Create states if not exist</option>
<option value="false">Ignore messages for non existing states</option>
</select>
</div>
<div class="form-tips">Tip: Leave topic blank if you want to set them via msg properties.</div>
</script>
<script type="text/x-red" data-help-name="ioBroker out">
<p>Connects to a ioBroker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> or to the topic specified in the edit window. The value in the edit window has precedence.</p>
<p>If <b>msg.payload</b> contains an object it will be stringified before being sent.</p>
<p>If <b>msg.payload</b> contains "__create__", the object will be only created, but no value will be written.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('ioBroker out',{
category: 'output',
defaults: {
name: {value:""},
topic: {value:""},
ack: {value:"false"},
autoCreate: {value: "false"}
},
color:"#a8bfd8",
inputs:1,
outputs:0,
icon: "ioBroker.png",
align: "right",
label: function() {
return this.name||this.topic||"ioBroker";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (!window.__iobroker) {
$.getJSON("iobroker.json", function(data) {
window.__iobroker = data;
$('#dialog-select-member').selectId('init', {
objects: window.__iobroker,
noMultiselect: true,
columns: ['name', 'role', 'enum', 'room'],
states: null,
noImg: true
});
});
} else {
$('#dialog-select-member').selectId('init', {
objects: window.__iobroker,
noMultiselect: true,
columns: ['name', 'role', 'enum', 'room'],
states: null,
noImg: true
});
}
$('#node-input-topic-button').button({
icons: {primary: 'ui-icon-folder-open'},
text: false
}).click(function () {
$('#dialog-select-member').selectId('show', $('#node-input-topic').val(), undefined, function (newId, oldId) {
var oldObj = $('#dialog-select-member').selectId('getInfo', oldId);
var newObj = $('#dialog-select-member').selectId('getInfo', newId);
$('#node-input-topic').val(newId);
var oldName = $('#node-input-name').val();
if (!oldName || !oldObj || (oldObj.common && oldName == oldObj.common.name && newObj.common)) {
$('#node-input-name').val(newObj.common.name);
}
});
});
}
});
</script>
<script type="text/x-red" data-template-name="ioBroker get">
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic" style="width:60%"><input type="button" id="node-input-topic-button" style="display:inline-block;width:10%" value="."/>
</div>
<div id="dialog-select-member" style="display:none"></div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-payloadType"><i class="fa fa-envelope"></i> Payload</label>
<select id="node-input-payloadType" style="width:125px !important">
<option value="value">value</option>
<option value="object">object</option>
</select>
</div>
<div class="form-row">
<label for="node-input-attrname"><i class="fa fa-tag"></i> Attribute</label>
<input type="text" id="node-input-attrname" placeholder="attrname">
</div>
<div class="form-tips">Tip: Leave topic blank if you want to set them via msg properties.</div>
</script>
<script type="text/x-red" data-help-name="ioBroker get">
<p>Connects to a ioBroker and returns the requested value or the object in the massage attribute in the properties dialog, e.g. <b>msg.payload</b>. The object could be identified either by the <b>msg.topic</b> or specified in the poperties dialog. The value in the poperties dialog has precedence.</p>
<p>The msg object also contains the attributs <b>msg.timestamp</b>, <b>msg.lastchange</b> and <b>msg.acknowledged</b>. All other attributes ob the input msg object will be passed to the output msg object.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('ioBroker get',{
category: 'function',
defaults: {
name: {value:""},
topic: {value:""},
attrname: {value:"payload"},
payloadType: {value:'value'}
},
color:"#a8bfd8",
inputs:1,
outputs:1,
icon: "ioBroker.png",
label: function() {
return this.name||this.topic||"ioBroker get";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (!window.__iobroker) {
$.getJSON("iobroker.json", function(data) {
window.__iobroker = data;
$('#dialog-select-member').selectId('init', {
objects: window.__iobroker,
noMultiselect: true,
columns: ['name', 'role', 'enum', 'room'],
states: null,
noImg: true
});
});
} else {
$('#dialog-select-member').selectId('init', {
objects: window.__iobroker,
noMultiselect: true,
columns: ['name', 'role', 'enum', 'room'],
states: null,
noImg: true
});
}
$('#node-input-topic-button').button({
icons: {primary: 'ui-icon-folder-open'},
text: false
}).click(function () {
$('#dialog-select-member').selectId('show', $('#node-input-topic').val(), undefined, function (newId, oldId) {
var oldObj = $('#dialog-select-member').selectId('getInfo', oldId);
var newObj = $('#dialog-select-member').selectId('getInfo', newId);
$('#node-input-topic').val(newId);
var oldName = $('#node-input-name').val();
if (!oldName || !oldObj || (oldObj.common && oldName == oldObj.common.name && newObj.common)) {
$('#node-input-name').val(newObj.common.name);
}
});
});
}
});
</script>
+389
View File
@@ -0,0 +1,389 @@
/**
* 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('iobrokerInstance') || 0;
var config = settings.get('iobrokerConfig');
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 IOBrokerInNode) {
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 ioBroker');
});
// 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 ioBroker 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 IOBrokerInNode(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('ioBroker in', IOBrokerInNode);
function IOBrokerOutNode(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 ioBroker 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 ioBroker');
}
});
} else {
if (id.indexOf('*') !== -1) {
log('Invalid topic name "' + id + '" for ioBroker');
} 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('ioBroker out', IOBrokerOutNode);
function IOBrokerGetNode(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 ioBroker');
}
};
};
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 ioBroker 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 ioBroker');
} else {
adapter.getState(id, node.getStateValue(msg));
}
}
} else {
node.warn('No key or topic set');
}
});
if (!ready) {
nodes.push(node);
}
}
RED.nodes.registerType('ioBroker get', IOBrokerGetNode);
};