Merge remote-tracking branch 'upstream/master'

Conflicts:
	social/xmpp/92-xmpp.html
	social/xmpp/92-xmpp.js
This commit is contained in:
Ben Hardill 2014-06-19 23:52:40 +01:00
commit 42ad8920cb
24 changed files with 1081 additions and 235 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 719 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -19,14 +19,12 @@ var util = require("util");
var exec = require('child_process').exec; var exec = require('child_process').exec;
var fs = require('fs'); var fs = require('fs');
if (!fs.existsSync("/usr/local/bin/gpio")) { if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi
exec("cat /proc/cpuinfo | grep BCM27",function(err,stdout,stderr) { throw "Info : Ignoring Raspberry Pi specific node.";
if (stdout.indexOf('BCM27') > -1) { }
util.log('[37-rpi-piface.js] Error: Cannot find Wiring-Pi "gpio" command');
} if (!fs.existsSync("/usr/local/bin/gpio")) { // gpio command not installed
// else not on a Pi so don't worry anyone with needless messages. throw "Info : Can't find Raspberry Pi wiringPi gpio command.";
});
return;
} }
// Map names of pins to Gordon's gpio PiFace pin numbers // Map names of pins to Gordon's gpio PiFace pin numbers
@ -109,13 +107,16 @@ function PiFACEInNode(n) {
} }
} }
}); });
}, 250); }, 200);
} }
}); });
} }
else { else {
node.error("Invalid PiFACE pin: "+node.pin); node.error("Invalid PiFACE pin: "+node.pin);
} }
node.on("close", function() {
clearInterval(node._interval);
});
} }
function PiFACEOutNode(n) { function PiFACEOutNode(n) {
@ -141,21 +142,10 @@ function PiFACEOutNode(n) {
} }
exec("gpio load spi",function(err,stdout,stderr) { exec("gpio load spi",function(err,stdout,stderr) {
if (err) { if (err) {
util.log('[37-rpi-piface.js] Error: "gpio load spi" command failed for some reason.'); util.log('[37-rpi-piface.js] Error: "gpio load spi" command failed for some reason.');
} }
exec("gpio -p reset",function(err,stdout,stderr) { RED.nodes.registerType("rpi-piface in",PiFACEInNode);
if (err) { RED.nodes.registerType("rpi-piface out",PiFACEOutNode);
util.log('[37-rpi-piface.js] Error: "gpio -p reset" command failed for some reason.');
}
RED.nodes.registerType("rpi-piface in",PiFACEInNode);
RED.nodes.registerType("rpi-piface out",PiFACEOutNode);
PiFACEInNode.prototype.close = function() {
clearInterval(this._interval);
}
PiFACEOutNode.prototype.close = function() {
}
});
}); });

View File

@ -18,7 +18,7 @@
<div class="form-row"> <div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Input</label> <label for="node-input-pin"><i class="icon-asterisk"></i> Input</label>
<select type="text" id="node-input-pin" style="width: 150px;"> <select type="text" id="node-input-pin" style="width: 150px;">
<option value="-" disabled>select input</option> <option value="-">select input</option>
<option value="Red Button">Red Button</option> <option value="Red Button">Red Button</option>
<option value="In A">In A</option> <option value="In A">In A</option>
<option value="In B">In B</option> <option value="In B">In B</option>
@ -64,7 +64,7 @@
<div class="form-row"> <div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Output</label> <label for="node-input-pin"><i class="icon-asterisk"></i> Output</label>
<select type="text" id="node-input-pin" style="width: 150px;"> <select type="text" id="node-input-pin" style="width: 150px;">
<option value="-" disabled>select output</option> <option value="-">select output</option>
<option value="Red LED">Red LED</option> <option value="Red LED">Red LED</option>
<option value="Amber LED">Amber LED</option> <option value="Amber LED">Amber LED</option>
<option value="Green LED">Green LED</option> <option value="Green LED">Green LED</option>

View File

@ -67,7 +67,7 @@ function PibrellaIn(n) {
this.pin = pintable[n.pin]; this.pin = pintable[n.pin];
var node = this; var node = this;
if (this.pin) { if (node.pin) {
exec("gpio mode "+node.pin+" in", function(err,stdout,stderr) { exec("gpio mode "+node.pin+" in", function(err,stdout,stderr) {
if (err) node.error(err); if (err) node.error(err);
else { else {
@ -90,11 +90,11 @@ function PibrellaIn(n) {
}); });
} }
else { else {
this.error("Invalid GPIO pin: "+this.pin); node.error("Invalid GPIO pin: "+node.pin);
} }
this.on("close", function() { node.on("close", function() {
clearInterval(this._interval); clearInterval(node._interval);
}); });
} }
@ -103,7 +103,7 @@ function PibrellaOut(n) {
this.pin = pintable[n.pin]; this.pin = pintable[n.pin];
var node = this; var node = this;
if (this.pin == "1") { if (node.pin == "1") {
exec("gpio mode 1 pwm"); exec("gpio mode 1 pwm");
process.nextTick(function() { process.nextTick(function() {
exec("gpio pwm-ms"); exec("gpio pwm-ms");
@ -121,7 +121,7 @@ function PibrellaOut(n) {
}); });
}); });
} }
else if (this.pin) { else if (node.pin) {
process.nextTick(function() { process.nextTick(function() {
exec("gpio mode "+node.pin+" out", function(err,stdout,stderr) { exec("gpio mode "+node.pin+" out", function(err,stdout,stderr) {
if (err) node.error(err); if (err) node.error(err);
@ -142,31 +142,31 @@ function PibrellaOut(n) {
}); });
} }
else { else {
this.error("Invalid GPIO pin: "+this.pin); node.error("Invalid GPIO pin: "+node.pin);
} }
this.on("close", function() { node.on("close", function() {
exec("gpio mode "+this.pin+" in"); exec("gpio mode "+node.pin+" in");
}); });
} }
exec("gpio mode 0 out",function(err,stdout,stderr) { //exec("gpio mode 0 out",function(err,stdout,stderr) {
if (err) { //if (err) {
util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.'); //util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.');
} //}
exec("gpio mode 1 out"); //exec("gpio mode 1 out");
exec("gpio mode 2 out"); //exec("gpio mode 2 out");
exec("gpio mode 3 out"); //exec("gpio mode 3 out");
exec("gpio mode 4 out"); //exec("gpio mode 4 out");
exec("gpio mode 5 out"); //exec("gpio mode 5 out");
exec("gpio mode 6 out"); //exec("gpio mode 6 out");
exec("gpio mode 7 out"); //exec("gpio mode 7 out");
exec("gpio mode 10 in"); //exec("gpio mode 10 in");
exec("gpio mode 11 in"); //exec("gpio mode 11 in");
exec("gpio mode 12 in"); //exec("gpio mode 12 in");
exec("gpio mode 13 in"); //exec("gpio mode 13 in");
exec("gpio mode 14 in"); //exec("gpio mode 14 in");
}); //});
RED.nodes.registerType("rpi-pibrella in",PibrellaIn); RED.nodes.registerType("rpi-pibrella in",PibrellaIn);
RED.nodes.registerType("rpi-pibrella out",PibrellaOut); RED.nodes.registerType("rpi-pibrella out",PibrellaOut);

View File

@ -27,7 +27,7 @@
<p>See <i><a href="http://www.piborg.com/ledborg/install" target="_new">the PiBorg site</a></i> for more information.</p> <p>See <i><a href="http://www.piborg.com/ledborg/install" target="_new">the PiBorg site</a></i> for more information.</p>
<p>You can also now use a <b>msg.payload</b> in the standard hex format "#rrggbb". The clip levels are :</p> <p>You can also now use a <b>msg.payload</b> in the standard hex format "#rrggbb". The clip levels are :</p>
<p><pre>0x00 - 0x57 = off<br/>0x58 - 0xA7 = 50%<br/>0xA8 - 0xFF = fully on</pre></p> <p><pre>0x00 - 0x57 = off<br/>0x58 - 0xA7 = 50%<br/>0xA8 - 0xFF = fully on</pre></p>
<p>You can also use the @cheerlight colour names - red, amber, green, blue, cyan, magenta, yeloow, orange, pink, purple, <p>You can also use the @cheerlight colour names - red, amber, green, blue, cyan, magenta, yellow, orange, pink, purple,
white, warmwhite, black</p> white, warmwhite, black</p>
</script> </script>

View File

@ -16,17 +16,43 @@
<script type="text/x-red" data-template-name="rawserial in"> <script type="text/x-red" data-template-name="rawserial in">
<div class="form-row"> <div class="form-row">
<label for="node-input-port"><i class="icon-random"></i> Port</label> <label for="node-input-port"><i class="fa fa-random"></i> Port</label>
<input type="text" id="node-input-port" placeholder="COM1"> <input type="text" id="node-input-port" placeholder="COM1">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-split"><i class="icon-edit"></i> Split on</label> <label for="node-input-out"><i class="fa fa-cut"></i> split input</label>
<input type="text" id="node-input-split" placeholder="\n"> <select type="text" id="node-input-out" style="width:52%;">
<option value="char">when character received is</option>
<option value="time">after a fixed timeout of</option>
</select>
<input type="text" id="node-input-splitc" style="width:50px;">
<span id="node-units"></span>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-bin"><i class="fa fa-sign-in"></i> and deliver</label>
<select type="text" id="node-input-bin" style="width: 77%;">
<option value="false">ascii strings</option>
<option value="true">binary buffers</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"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
<script>
var previous = null;
$("#node-input-out").on('focus', function () { previous = this.value; }).change(function() {
if (previous == null) { previous = $("#node-input-out").val(); }
if ($("#node-input-out").val() == "char") {
if (previous != "char") { $("#node-input-splitc").val("\\n"); }
$("#node-units").text("");
}
else {
if (previous != "time") { $("#node-input-splitc").val("0"); }
$("#node-units").text("ms");
}
});
</script>
</script> </script>
<script type="text/x-red" data-help-name="rawserial in"> <script type="text/x-red" data-help-name="rawserial in">
@ -44,7 +70,9 @@
color:"BurlyWood", color:"BurlyWood",
defaults: { defaults: {
name: {value:""}, name: {value:""},
split: {value:""}, splitc: {value:"\n"},
out: {value:"char"},
bin: {value:"false"},
port: {value:"", required:true} port: {value:"", required:true}
}, },
inputs:0, inputs:0,
@ -61,11 +89,11 @@
<script type="text/x-red" data-template-name="rawserial out"> <script type="text/x-red" data-template-name="rawserial out">
<div class="form-row"> <div class="form-row">
<label for="node-input-port"><i class="icon-random"></i> Port</label> <label for="node-input-port"><i class="fa fa-random"></i> Port</label>
<input type="text" id="node-input-port" placeholder="COM1"> <input type="text" id="node-input-port" placeholder="COM1">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
</script> </script>

View File

@ -29,36 +29,67 @@ if (!plat.match(/^win/)) {
function RawSerialInNode(n) { function RawSerialInNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.port = n.port; this.port = n.port;
this.split = n.split||null; this.splitc = n.splitc||null;
if (this.split == '\\n') this.split = "\n"; this.out = n.out||"char";
if (this.split == '\\r') this.split = "\r"; this.bin = n.bin||false;
if (this.splitc == '\\n') this.splitc = "\n";
if (this.splitc == '\\r') this.splitc = "\r";
if (!isNaN(parseInt(this.splitc))) { this.splitc = parseInt(this.splitc); }
var node = this; var node = this;
var setupSerial = function() { var setupSerial = function() {
node.inp = fs.createReadStream(pre+node.port); node.inp = fs.createReadStream(pre+node.port);
node.log("opened "+pre+node.port); node.log("open "+pre+node.port);
node.inp.setEncoding('utf8'); node.tout = null;
var line = ""; var line = "";
var buf = new Buffer(32768);
var i = 0;
node.inp.on('data', function (data) { node.inp.on('data', function (data) {
if (node.split != null) { for (var z = 0; z < data.length; z++) {
if (data == node.split) { if ((node.out === "time") && (node.splitc != 0)) {
node.send({payload:line}); if (node.tout) {
line = ""; i += 1;
buf[i] = data[z];
}
else {
node.tout = setTimeout(function () {
node.tout = null;
var m = new Buffer(i+1);
buf.copy(m,0,0,i+1);
if (node.bin !== "true") { m = m.toString(); }
node.send({"payload": m});
}, node.splitc);
i = 0;
buf[0] = data[z];
}
}
else if ((node.out == "char") && (node.splitc != null)) {
buf[i] = data[z];
i += 1;
if ((data[z] === node.splitc.charCodeAt(0)) || (i === 32768)) {
var m = new Buffer(i);
buf.copy(m,0,0,i);
if (node.bin !== "true") { m = m.toString(); }
node.send({"payload":m});
i = 0;
}
}
else {
if (node.bin !== "true") { node.send({"payload": String.fromCharCode(data[z])}); }
else { node.send({"payload": new Buffer([data[z]])});}
} }
else { line += data; }
} }
else { node.send({payload:data}); }
}); });
//node.inp.on('end', function (error) {console.log("End", error);}); //node.inp.on('end', function (error) {console.log("End", error);});
node.inp.on('close', function (error) { node.inp.on('close', function (error) {
util.log("[rawserial] "+node.port+" closed"); node.log(node.port+" closed");
node.tout = setTimeout(function() { node.tout = setTimeout(function() {
setupSerial(); setupSerial();
},settings.serialReconnectTime); },settings.serialReconnectTime);
}); });
node.inp.on('error', function(error) { node.inp.on('error', function(error) {
if (error.code == "ENOENT") { util.log("[rawserial] port "+node.port+" not found"); } if (error.code == "ENOENT") { node.log(node.port+" not found"); }
else { util.log("[rawserial] "+node.port+" error "+error); } else { node.log(node.port+" error "+error); }
node.tout = setTimeout(function() { node.tout = setTimeout(function() {
setupSerial(); setupSerial();
},settings.serialReconnectTime); },settings.serialReconnectTime);
@ -87,18 +118,18 @@ function RawSerialOutNode(n) {
node.oup.write(msg.payload); node.oup.write(msg.payload);
} }
}); });
node.oup.on('open', function (error) { util.log("[rawserial] opened "+node.port); }); node.oup.on('open', function (error) { node.log("opened "+node.port); });
node.oup.on('end', function (error) { console.log("End",error); }); node.oup.on('end', function (error) { node.log("end :"+error); });
node.oup.on('close', function (error) { node.oup.on('close', function (error) {
util.log("[rawserial] "+node.port+" closed"); node.log(node.port+" closed");
node.tout = setTimeout(function() { node.tout = setTimeout(function() {
setupSerial(); setupSerial();
},settings.serialReconnectTime); },settings.serialReconnectTime);
}); });
node.oup.on('error', function(error) { node.oup.on('error', function(error) {
if (error.code == "EACCES") { util.log("[rawserial] can't access port "+node.port); } if (error.code == "EACCES") { node.log("can't access port "+node.port); }
else if (error.code == "EIO") { util.log("[rawserial] can't write to port "+node.port); } else if (error.code == "EIO") { node.log("can't write to port "+node.port); }
else { util.log("[rawserial] "+node.port+" error "+error); } else { node.log(node.port+" error "+error); }
node.tout = setTimeout(function() { node.tout = setTimeout(function() {
setupSerial(); setupSerial();
},settings.serialReconnectTime); },settings.serialReconnectTime);

169
io/stomp/18-stomp.html Normal file
View File

@ -0,0 +1,169 @@
<!--
Copyright 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.
-->
<script type="text/x-red" data-template-name="stomp in">
<div class="form-row">
<label for="node-input-server" style="width: 110px;"><i class="icon-bookmark"></i> Server</label>
<input type="text" id="node-input-server">
</div>
<div class="form-row">
<label for="node-input-topic" style="width: 110px;"><i class="icon-envelope"></i> Destination</label>
<input type="text" id="node-input-topic" placeholder="topic or queue">
</div>
<div class="form-row">
<label for="node-input-name" style="width: 110px;"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="stomp in">
<p>Connects to a server using the Stomp protocol to receive messages.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('stomp in',{
category: 'input',
color:"#e8cfe8",
defaults: {
name: {value:""},
server: {type:"stomp-server",required:true},
topic: {value:"",required:true}
},
inputs:0,
outputs:1,
icon: "bridge.png",
label: function() {
return this.name||"stomp";
},
labelStyle: function() {
return (this.name)?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="stomp out">
<div class="form-row">
<label for="node-input-server" style="width: 110px;"><i class="icon-bookmark"></i> Server</label>
<input type="text" id="node-input-server">
</div>
<div class="form-row">
<label for="node-input-topic" style="width: 110px;"><i class="icon-envelope"></i> Destination</label>
<input type="text" id="node-input-topic" placeholder="topic or queue">
</div>
<div class="form-row">
<label for="node-input-name" style="width: 110px;"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The <b>Destination</b> field is optional. If not set uses the <b>msg.topic</b> property of the message.</div>
</script>
<script type="text/x-red" data-help-name="stomp out">
<p>Connects to an Stomp capable server to send messages.</p>
<p>The <b>Destination</b> field is optional. If set it overrides the <b>msg.topic</b> property of the message.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('stomp out',{
category: 'output',
color:"#e8cfe8",
defaults: {
name: {value:""},
server: {type:"stomp-server",required:true},
topic: {value:""}
},
inputs:1,
outputs:0,
icon: "bridge.png",
align: "right",
label: function() {
return this.name||"stomp";
},
labelStyle: function() {
return (this.name)?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="stomp-server">
<div class="form-row node-input-server">
<label for="node-config-input-server"><i class="icon-bookmark"></i> Server</label>
<input class="input-append-left" type="text" id="node-config-input-server" placeholder="localhost" style="width: 45%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="Port" style="width:45px">
</div>
<div class="form-row">
<label for="node-config-input-user"><i class="icon-user"></i> Username</label>
<input type="text" id="node-config-input-user">
</div>
<div class="form-row">
<label for="node-config-input-pass"><i class="icon-lock"></i> Password</label>
<input type="password" id="node-config-input-pass">
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('stomp-server',{
category: 'config',
defaults: {
server: {required:true},
port: {value:61618,required:true,validate:RED.validators.number()},
name: {}
},
label: function() {
return (this.name?this.name:this.server+":"+this.port);
},
oneditprepare: function() {
$.getJSON('stomp-server/'+this.id,function(data) {
if (data.user) {
$('#node-config-input-user').val(data.user);
}
if (data.hasPassword) {
$('#node-config-input-pass').val('__PWRD__');
} else {
$('#node-config-input-pass').val('');
}
if (data.global) $('#node-config-cred-tip').show();
else $('#node-config-cred-tip').hide();
});
},
oneditsave: function() {
var credentials = {};
var newUser = $('#node-config-input-user').val();
var newPass = $('#node-config-input-pass').val();
credentials.user = newUser;
if (newPass != '__PWRD__') {
credentials.password = newPass;
}
$.ajax({
url: 'stomp-server/'+this.id,
type: 'POST',
data: credentials,
success: function(result){}
});
},
ondelete: function() {
$.ajax({
url: 'stomp-server/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
});
</script>

180
io/stomp/18-stomp.js Normal file
View File

@ -0,0 +1,180 @@
/**
* Copyright 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";
var StompClient = require('stomp-client');
var querystring = require('querystring');
function StompServerNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
this.port = n.port;
this.name = n.name;
var credentials = RED.nodes.getCredentials(n.id);
if (credentials) {
this.username = credentials.user;
this.password = credentials.password;
}
}
RED.nodes.registerType("stomp-server",StompServerNode);
RED.httpAdmin.get('/stomp-server/:id',function(req,res) {
var credentials = RED.nodes.getCredentials(req.params.id);
if (credentials) {
res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")}));
} else {
res.send(JSON.stringify({}));
}
});
RED.httpAdmin.delete('/stomp-server/:id',function(req,res) {
RED.nodes.deleteCredentials(req.params.id);
res.send(200);
});
RED.httpAdmin.post('/stomp-server/:id',function(req,res) {
var body = "";
req.on('data', function(chunk) {
body+=chunk;
});
req.on('end', function(){
var newCreds = querystring.parse(body);
var credentials = RED.nodes.getCredentials(req.params.id)||{};
if (newCreds.user == null || newCreds.user == "") {
delete credentials.user;
} else {
credentials.user = newCreds.user;
}
if (newCreds.password == "") {
delete credentials.password;
} else {
credentials.password = newCreds.password||credentials.password;
}
RED.nodes.addCredentials(req.params.id,credentials);
res.send(200);
});
});
function StompInNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
this.topic = n.topic;
this.serverConfig = RED.nodes.getNode(this.server);
this.host = this.serverConfig.server;
this.port = this.serverConfig.port;
this.userid = this.serverConfig.username;
this.password = this.serverConfig.password;
var node = this;
var msg = {topic:this.topic};
var closing = false;
node.client = new StompClient(node.host, node.port, node.userid, node.password, '1.0');
node.status({fill:"grey",shape:"ring",text:"connecting"});
var doConnect = function() {
node.client.connect(function(sessionId) {
node.status({fill:"green",shape:"dot",text:"connected"});
node.log('subscribed to: '+node.topic);
node.client.subscribe(node.topic, function(body, headers) {
msg.payload = JSON.parse(body);
node.send(msg);
});
}, function(error) {
node.status({fill:"grey",shape:"dot",text:"error"});
node.warn(error);
});
}
node.client.on("disconnect", function() {
node.status({fill:"red",shape:"ring",text:"disconnected"});
if (!closing) {
setTimeout( function () { doConnect(); }, 15000);
}
});
node.client.on("error", function(error) {
node.status({fill:"grey",shape:"dot",text:"error"});
node.log(error);
});
doConnect();
node.on("close", function(done) {
closing = true;
if (node.client) {
node.client.on("disconnect", function() {
done();
});
//node.client.unsubscribe(node.topic);
node.client.disconnect();
} else { done(); }
});
}
RED.nodes.registerType("stomp in",StompInNode);
function StompOutNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
this.topic = n.topic;
this.serverConfig = RED.nodes.getNode(this.server);
this.host = this.serverConfig.server;
this.port = this.serverConfig.port;
this.userid = this.serverConfig.username;
this.password = this.serverConfig.password;
var node = this;
var msg = {topic:this.topic};
var closing = false;
node.client = new StompClient(node.host, node.port, node.userid, node.password, '1.0');
node.status({fill:"grey",shape:"ring",text:"connecting"});
node.client.connect( function(sessionId) {
node.status({fill:"green",shape:"dot",text:"connected"});
}, function(error) {
node.status({fill:"grey",shape:"dot",text:"error"});
node.warn(error);
});
node.client.on("disconnect", function() {
node.status({fill:"red",shape:"ring",text:"disconnected"});
if (!closing) {
setTimeout( function () { node.client.connect(); }, 15000);
}
});
node.client.on("error", function(error) {
node.log(error);
});
node.on("input", function(msg) {
node.client.publish(node.topic || msg.topic, msg.payload);
});
node.on("close", function(done) {
closing = true;
if (client) { client.disconnect(); }
});
}
RED.nodes.registerType("stomp out",StompOutNode);
}

View File

@ -24,7 +24,7 @@
<script type="text/x-red" data-help-name="mpd out"> <script type="text/x-red" data-help-name="mpd out">
<p>MPD music control output node.</p> <p>MPD music control output node.</p>
<p>Expects <b>msg.payload</b> to be a valid mpc command. Currently only simple commands that expect no reply are supported.</p> <p>Expects <b>msg.payload</b> to be a valid mpc command. Currently only simple commands that expect no reply are supported. play, pause, next, previous, stop, etc.</p>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
@ -57,7 +57,7 @@
<script type="text/x-red" data-help-name="mpd in"> <script type="text/x-red" data-help-name="mpd in">
<p>MPD music control input node.</p> <p>MPD music control input node.</p>
<p>Creates a <b>msg.payload</b> object with Artist, Album, Track, Genre and Date.</p> <p>Creates a <b>msg.payload</b> object containing Artist, Album, Track, Genre and Date.</p>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">

View File

@ -21,15 +21,13 @@ var komponist = require('komponist');
var mpc = null; var mpc = null;
exec("which mpd",function(err,stdout,stderr) { exec("which mpd",function(err,stdout,stderr) {
if (stdout.indexOf('mpd') == -1) { if (stdout.indexOf('mpd') == -1) {
util.log('[69-mpd.js] Error: Cannot find "mpd" command. Please install MPD.'); throw 'Error: Cannot find "mpd" command. Please install MPD.';
return;
} }
}); });
exec("netstat -an | grep LISTEN | grep 6600",function(err,stdout,stderr) { exec("netstat -an | grep LISTEN | grep 6600",function(err,stdout,stderr) {
if (stdout.indexOf('6600') == -1) { if (stdout.indexOf('6600') == -1) {
util.log('[69-mpd.js] Warning: MPD daemon not listening on port 6600. Please start MPD.'); throw '[69-mpd.js] Error: MPD daemon not listening on port 6600. Please start MPD.';
return;
} }
komponist.createConnection(6600, 'localhost', function(err, client) { komponist.createConnection(6600, 'localhost', function(err, client) {
if (err) node.error("MPD: Failed to connect to MPD server"); if (err) node.error("MPD: Failed to connect to MPD server");
@ -46,19 +44,18 @@ function MPDOut(n) {
if (mpc != null) { if (mpc != null) {
this.on("input", function(msg) { this.on("input", function(msg) {
if (msg != null) { if (msg != null) {
console.log(msg);
try { try {
//node.mpc.command(msg.payload); //node.mpc.command(msg.payload);
node.mpc.command(msg.payload, msg.param, function(err, results) { node.mpc.command(msg.payload, msg.param, function(err, results) {
if (err) { console.log("MPD: Error:",err); } if (err) { node.log("error: "+err); }
//else { console.log(results); } //else { console.log(results); }
}); });
} catch (err) { console.log("MPD: Error:",err); } } catch (err) { node.log("error: "+err); }
} }
}); });
node.mpc.on('error', function(err) { node.mpc.on('error', function(err) {
console.log("MPD: Error:",err); node.log("error: "+err);
}); });
} }
else { node.warn("MPD not running"); } else { node.warn("MPD not running"); }
@ -76,7 +73,7 @@ function MPDIn(n) {
function getSong() { function getSong() {
node.mpc.currentsong(function(err, info) { node.mpc.currentsong(function(err, info) {
if (err) console.log(err); if (err) node.log(err);
else { else {
var msg = {payload:{},topic:"music"}; var msg = {payload:{},topic:"music"};
msg.payload.Artist = info.Artist; msg.payload.Artist = info.Artist;

View File

@ -32,6 +32,7 @@
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
<div class="form-tips" id="node-tip"><b>Note:</b> Using credentials from global pushkey.js file.</div>
</script> </script>
<script type="text/x-red" data-help-name="prowl"> <script type="text/x-red" data-help-name="prowl">
@ -69,6 +70,8 @@
} else { } else {
$('#node-config-input-pushkey').val(''); $('#node-config-input-pushkey').val('');
} }
if (data.global) $('#node-tip').show();
else $('#node-tip').hide();
}); });
}, },
oneditsave: function() { oneditsave: function() {

View File

@ -75,7 +75,13 @@ RED.httpAdmin.get('/prowl/:id',function(req,res) {
var credentials = RED.nodes.getCredentials(req.params.id); var credentials = RED.nodes.getCredentials(req.params.id);
if (credentials) { if (credentials) {
res.send(JSON.stringify({hasPassword:(credentials.pushkey&&credentials.pushkey!="")})); res.send(JSON.stringify({hasPassword:(credentials.pushkey&&credentials.pushkey!="")}));
} else { }
else if (pushkeys && pushkeys.prowlkey) {
RED.nodes.addCredentials(req.params.id,{pushkey:pushkeys.prowlkey,global:true});
credentials = RED.nodes.getCredentials(req.params.id);
res.send(JSON.stringify({hasPassword:(credentials.pushkey&&credentials.pushkey!=""),global:credentials.global}));;
}
else {
res.send(JSON.stringify({})); res.send(JSON.stringify({}));
} }
}); });

View File

@ -0,0 +1,184 @@
<!--
Copyright 2014 Charalampos Doukas, @BuildingIoT
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="pusher in">
<div class="form-row">
<label for="node-input-channel"><i class="icon-random"></i> Channel</label>
<input type="text" id="node-input-channel" placeholder="my_channel">
</div>
<div class="form-row">
<label for="node-input-eventname"><i class="icon-tasks"></i> Event</label>
<input type="text" id="node-input-eventname" placeholder="test_event_name">
</div>
<div class="form-row">
<label for="node-input-appkey_sub"><i class="icon-lock"></i> App Key</label>
<input type="text" id="node-input-appkey_sub" placeholder="key">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="pusher in">
<p>Pusher input mode. Use this node to subscribe to a Pusher channel/event. You need a valid Pusher App key.</p>
</script>
<!-- Finally, the node type is registered along with all of its properties -->
<script type="text/javascript">
RED.nodes.registerType('pusher in',{
category: 'social-input', // the palette category
color:"#A9D0F5",
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
channel: {value:"", required:true},
eventname: {value:"", required:true}
},
inputs:0, // set the number of inputs - only 0 or 1
outputs:1, // set the number of outputs - 0 to n
icon: "pusher.png", // set the icon (held in public/icons)
label: function() { // sets the default label contents
return this.name||"pusher";
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
},
oneditsave: function() {
var credentials = {};
var newAppKey_sub = $('#node-input-appkey_sub').val();
credentials.pusherappkey_sub = newAppKey_sub;
$.ajax({
url: 'pusher/'+this.id,
type: 'POST',
data: credentials,
success: function(result){}
});
},
oneditprepare: function() {
$.getJSON('pusher/'+this.id,function(data) {
if (data.pusherappkey_sub) {
$('#node-input-appkey_sub').val(data.pusherappkey_sub);
}
});
},
ondelete: function() {
$.ajax({
url: 'pusher/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
});
</script>
<script type="text/x-red" data-template-name="pusher out">
<div class="form-row">
<label for="node-input-channel"><i class="icon-random"></i> Channel</label>
<input type="text" id="node-input-channel" placeholder="my_channel">
</div>
<div class="form-row">
<label for="node-input-eventname"><i class="icon-tasks"></i> Event</label>
<input type="text" id="node-input-eventname" placeholder="test_event_name">
</div>
<div class="form-row">
<label for="node-input-appid"><i class="icon-tag"></i> App ID</label>
<input type="text" id="node-input-appid" placeholder="app_id">
</div>
<div class="form-row">
<label for="node-input-appkey"><i class="icon-lock"></i> App Key</label>
<input type="text" id="node-input-appkey" placeholder="key">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-asterisk"></i> App Secret</label>
<input type="password" id="node-input-appsecret" placeholder="secret">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="pusher out">
<p>Pusher output node for sending messages to a specific channel/event. You need an App key, secret and ID of a Pusher app. The node will send the payload of the incoming message.</p>
</script>
<!-- Finally, the node type is registered along with all of its properties -->
<script type="text/javascript">
RED.nodes.registerType('pusher out',{
category: 'social-output', // the palette category
color:"#A9D0F5",
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
channel: {value:"", required:true},
eventname: {value:"", required:true}
},
inputs:1, // set the number of inputs - only 0 or 1
outputs:0, // set the number of outputs - 0 to n
icon: "pusher.png", // set the icon (held in public/icons)
align: "right",
label: function() { // sets the default label contents
return this.name||"pusher";
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
},
oneditsave: function() {
var credentials = {};
var newAppID = $('#node-input-appid').val();
var newAppKey = $('#node-input-appkey').val();
var newAppSecret = $('#node-input-appsecret').val();
credentials.pusherappid = newAppID;
credentials.pusherappkey = newAppKey;
credentials.pusherappsecret = newAppSecret;
$.ajax({
url: 'pusher/'+this.id,
type: 'POST',
data: credentials,
success: function(result){}
});
},
oneditprepare: function() {
$.getJSON('pusher/'+this.id,function(data) {
if (data.pusherappid) {
$('#node-input-appid').val(data.pusherappid);
}
if (data.pusherappkey) {
$('#node-input-appkey').val(data.pusherappkey);
}
if (data.pusherappsecret) {
$('#node-input-appsecret').val(data.pusherappsecret);
}
});
},
ondelete: function() {
$.ajax({
url: 'pusher/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
});
</script>

158
social/pusher/114-pusher.js Normal file
View File

@ -0,0 +1,158 @@
/**
* pusher.js
* Subscription module for the Pusher service (www.pusher.com)
* Requires 'pusher' and 'pusher-client' modules.
*
* Copyright 2014 Charalampos Doukas, @BuildingIoT
*
* 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.
**/
var Pusher = require('pusher');
var PusherClient = require('pusher-client');
// Require main module
var RED = require(process.env.NODE_RED_HOME+"/red/red");
//node for subscribing to an event/channel
function PusherNode(n) {
// Create a RED node
RED.nodes.createNode(this,n);
var node = this;
var credentials = RED.nodes.getCredentials(n.id);
if ((credentials) && (credentials.hasOwnProperty("pusherappkey_sub"))) { this.appkey = credentials.pusherappkey_sub; }
else { this.error("No Pusher app key set for input node"); }
//get parameters from user
this.channel = n.channel;
this.eventname = n.eventname;
//create a subscription to the channel and event defined by user
var socket = new PusherClient(''+this.appkey);
var my_channel = socket.subscribe(''+this.channel);
socket.bind(''+this.eventname,
function(data) {
var msg = {};
if (data.hasOwnProperty("payload")) { msg.payload = data.payload; }
else { msg.payload = data; }
node.send(msg);
}
);
this.on("close", function() {
socket.disconnect();
});
}
//Node for sending Pusher events
function PusherNodeSend(n) {
// Create a RED node
RED.nodes.createNode(this,n);
var node = this;
var credentials = RED.nodes.getCredentials(n.id);
if ((credentials) && (credentials.hasOwnProperty("pusherappid"))) { this.appid = credentials.pusherappid; }
else { this.error("No Pusher api token set"); }
if ((credentials) && (credentials.hasOwnProperty("pusherappsecret"))) { this.appsecret = credentials.pusherappsecret; }
else { this.error("No Pusher user secret set"); }
if ((credentials) && (credentials.hasOwnProperty("pusherappkey"))) { this.appkey = credentials.pusherappkey; }
else { this.error("No Pusher user key set"); }
//get parameters from user
this.channel = n.channel;
this.eventname = n.eventname;
var pusher = new Pusher({
appId: this.appid,
key: this.appkey,
secret: this.appsecret
});
this.on("input", function(msg){
pusher.trigger(this.channel, this.eventname, {
"payload": msg.payload
});
});
this.on("close", function() {
});
}
//debugging on the output:
var displayResult = function(result) {
node.log(result);
};
var displayError = function(err) {
node.log("Error: "+err);
};
RED.nodes.registerType("pusher in",PusherNode);
RED.nodes.registerType("pusher out",PusherNodeSend);
var querystring = require('querystring');
RED.httpAdmin.get('/pusher/:id',function(req,res) {
var credentials = RED.nodes.getCredentials(req.params.id);
if (credentials) {
res.send(JSON.stringify({pusherappid:credentials.pusherappid,pusherappsecret:credentials.pusherappsecret, pusherappkey:credentials.pusherappkey, pusherappkey_sub:credentials.pusherappkey_sub}));
} else {
res.send(JSON.stringify({}));
}
});
RED.httpAdmin.delete('/pusher/:id',function(req,res) {
RED.nodes.deleteCredentials(req.params.id);
res.send(200);
});
RED.httpAdmin.post('/pusher/:id',function(req,res) {
var body = "";
req.on('data', function(chunk) {
body+=chunk;
});
req.on('end', function(){
var newCreds = querystring.parse(body);
var credentials = RED.nodes.getCredentials(req.params.id)||{};
if (newCreds.pusherappid == null || newCreds.pusherappid == "") {
delete credentials.pusherappid;
} else {
credentials.pusherappid = newCreds.pusherappid;
}
if (newCreds.pusherappkey == "") {
delete credentials.pusherappkey;
} else {
credentials.pusherappkey = newCreds.pusherappkey||credentials.pusherappkey;
}
if (newCreds.pusherappsecret == "") {
delete credentials.pusherappsecret;
} else {
credentials.pusherappsecret = newCreds.pusherappsecret||credentials.pusherappsecret;
}
if (newCreds.pusherappkey_sub == "") {
delete credentials.pusherappkey_sub;
} else {
credentials.pusherappkey_sub = newCreds.pusherappkey_sub||credentials.pusherappkey_sub;
}
RED.nodes.addCredentials(req.params.id,credentials);
res.send(200);
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

After

Width:  |  Height:  |  Size: 383 B

View File

@ -1,5 +1,5 @@
<!-- <!--
Copyright 2013 IBM Corp. Copyright 2013,2014 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,7 +14,56 @@
limitations under the License. limitations under the License.
--> -->
<script type="text/x-red" data-template-name="xmpp"> <script type="text/x-red" data-template-name="xmpp in">
<div class="form-row">
<label for="node-input-server"><i class="icon-bookmark"></i> Server</label>
<input type="text" id="node-input-server">
</div>
<div class="form-row">
<label for="node-input-to"><i class="icon-envelope"></i> Buddy</label>
<input type="text" id="node-input-to" placeholder="joe@gmail.com">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-join" placeholder="" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-join" style="width: 70%;">Is a Chat Room ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="xmpp in">
<p>Connects to an XMPP server to receive messages.</p>
<p>The <b>Buddy</b> field is the id of the buddy or room you want to receive messages from.</p>
<p>Incoming messages will appear as <b>msg.payload</b> on the first output, while <b>msg.topic</b> will contain who it is from.</p>
<p>The second output will show the presence and status of a user in <b>msg.payload</b>. Again <b>msg.topic</b> will hold the user.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('xmpp in',{
category: 'social-input',
color:"#F4F492",
defaults: {
name: {value:""},
server: {type:"xmpp-server",required:true},
to: {value:"",required:true},
join: {value:false}
},
inputs:0,
outputs:2,
icon: "xmpp.png",
label: function() {
return this.name||"xmpp";
},
labelStyle: function() {
return (this.name)?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="xmpp out">
<div class="form-row"> <div class="form-row">
<label for="node-input-server"><i class="icon-bookmark"></i> Server</label> <label for="node-input-server"><i class="icon-bookmark"></i> Server</label>
<input type="text" id="node-input-server"> <input type="text" id="node-input-server">
@ -33,10 +82,6 @@
<input type="checkbox" id="node-input-join" placeholder="" style="display: inline-block; width: auto; vertical-align: top;"> <input type="checkbox" id="node-input-join" placeholder="" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-join" style="width: 70%;">Is a Chat Room ?</label> <label for="node-input-join" style="width: 70%;">Is a Chat Room ?</label>
</div> </div>
<div class="form-row">
<label for="node-input-nick"><i class="icon-user"></i> Nickname</label>
<input type="text" id="node-input-nick" placeholder="Joe">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
@ -44,35 +89,34 @@
<div class="form-tips">The <b>To</b> field is optional. If not set uses the <b>msg.topic</b> property of the message.</div> <div class="form-tips">The <b>To</b> field is optional. If not set uses the <b>msg.topic</b> property of the message.</div>
</script> </script>
<script type="text/x-red" data-help-name="xmpp"> <script type="text/x-red" data-help-name="xmpp out">
<p>Connects to an XMPP server to send and receive messages.</p> <p>Connects to an XMPP server to send messages.</p>
<p>Incoming messages will appear as <b>msg.payload</b> on the first output, while <b>msg.topic</b> will contain who it is from.</p> <p>The <b>To</b> field is optional. If not set the <b>msg.topic</b> property of the message is used.</p>
<p>The second output will user presence and status in <b>msg.payload</b>.</p>
<p>The <b>To</b> field is optional. If not set uses the <b>msg.topic</b> property of the message.</p>
<p>If you are joining a room then the <b>To</b> field must be filled in.</p> <p>If you are joining a room then the <b>To</b> field must be filled in.</p>
<p>You may also send a msg with <b>msg.presence</b> to set your presence to one of <i>chat, away, dnd</i> or <i>xa</i>.
If you do so then the <b>msg.payload</b> will set your status message.</p>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
RED.nodes.registerType('xmpp',{ RED.nodes.registerType('xmpp out',{
category: 'advanced-function', category: 'social-output',
color:"Silver", color:"#F4F492",
defaults: { defaults: {
name: {value:""}, name: {value:""},
server: {type:"xmpp-server",required:true}, server: {type:"xmpp-server",required:true},
to: {value:""}, to: {value:""},
join: {value:false}, join: {value:false},
sendObject: {value:false}, sendObject: {value:false}
nick: {value:""}
}, },
inputs:1, inputs:1,
outputs:2, outputs:0,
icon: "xmpp.png", icon: "xmpp.png",
align: "right",
label: function() { label: function() {
return this.name||"xmpp"; return this.name||"xmpp";
}, },
labelStyle: function() { labelStyle: function() {
return (this.name||!this.server)?"node_label_italic":""; return (this.name)?"node_label_italic":"";
} }
}); });
</script> </script>
@ -84,6 +128,10 @@
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label> <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="Port" style="width:45px"> <input type="text" id="node-config-input-port" placeholder="Port" style="width:45px">
</div> </div>
<div class="form-row">
<label for="node-config-input-nickname"><i class="icon-user"></i> Nickname</label>
<input type="text" id="node-config-input-nickname" placeholder="Joe">
</div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-user"><i class="icon-user"></i> Username</label> <label for="node-config-input-user"><i class="icon-user"></i> Username</label>
<input type="text" id="node-config-input-user"> <input type="text" id="node-config-input-user">
@ -92,6 +140,7 @@
<label for="node-config-input-pass"><i class="icon-lock"></i> Password</label> <label for="node-config-input-pass"><i class="icon-lock"></i> Password</label>
<input type="password" id="node-config-input-pass"> <input type="password" id="node-config-input-pass">
</div> </div>
<div class="form-tips" id="node-config-cred-tip"><b>Note:</b> Using credentials from global xmppkey.js file.</div>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
@ -100,49 +149,38 @@
defaults: { defaults: {
server: {required:true}, server: {required:true},
port: {value:5222,required:true,validate:RED.validators.number()}, port: {value:5222,required:true,validate:RED.validators.number()},
user: {required:true}, nickname: {}
pass: {required:true}
}, },
label: function() { label: function() {
return (this.user?this.user+"@":"")+this.server+":"+this.port; return (this.nickname?this.nickname+"@":"")+this.server+":"+this.port;
}, },
oneditprepare: function() { oneditprepare: function() {
$.getJSON('xmpp-server/'+this.id,function(data) { $.getJSON('xmpp-server/'+this.id,function(data) {
if (data.user) { if (data.user) {
$('#node-config-input-user').val(data.user); $('#node-config-input-user').val(data.user);
} }
if (data.server) {
$('#node-config-input-server').val(data.server);
}
if (data.port) {
$('#node-config-input-port').val(data.port);
}
if (data.hasPassword) { if (data.hasPassword) {
$('#node-config-input-pass').val('__PWRD__'); $('#node-config-input-pass').val('__PWRD__');
} else { } else {
$('#node-config-input-pass').val(''); $('#node-config-input-pass').val('');
} }
if (data.global) $('#node-config-cred-tip').show();
else $('#node-config-cred-tip').hide();
}); });
}, },
oneditsave: function() { oneditsave: function() {
var credentials = {};
var newUser = $('#node-config-input-user').val(); var newUser = $('#node-config-input-user').val();
var newPass = $('#node-config-input-pass').val(); var newPass = $('#node-config-input-pass').val();
//var newServer = $('#node-config-input-server').val();
//var newPort = $('#node-config-input-port').val();
var credentials = {};
credentials.user = newUser; credentials.user = newUser;
if (newPass != '__PWRD__') { if (newPass != '__PWRD__') {
credentials.password = newPass; credentials.password = newPass;
} }
//credentials.server = newServer;
//credentials.port = newPort;
$.ajax({ $.ajax({
url: 'xmpp-server/'+this.id, url: 'xmpp-server/'+this.id,
type: 'POST', type: 'POST',
data: credentials, data: credentials,
success:function(result){} success: function(result){}
}); });
}, },
ondelete: function() { ondelete: function() {

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2013 IBM Corp. * Copyright 2013,2014 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,33 +14,23 @@
* limitations under the License. * limitations under the License.
**/ **/
var orig=console.warn; module.exports = function(RED) {
console.warn=(function() { // suppress warning from stringprep when not needed) "use strict";
var orig=console.warn; var XMPP = require('simple-xmpp');
return function() {
//orig.apply(console, arguments);
};
})();
var RED = require(process.env.NODE_RED_HOME+"/red/red"); try { var xmppkey = RED.settings.xmpp || require(process.env.NODE_RED_HOME+"/../xmppkeys.js"); }
var xmpp = require('simple-xmpp'); catch(err) { }
console.warn = orig;
try {
var xmppkey = RED.settings.xmpp || require(process.env.NODE_RED_HOME+"/../xmppkeys.js");
} catch(err) {
// throw new Error("Failed to load XMPP credentials");
}
function XMPPServerNode(n) { function XMPPServerNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.server = n.server; this.server = n.server;
this.port = n.port; this.port = n.port;
this.nickname = n.nickname;
var credentials = RED.nodes.getCredentials(n.id); var credentials = RED.nodes.getCredentials(n.id);
if (credentials) { if (credentials) {
this.username = credentials.user; this.username = credentials.user;
this.password = credentials.password; this.password = credentials.password;
} }
} }
RED.nodes.registerType("xmpp-server",XMPPServerNode); RED.nodes.registerType("xmpp-server",XMPPServerNode);
@ -51,9 +41,9 @@ RED.httpAdmin.get('/xmpp-server/:id',function(req,res) {
if (credentials) { if (credentials) {
res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")})); res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")}));
} else if (xmppkey && xmppkey.jid && xmppkey.password) { } else if (xmppkey && xmppkey.jid && xmppkey.password) {
RED.nodes.addCredentials(req.params.id,{user:xmppkey.jid,password:xmppkey.password}); RED.nodes.addCredentials(req.params.id,{user:xmppkey.jid, password:xmppkey.password, global:true});
credentials = RED.nodes.getCredentials(req.params.id); credentials = RED.nodes.getCredentials(req.params.id);
res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")})); res.send(JSON.stringify({user:credentials.user,global:credentials.global,hasPassword:(credentials.password&&credentials.password!="")}));
} else { } else {
res.send(JSON.stringify({})); res.send(JSON.stringify({}));
} }
@ -88,46 +78,28 @@ RED.httpAdmin.post('/xmpp-server/:id',function(req,res) {
}); });
function XmppNode(n) { function XmppInNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.server = n.server; this.server = n.server;
try {
this.serverConfig = RED.nodes.getNode(this.server); this.serverConfig = RED.nodes.getNode(this.server);
} catch (err) { this.host = this.serverConfig.server;
} this.port = this.serverConfig.port;
if (this.serverConfig){ this.nick = this.serverConfig.nickname || "Node-RED";
this.host = this.serverConfig.server; this.userid = this.serverConfig.username;
this.port = this.serverConfig.port; this.password = this.serverConfig.password;
this.jid = this.serverConfig.username;
this.password = this.serverConfig.password;
} else if (xmppkey) {
console.warn("no serverConfig found, trying old creds file");
this.host = n.server;
this.port = n.port;
this.jid = xmppkey.jid;
this.password = xmppkey.password;
}
this.join = n.join || false; this.join = n.join || false;
this.nick = n.nick || "Node-RED";
this.sendAll = n.sendObject; this.sendAll = n.sendObject;
this.to = n.to || ""; this.to = n.to || "";
var node = this; var node = this;
setTimeout(function() { var xmpp = new XMPP.SimpleXMPP();
xmpp.connect({
jid : node.jid,
password : node.password,
host : node.host,
port : node.port,
skipPresence : true,
reconnect : false
});
}, 5000);
xmpp.on('online', function() { xmpp.on('online', function() {
node.log('connected to '+node.server); node.log('connected to '+node.host+":"+node.port);
xmpp.setPresence('online', node.nick+' online'); node.status({fill:"green",shape:"dot",text:"connected"});
//xmpp.setPresence('online', node.nick+' online');
if (node.join) { if (node.join) {
xmpp.join(node.to+'/'+node.nick); xmpp.join(node.to+'/'+node.nick);
} }
@ -156,39 +128,126 @@ function XmppNode(n) {
}); });
xmpp.on('error', function(err) { xmpp.on('error', function(err) {
console.error(err); console.error("error",err);
}); });
xmpp.on('close', function(err) { xmpp.on('close', function(err) {
node.log('connection closed'); node.log('connection closed');
node.status({fill:"red",shape:"ring",text:"not connected"});
}); });
xmpp.on('subscribe', function(from) { xmpp.on('subscribe', function(from) {
xmpp.acceptSubscription(from); xmpp.acceptSubscription(from);
}); });
this.on("input", function(msg) { // Now actually make the connection
var to = msg.topic; try {
if (node.to != "") { to = node.to; } xmpp.connect({
if (node.sendAll) { jid : node.userid,
xmpp.send(to, JSON.stringify(msg), node.join); password : node.password,
} host : node.host,
else { port : node.port,
xmpp.send(to, msg.payload, node.join); skipPresence : true,
} reconnect : false
}); });
} catch(e) {
node.error("Bad xmpp configuration");
node.status({fill:"red",shape:"ring",text:"not connected"});
}
this.on("close", function() { node.on("close", function(done) {
xmpp.setPresence('offline'); //xmpp.setPresence('offline');
try { if (xmpp.conn) { xmpp.conn.end(); }
xmpp.disconnect(); xmpp = null;
// TODO - DCJ NOTE... this is not good. It leaves the connection up over a restart - which will end up with bad things happening... done();
// (but requires the underlying xmpp lib to be fixed, which does have an open bug request on fixing the close method - and a work around.
// see - https://github.com/simple-xmpp/node-simple-xmpp/issues/12 for the fix
} catch(e) {
this.warn("Due to an underlying bug in the xmpp library this does not disconnect old sessions. This is bad... A restart would be better.");
}
}); });
} }
RED.nodes.registerType("xmpp in",XmppInNode);
RED.nodes.registerType("xmpp",XmppNode); function XmppOutNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
this.serverConfig = RED.nodes.getNode(this.server);
this.host = this.serverConfig.server;
this.port = this.serverConfig.port;
this.nick = this.serverConfig.nickname || "Node-RED";
this.userid = this.serverConfig.username;
this.password = this.serverConfig.password;
this.join = n.join || false;
this.sendAll = n.sendObject;
this.to = n.to || "";
var node = this;
var xmpp = new XMPP.SimpleXMPP();
xmpp.on('online', function() {
node.log('connected to '+node.host+":"+node.port);
node.status({fill:"green",shape:"dot",text:"connected"});
xmpp.setPresence('online', node.nick+' online');
if (node.join) {
xmpp.join(node.to+'/'+node.nick);
}
});
xmpp.on('error', function(err) {
console.error("error",err);
});
xmpp.on('close', function(err) {
node.log('connection closed');
node.status({fill:"red",shape:"ring",text:"not connected"});
});
xmpp.on('subscribe', function(from) {
xmpp.acceptSubscription(from);
});
// Now actually make the connection
try {
xmpp.connect({
jid : node.userid,
password : node.password,
host : node.host,
port : node.port,
skipPresence : true,
reconnect : false
});
} catch(e) {
node.error("Bad xmpp configuration");
node.status({fill:"red",shape:"ring",text:"not connected"});
}
node.on("input", function(msg) {
if (msg.presence) {
if (['away', 'dnd', 'xa','chat'].indexOf(msg.presence) > -1 ) {
xmpp.setPresence(msg.presence, msg.payload);
}
else { node.warn("Can't set presence - invalid value"); }
}
else {
var to = msg.topic;
if (node.to != "") { to = node.to; }
if (node.sendAll) {
xmpp.send(to, JSON.stringify(msg), node.join);
}
else if (msg.payload) {
if (typeof(msg.payload) === "object") {
xmpp.send(to, JSON.stringify(msg.payload), node.join);
} else {
xmpp.send(to, msg.payload.toString(), node.join);
}
}
}
});
node.on("close", function() {
xmpp.setPresence('offline');
if (xmpp.conn) { xmpp.conn.end(); }
xmpp = null;
});
}
RED.nodes.registerType("xmpp out",XmppOutNode);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 582 B

View File

@ -14,44 +14,47 @@
* limitations under the License. * limitations under the License.
**/ **/
var RED = require(process.env.NODE_RED_HOME+"/red/red"); module.exports = function(RED) {
var SunCalc = require('suncalc'); var SunCalc = require('suncalc');
function SunNode(n) { function SunNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.lat = n.lat; this.lat = n.lat;
this.lon = n.lon; this.lon = n.lon;
this.start = n.start; this.start = n.start;
this.end = n.end; this.end = n.end;
var node = this; var node = this;
var oldval = null; var oldval = null;
this.tick = setInterval(function() { this.tick = setInterval(function() {
var now = new Date(); var now = new Date();
var hour = now.getUTCHours(); var hour = now.getUTCHours();
var mins = now.getUTCMinutes(); var mins = now.getUTCMinutes();
var times = SunCalc.getTimes(now, node.lat, node.lon); var times = SunCalc.getTimes(now, node.lat, node.lon);
var hour1 = times[node.start].getUTCHours(); var hour1 = times[node.start].getUTCHours();
var mins1 = times[node.start].getUTCMinutes(); var mins1 = times[node.start].getUTCMinutes();
var hour2 = times[node.end].getUTCHours(); var hour2 = times[node.end].getUTCHours();
var mins2 = times[node.end].getUTCMinutes(); var mins2 = times[node.end].getUTCMinutes();
var e1 = (hour*60+mins) - (hour1*60+mins1); var e1 = (hour*60+mins) - (hour1*60+mins1);
var e2 = (hour*60+mins) - (hour2*60+mins2); var e2 = (hour*60+mins) - (hour2*60+mins2);
var moon = SunCalc.getMoonIllumination(now).fraction; var moon = SunCalc.getMoonIllumination(now).fraction;
msg = { payload:0, topic:"sun", moon:moon }; msg = { payload:0, topic:"sun", moon:moon };
if ((e1 > 0) & (e2 < 0)) { msg.payload = 1; } if ((e1 > 0) & (e2 < 0)) { msg.payload = 1; }
if (oldval == null) { oldval = msg.payload; } if (oldval == null) { oldval = msg.payload; }
if (msg.payload != oldval) { if (msg.payload == 1) { node.status({fill:"yellow",shape:"dot",text:"day"}); }
oldval = msg.payload; else { node.status({fill:"blue",shape:"dot",text:"night"}); }
msg2 = msg; if (msg.payload != oldval) {
node.send( [msg,msg2] ); oldval = msg.payload;
} msg2 = msg;
else { node.send(msg); } node.send( [msg,msg2] );
}, 60000); }
else { node.send(msg); }
}, 60000);
this.on("close", function() { this.on("close", function() {
clearInterval(this.tick); clearInterval(this.tick);
}); });
}
RED.nodes.registerType("sunrise",SunNode);
} }
RED.nodes.registerType("sunrise",SunNode);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 B

After

Width:  |  Height:  |  Size: 581 B