NLS Core nodes

NLS exec node

NLS function/temple/delay nodes

NLS function/template/delay/trigger/comment nodes

NLS io nodes (mqtt/httpin/websocket/watch/serial)

NLS messages.json for tcpin

NLS io nodes (tcpin & udp half)

NLS io nodes (udp)

NLS logic nodes (switch/change)

NLS logic (range) and parsers (csv&html) nodes

NLS parser nodes (json/xml)

NLS test case update for logic/parsers

NLS analysis and hardware nodes

NLS storage nodes (file/redisout/mongodb) and test

NLS storage node (tail)

NLS social nodes (feedparse/email/irc)

NLS socal node (twitter half change)

NLS social node (twitter) and core node (unknown)
This commit is contained in:
Scott Yoshizawa 2015-05-10 15:47:22 -05:00 committed by Nick O'Leary
parent c105b2df37
commit 2fe859b111
49 changed files with 1903 additions and 1159 deletions

View File

@ -16,8 +16,8 @@
<script type="text/x-red" data-template-name="sentiment">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -40,7 +40,7 @@
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"sentiment";
return this.name||this._("sentiment.label.sentimentlabel");
},
labelStyle: function() {
return this.name?"node_label_italic":"";

View File

@ -19,8 +19,8 @@
<label for="node-input-payloadType"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
<select id="node-input-payloadType" style="width:73%">
<option value="date" data-i18n="inject.timestamp"></option>
<option value="string" data-i18n="inject.string">string</option>
<option value="none" data-i18n="inject.blank">blank</option>
<option value="string" data-i18n="inject.string"></option>
<option value="none" data-i18n="inject.blank"></option>
</select>
</div>
@ -122,7 +122,7 @@
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name">
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips" data-i18n="[html]inject.tip"></div>

View File

@ -16,8 +16,8 @@
<script type="text/x-red" data-template-name="catch">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
<script type="text/x-red" data-help-name="catch">
@ -53,7 +53,7 @@
outputs:1,
icon: "alert.png",
label: function() {
return this.name||"catch";
return this.name||this._("catch.catch");
},
labelStyle: function() {
return this.name?"node_label_italic":"";

View File

@ -16,25 +16,25 @@
<script type="text/x-red" data-template-name="debug">
<div class="form-row">
<label for="node-input-select-complete"><i class="fa fa-list"></i> Output</label>
<label for="node-input-select-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label>
<select type="text" id="node-input-select-complete" style="display: inline-block; width: 250px; vertical-align: top;">
<option value="false">message property</option>
<option value="true">complete msg object</option>
<option value="false" data-i18n="debug.msgprop"></option>
<option value="true" data-i18n="debug.msgobj"></option>
</select>
</div>
<div class="form-row" id="node-prop-row">
<label for="node-input-complete">&nbsp;</label>msg.<input type="text" style="width:208px" id="node-input-complete">
</div>
<div class="form-row">
<label for="node-input-console"><i class="fa fa-random"></i> to</label>
<label for="node-input-console"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
<select type="text" id="node-input-console" style="display: inline-block; width: 250px; vertical-align: top;">
<option value="false">debug tab</option>
<option value="true">debug tab and console</option>
<option value="false" data-i18n="debug.debtab"></option>
<option value="true" data-i18n="debug.tabcon"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>

View File

@ -16,28 +16,28 @@
<script type="text/x-red" data-template-name="exec">
<div class="form-row">
<label for="node-input-command"><i class="fa fa-file"></i> Command</label>
<input type="text" id="node-input-command" placeholder="command">
<label for="node-input-command"><i class="fa fa-file"></i> <span data-i18n="exec.command"></span></label>
<input type="text" id="node-input-command" data-i18n="[placeholder]exec.commandph">
</div>
<div class="form-row">
<label><i class="fa fa-plus"></i> Append</label>
<label><i class="fa fa-plus"></i> <span data-i18n="exec.append"></span></label>
<input type="checkbox" id="node-input-addpay" style="display: inline-block; width: auto; vertical-align: top;">
&nbsp;msg.payload
</div>
<div class="form-row">
<label for="node-input-append"> </label>
<input type="text" id="node-input-append" placeholder="extra input parameters">
<input type="text" id="node-input-append" data-i18n="[placeholder]exec.extraparams">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-useSpawn" placeholder="spawn" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-useSpawn" style="width: 70%;">Use spawn() instead of exec() ?</label>
<label for="node-input-useSpawn" style="width: 70%;"><span data-i18n="exec.spawn"></span></label>
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips" id="spawnTip">Tip: <i>spawn</i> expects only one command word - and appended args to be comma separated.</div>
<div class="form-tips" id="spawnTip"><span data-i18n="[html]exec.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="exec">

View File

@ -63,7 +63,7 @@ module.exports = function(RED) {
node.error(code,msg);
});
}
else { node.error("Spawn command must be just the command - no spaces or extra parameters"); }
else { node.error(RED._("exec.spawnerr")); }
}
else {
var cl = node.cmd;
@ -75,7 +75,7 @@ module.exports = function(RED) {
try {
if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
} catch(e) {
node.log("Bad STDOUT");
node.log(RED._("exec.badstdout"));
}
var msg2 = {payload:stderr};
var msg3 = null;

View File

@ -16,11 +16,11 @@
<script type="text/x-red" data-template-name="function">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-row" style="margin-bottom: 0px;">
<label for="node-input-func"><i class="fa fa-wrench"></i> Function</label>
<label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="function.functionlabel"></span></label>
<input type="hidden" id="node-input-func" autofocus="autofocus">
<input type="hidden" id="node-input-noerr">
</div>
@ -28,10 +28,10 @@
<div style="height: 250px;" class="node-text-editor" id="node-input-func-editor" ></div>
</div>
<div class="form-row">
<label for="node-input-outputs"><i class="fa fa-random"></i> Outputs</label>
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.outputs"></span></label>
<input id="node-input-outputs" style="width: 60px; height: 1.7em;" value="1">
</div>
<div class="form-tips">See the Info tab for help writing functions.</div>
<div class="form-tips"><span data-i18n="function.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="function">

View File

@ -16,11 +16,11 @@
<script type="text/x-red" data-template-name="template">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-row" style="margin-bottom: 0px;">
<label for="node-input-template"><i class="fa fa-file-code-o"></i> Template</label>
<label for="node-input-template"><i class="fa fa-file-code-o"></i> <span data-i18n="template.templatelabel"></span></label>
<input type="hidden" id="node-input-template" autofocus="autofocus">
<select id="node-input-format" style=" font-size: 0.8em; margin-bottom: 3px; width:110px; float:right;">
<option value="handlebars">mustache</option>
@ -34,7 +34,7 @@
<div style="height: 250px;" class="node-text-editor" id="node-input-template-editor" ></div>
</div>
<div class="form-row">
<label for="node-input-field"><i class="fa fa-edit"></i> Property</label>
<label for="node-input-field"><i class="fa fa-edit"></i> <span data-i18n="template.property"></span></label>
msg.<input type="text" id="node-input-field" placeholder="payload" style="width:170px;">
</div>
</script>

View File

@ -18,58 +18,58 @@
<script type="text/x-red" data-template-name="delay">
<div class="form-row">
<label for="node-input-pauseType"><i class="fa fa-tasks"></i> Action</label>
<label for="node-input-pauseType"><i class="fa fa-tasks"></i> <span data-i18n="delay.action"></span></label>
<select id="node-input-pauseType" style="width:270px !important">
<option value="delay">Delay message</option>
<option value="random">Random delay</option>
<option value="rate">Limit rate to</option>
<option value="queue">Topic based fair queue</option>
<option value="delay" data-i18n="delay.delaymsg"></option>
<option value="random" data-i18n="delay.ramdomdelay"></option>
<option value="rate" data-i18n="delay.limitrate"></option>
<option value="queue" data-i18n="delay.fairqueue"></option>
</select>
</div>
<div id="delay-details" class="form-row">
<label for="node-input-timeout"><i class="fa fa-clock-o"></i> For</label>
<label for="node-input-timeout"><i class="fa fa-clock-o"></i> <span data-i18n="delay.for"></span></label>
<input type="text" id="node-input-timeout" placeholder="Time" style="direction:rtl; width:50px !important">
<select id="node-input-timeoutUnits" style="width:200px !important">
<option value="milliseconds">Milliseconds</option>
<option value="seconds">Seconds</option>
<option value="minutes">Minutes</option>
<option value="hours">Hours</option>
<option value="days">Days</option>
<option value="milliseconds" data-i18n="delay.milisecs"></option>
<option value="seconds" data-i18n="delay.secs"></option>
<option value="minutes" data-i18n="delay.mins"></option>
<option value="hours" data-i18n="delay.hours"></option>
<option value="days" data-i18n="delay.days"></option>
</select>
</div>
<div id="rate-details" class="form-row">
<label for="node-input-rate"><i class="fa fa-clock-o"></i> Rate</label>
<label for="node-input-rate"><i class="fa fa-clock-o"></i> <span data-i18n="delay.rate"></span></label>
<input type="text" id="node-input-rate" placeholder="1" style="direction:rtl; width:30px !important">
<label for="node-input-rateUnits">msg(s) per</label>
<label for="node-input-rateUnits"><span data-i18n="delay.msgper"></span></label>
<select id="node-input-rateUnits" style="width:140px !important">
<option value="second">Second</option>
<option value="minute">Minute</option>
<option value="hour">Hour</option>
<option value="day">Day</option>
<option value="second" data-i18n="delay.sec"></option>
<option value="minute" data-i18n="delay.min"></option>
<option value="hour" data-i18n="delay.hour"></option>
<option value="day" data-i18n="delay.day"></option>
</select>
<br/>
<div id="node-input-dr"><input style="margin: 20px 0 20px 100px; width: 30px;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop">drop intermediate messages</label></div>
<div id="node-input-dr"><input style="margin: 20px 0 20px 100px; width: 30px;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop"><span data-i18n="delay.dropmsg"></span></label></div>
</div>
<div id="random-details" class="form-row">
<label for="node-input-randomFirst"><i class="fa fa-clock-o"></i> Between</label>
<label for="node-input-randomFirst"><i class="fa fa-clock-o"></i> <span data-i18n="delay.between"></span></label>
<input type="text" id="node-input-randomFirst" placeholder="" style="directon:rtl; width:30px !important">
<label for="node-input-randomLast" style="width:20px"> &amp; </label>
<input type="text" id="node-input-randomLast" placeholder="" style="directon:rtl; width:30px !important">
<select id="node-input-randomUnits" style="width:140px !important">
<option value="milliseconds">Milliseconds</option>
<option value="seconds">Seconds</option>
<option value="minutes">Minutes</option>
<option value="hours">Hours</option>
<option value="days">Days</option>
<option value="milliseconds" data-i18n="delay.milisecs"></option>
<option value="seconds" data-i18n="delay.secs"></option>
<option value="minutes" data-i18n="delay.mins"></option>
<option value="hours" data-i18n="delay.hours"></option>
<option value="days" data-i18n="delay.days"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -109,16 +109,16 @@
if (this.pauseType == "delay") {
var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
if (this.timeoutUnits == "milliseconds") { units = "ms"; }
return this.name||"delay "+this.timeout+" " + units;
return this.name||this._("delay.delaylabel")+" "+this.timeout+" " + units;
} else if (this.pauseType == "rate") {
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
return this.name||"limit "+this.rate+" msg/"+ units;
return this.name||this._("delay.limitlabel")+" "+this.rate+" "+this._("delay.msgperlabel")+ units;
} else if (this.pauseType == "random") {
return this.name || "random";
return this.name || this._("delay.randomlabel");
}
else {
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
return this.name || "queue" +this.rate+" msg/"+ units;
return this.name || this._("delay.queuelabel") +this.rate+" "+this._("delay.msgperlabel")+ units;
}
},
labelStyle: function() { // sets the class to apply to the label

View File

@ -104,7 +104,7 @@ module.exports = function(RED) {
node.status({text:node.buffer.length});
}
if (node.buffer.length > 1000) {
node.warn(this.name + " buffer exceeded 1000 messages");
node.warn(this.name + " " + RED._("delay.buffererr"));
}
} else {
node.send(msg);

View File

@ -1,5 +1,5 @@
<!--
Copyright 2014, 2015 IBM Corp.
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.
@ -16,48 +16,64 @@
<script type="text/x-red" data-template-name="trigger">
<div class="form-row">
Send
<select id="node-input-op1type" style="width:200px !important">
<option value="val">the string payload</option>
<option value="pay">the existing message</option>
<option value="nul">nothing</option>
<label for="node-input-op1type"><i class="fa fa-arrow-up"></i> <span data-i18n="trigger.output1"></span></label>
<select id="node-input-op1type" style="width:73% !important">
<option value="val" data-i18n="trigger.below"></option>
<option value="pay" data-i18n="trigger.payload"></option>
<option value="nul" data-i18n="trigger.nothing"></option>
</select>
<input style="width: 180px !important" type="text" id="node-input-op1">
</div>
<div class="form-row" id="node-op1">
<label for="node-input-op1">&nbsp;</label>
<input type="text" id="node-input-op1">
</div>
<div class="form-row">
then
<select id="node-then-type" style="width:150px;">
<option value="block">wait to be reset</option>
<option value="wait">wait for</option>
<label for="node-input-duration"><i class="fa fa-clock-o"></i> <span data-i18n="trigger.wait"></span></label>
<input type="text" id="node-input-duration" placeholder="250" style="direction:rtl; width:70px !important">
<select id="node-input-units" style="width:140px !important">
<option value="ms" data-i18n="trigger.milisecs"></option>
<option value="s" data-i18n="trigger.secs"></option>
<option value="min" data-i18n="trigger.mins"></option>
<option value="hr" data-i18n="trigger.hours"></option>
</select>
<span class="node-type-wait">
<input type="text" id="node-input-duration" style="width:70px !important">
<select id="node-input-units" style="width:140px !important">
<option value="ms">Milliseconds</option>
<option value="s">Seconds</option>
<option value="min">Minutes</option>
<option value="hr">Hours</option>
</select>
</span>
</div>
<div class="form-row node-type-wait">
then send
<select id="node-input-op2type" style="width:200px !important">
<option value="val">the string payload</option>
<option value="pay">the existing message</option>
<option value="nul">nothing</option>
</select>
<input style="width: 145px !important" type="text" id="node-input-op2">
</div>
<div class="form-row node-type-wait">
<input type="checkbox" id="node-input-extend" style="margin-left: 5px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-extend">extend delay if new message arrives</label>
</div>
<br/>
<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">
<label for="node-input-op2type"><i class="fa fa-arrow-down"></i> <span data-i18n="trigger.output2"></span></label>
<select id="node-input-op2type" style="width:73% !important">
<option value="val" data-i18n="trigger.below"></option>
<option value="pay" data-i18n="trigger.payload"></option>
<option value="nul" data-i18n="trigger.nothing"></option>
</select>
</div>
<div class="form-tips">The node can be reset by sending a message with the <b>msg.reset</b> property set</div>
<div class="form-row" id="node-op2">
<label for="node-input-op2">&nbsp;</label>
<input type="text" id="node-input-op2">
</div>
<div class="form-row">
<label for="node-input-extend"><i class="fa fa-repeat"></i> <span data-i18n="trigger.and"></span></label>
<select id="node-input-extend" style="width:73% !important">
<option value="false" data-i18n="trigger.notext"></option>
<option value="true" data-i18n="trigger.extend"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<!-- <div class="form-tips">Tip: Outputs can be values, null, {{templated}} or msg.payload<br/> -->
<div class="form-tips"><span data-i18n="trigger.tip"></span></div>
<script>
{
$("#node-input-op1type").change(function() {
if ($("#node-input-op1type").val() == "val") { $("#node-op1").show(); }
else { $("#node-op1").hide(); }
});
$("#node-input-op2type").change(function() {
if ($("#node-input-op2type").val() == "val") { $("#node-op2").show(); }
else { $("#node-op2").hide(); }
});
}
</script>
</script>
<script type="text/x-red" data-help-name="trigger">
@ -95,60 +111,20 @@
icon: "trigger.png",
label: function() {
if (this.duration > 0) {
return this.name|| "trigger"+" "+this.duration+this.units;
return this.name|| this._("trigger.triggerlabel")+" "+this.duration+this.units;
}
else {
return this.name|| "trigger & block";
return this.name|| this._("trigger.triggeroncelabel");
}
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
$("#node-then-type").change(function() {
if ($(this).val() == "block") {
$(".node-type-wait").hide();
$(".form-tips").show();
} else {
$(".node-type-wait").show();
$(".form-tips").hide();
}
$( "#node-input-duration" ).spinner({
min:1,
increment:25
});
$("#node-input-op1type").change(function() {
if ($(this).val() == "val") {
$("#node-input-op1").show();
} else {
$("#node-input-op1").hide();
}
});
$("#node-input-op2type").change(function() {
if ($(this).val() == "val") {
$("#node-input-op2").show();
} else {
$("#node-input-op2").hide();
}
});
if (this.duration == "0") {
$("#node-then-type").val("block");
} else {
$("#node-then-type").val("wait");
}
$("#node-then-type").change();
$("#node-input-op1type").change();
$("#node-input-op2type").change();
if (this.extend === "true" || this.extend === true) {
$("#node-input-extend").prop("checked",true);
} else {
$("#node-input-extend").prop("checked",false);
}
},
oneditsave: function() {
if ($("#node-then-type").val() == "block") {
$("#node-input-duration").val("0");
}
}
});
</script>

View File

@ -16,17 +16,17 @@
<script type="text/x-red" data-template-name="comment">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-comment"></i> Title</label>
<input type="text" id="node-input-name" placeholder="Comment">
<label for="node-input-name"><i class="fa fa-comment"></i> <span data-i18n="comment.title"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]comment.commentph">
</div>
<div class="form-row" style="margin-bottom: 0px;">
<label for="node-input-info" style="width: 100% !important;"><i class="fa fa-comments"></i> Body - will be rendered in info tab.</label>
<label for="node-input-info" style="width: 100% !important;"><i class="fa fa-comments"></i> <span data-i18n="comment.body"></span></label>
<input type="hidden" id="node-input-info" autofocus="autofocus">
</div>
<div class="form-row node-text-editor-row">
<div style="height: 250px;" class="node-text-editor" id="node-input-info-editor" ></div>
</div>
<div class="form-tips">Tip: The text here can be styled as <i><a href="https://help.github.com/articles/markdown-basics/" target="_new">Github flavoured Markdown</a></i></div>
<div class="form-tips"><span data-i18n="comment.tip1"></span><i><a href="https://help.github.com/articles/markdown-basics/" target="_new"><span data-i18n="comment.tip2"></span></a></i></div>
</script>
<script type="text/x-red" data-help-name="comment">
@ -51,8 +51,8 @@
return this.name?"node_label_italic":"";
},
info: function() {
var t = this.name || "Comment node";
var b = this.info || "Use this node to add simple documentation.\n\nAnything you add will be rendered in this info panel.\n\nYou may use Markdown syntax to **enhance** the *presentation*.";
var t = this.name || this._("comment.commentnode");
var b = this.info || this._("comment.commentinfo");
return "### "+t+"\n"+b;
},
oneditprepare: function() {

View File

@ -15,10 +15,7 @@
-->
<script type="text/x-red" data-template-name="unknown">
<div class="form-tips"><p>This node is a type unknown to your installation of Node-RED.</p>
<p><i>If you deploy with the node in this state, it's configuration will be preserved, but
the flow will not start until the missing type is installed.</i></p>
<p>See the Info side bar for more help</p></div>
<div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="unknown">
@ -42,7 +39,7 @@
outputs:1,
icon: "",
label: function() {
return "("+this.name+")"||"unknown";
return "("+this.name+")"||this._("unknown.label.unknownlabel");
},
labelStyle: function() {
return "node_label_unknown";

View File

@ -16,9 +16,9 @@
<script type="text/x-red" data-template-name="rpi-gpio in">
<div class="form-row">
<label for="node-input-pin"><i class="fa fa-circle"></i> GPIO Pin</label>
<label for="node-input-pin"><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.label.gpiopin"></span></label>
<select type="text" id="node-input-pin" style="width: 250px;">
<option value='' disabled selected style='display:none;'>select pin</option>
<option value='' disabled selected style='display:none;'><span data-i18n="rpi-gpio.label.selectpin"></span></option>
<option value="3">3 - SDA1 - BCM2</option>
<option value="5">5 - SCL1 - BCM3</option>
<option value="7">7 - GPIO7 - BCM4</option>
@ -40,25 +40,25 @@
&nbsp;<span id="pitype"></span>
</div>
<div class="form-row">
<label for="node-input-intype"><i class="fa fa-level-up"></i> Resistor ?</label>
<label for="node-input-intype"><i class="fa fa-level-up"></i> <span data-i18n="rpi-gpio.label.registor"></span></label>
<select type="text" id="node-input-intype" style="width: 150px;">
<option value="tri">none</option>
<option value="up">pullup</option>
<option value="down">pulldown</option>
<option value="tri" data-i18n="rpi-gpio.none"></option>
<option value="up" data-i18n="rpi-gpio.pullup"></option>
<option value="down" data-i18n="rpi-gpio.pulldown"></option>
</select>
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-read" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-read" style="width: 70%;">Read initial state of pin on deploy/restart ?</label>
<label for="node-input-read" style="width: 70%;"><span data-i18n="rpi-gpio.label.readinitial"></span></label>
</div>
<br/>
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips" id="pin-tip"><b>Pins in Use</b>: </div>
<div class="form-tips">Tip: Only Digital Input is supported - input must be 0 or 1.</div>
<div class="form-tips" id="pin-tip"><span data-i18n="[html]rpi-gpio.pin-tip"></span></div>
<div class="form-tips"><span data-i18n="[html]rpi-gpio.in-tip"></span></div>
</script>
<script type="text/x-red" data-help-name="rpi-gpio in">
@ -97,6 +97,10 @@
},
oneditprepare: function() {
var pinnow = this.pin;
var pintip = this._("rpi-gpio.pin-tip");
var pinname = this._("rpi-gpio.pinname");
var alreadyuse = this._("rpi-gpio.alreadyuse");
var alreadyset = this._("rpi-gpio.alreadyset");
$.getJSON('rpi-gpio/'+this.id,function(data) {
$('#pitype').text(data.type);
if ((data.type === "Model B+") || (data.type === "Model A+")) {
@ -117,14 +121,14 @@
$.getJSON('rpi-pins/'+this.id,function(data) {
pinsInUse = data || {};
$('#pin-tip').html("<b>Pins in Use</b>: "+Object.keys(data));
$('#pin-tip').html(pintip + Object.keys(data));
});
$("#node-input-pin").change(function() {
var pinnew = $("#node-input-pin").val();
if ((pinnew) && (pinnew !== pinnow)) {
if (pinsInUse.hasOwnProperty(pinnew)) {
RED.notify("Pin "+pinnew+" already in use.","warn");
RED.notify(pinname+" "+pinnew+" "+alreadyuse,"warn");
}
pinnow = pinnew;
}
@ -133,7 +137,7 @@
$("#node-input-intype").change(function() {
var newtype = $("#node-input-intype option:selected").val();
if ((pinsInUse.hasOwnProperty(pinnow)) && (pinsInUse[pinnow] !== newtype)) {
RED.notify("Pin "+pinnow+" already set as "+pinsInUse[pinnow],"error");
RED.notify(pinname+" "+pinnow+" "+alreadyset+" "+pinsInUse[pinnow],"error");
}
});
}
@ -142,9 +146,9 @@
<script type="text/x-red" data-template-name="rpi-gpio out">
<div class="form-row">
<label for="node-input-pin"><i class="fa fa-circle"></i> GPIO Pin</label>
<label for="node-input-pin"><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.label.gpiopin"></span></label>
<select type="text" id="node-input-pin" style="width: 250px;">
<option value='' disabled selected style='display:none;'>select pin</option>
<option value='' disabled selected style='display:none;'><span data-i18n="rpi-gpio.label.selectpin"></span></option>
<option value="3">3 - SDA1 - BCM2</option>
<option value="5">5 - SCL1 - BCM3</option>
<option value="7">7 - GPIO7 - BCM4</option>
@ -166,32 +170,32 @@
&nbsp;<span id="pitype"></span>
</div>
<div class="form-row" id="node-set-pwm">
<label>&nbsp;&nbsp;&nbsp;&nbsp;Type</label>
<label>&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="rpi-gpio.label.type"></span></label>
<select id="node-input-out" style="width: 250px;">
<option value="out">Digital output</option>
<option value="pwm">PWM output</option>
<option value="out" data-i18n="rpi-gpio.digout"></option>
<option value="pwm" data-i18n="rpi-gpio.pwmout"></option>
</select>
</div>
<div class="form-row" id="node-set-tick">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-set" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-set" style="width: 70%;">Initialise pin state ?</label>
<label for="node-input-set" style="width: 70%;"><span data-i18n="rpi-gpio.label.initpin"></span></label>
</div>
<div class="form-row" id="node-set-state">
<label for="node-input-level">&nbsp;</label>
<select id="node-input-level" style="width: 250px;">
<option value="0">initial level of pin - low (0)</option>
<option value="1">initial level of pin - high (1)</option>
<option value="0" data-i18n="rpi-gpio.initpin0"></option>
<option value="1" data-i18n="rpi-gpio.initpin1"></option>
</select>
</div>
<br/>
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips" id="pin-tip"><b>Pins in Use</b>: </div>
<div class="form-tips" id="dig-tip"><b>Tip</b>: For digital output - input must be 0 or 1.</div>
<div class="form-tips" id="pwm-tip"><b>Tip</b>: For PWM output - input must be between 0 to 100.</div>
<div class="form-tips" id="pin-tip"><span data-i18n="[html]rpi-gpio.pin-tip"></span></div>
<div class="form-tips" id="dig-tip"><span data-i18n="[html]rpi-gpio.dig-tip"></span></div>
<div class="form-tips" id="pwm-tip"><span data-i18n="[html]rpi-gpio.pwm-tip"></span></div>
</script>
<script type="text/x-red" data-help-name="rpi-gpio out">
@ -234,6 +238,10 @@
},
oneditprepare: function() {
var pinnow = this.pin;
var pintip = this._("rpi-gpio.pin-tip");
var pinname = this._("rpi-gpio.pinname");
var alreadyuse = this._("rpi-gpio.alreadyuse");
var alreadyset = this._("rpi-gpio.alreadyset");
if (!$("#node-input-out").val()) { $("#node-input-out").val("out"); }
$.getJSON('rpi-gpio/'+this.id,function(data) {
$('#pitype').text(data.type);
@ -255,14 +263,14 @@
$.getJSON('rpi-pins/'+this.id,function(data) {
pinsInUse = data || {};
$('#pin-tip').html("<b>Pins in Use</b>: "+Object.keys(data));
$('#pin-tip').html(pintip + Object.keys(data));
});
$("#node-input-pin").change(function() {
var pinnew = $("#node-input-pin").val();
if ((pinnew) && (pinnew !== pinnow)) {
if (pinsInUse.hasOwnProperty(pinnew)) {
RED.notify("Pin "+pinnew+" already in use.","warn");
RED.notify(pinname+" "+pinnew+" "+alreadyuse,"warn");
}
pinnow = pinnew;
}
@ -271,7 +279,7 @@
$("#node-input-out").change(function() {
var newtype = $("#node-input-out option:selected").val();
if ((pinsInUse.hasOwnProperty(pinnow)) && (pinsInUse[pinnow] !== newtype)) {
RED.notify("Pin "+pinnow+" already set as "+pinsInUse[pinnow],"error");
RED.notify(pinname+" "+pinnow+" "+alreadyset+" "+pinsInUse[pinnow],"error");
}
});
@ -307,17 +315,17 @@
<script type="text/x-red" data-template-name="rpi-mouse">
<div class="form-row">
<label for="node-input-butt"><i class="fa fa-circle"></i> Button</label>
<label for="node-input-butt"><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.label.button"></span></label>
<select type="text" id="node-input-butt" style="width: 250px;">
<option value="1">left</option>
<option value="2">right</option>
<option value="4">middle</option>
<option value="7">any</option>
<option value="1" data-i18n="rpi-gpio.left"></option>
<option value="2" data-i18n="rpi-gpio.right"></option>
<option value="4" data-i18n="rpi-gpio.middle"></option>
<option value="7" data-i18n="rpi-gpio.any"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -341,10 +349,10 @@
outputs:1,
icon: "rpi.png",
label: function() {
var na = "Pi Mouse";
if (this.butt === "1") { na += " Left"; }
if (this.butt === "2") { na += " Right"; }
if (this.butt === "4") { na += " Middle"; }
var na = this._("rpi-gpio.label.pimouse");
if (this.butt === "1") { na += " "+this._("rpi-gpio.label.left"); }
if (this.butt === "2") { na += " "+this._("rpi-gpio.label.right"); }
if (this.butt === "4") { na += " "+this._("rpi-gpio.label.middle"); }
return this.name||na;
},
labelStyle: function() {

View File

@ -23,25 +23,25 @@ module.exports = function(RED) {
var gpioCommand = __dirname+'/nrgpio';
if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi
//RED.log.info("Ignoring Raspberry Pi specific node.");
throw "Info : Ignoring Raspberry Pi specific node.";
//RED.log.info(RED._("rpi-gpio.errors.ignorenode"));
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}
if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
RED.log.warn("Can't find Pi RPi.GPIO python library.");
throw "Warning : Can't find Pi RPi.GPIO python library.";
RED.log.warn(RED._("rpi-gpio.errors.libnotfound"));
throw "Warning : "+RED._("rpi-gpio.errors.libnotfound");
}
if ( !(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
RED.log.error(gpioCommand+" needs to be executable.");
throw "Error : nrgpio must to be executable.";
RED.log.error(gpioCommand+" "+RED._("rpi-gpio.errors.needtobeexecutable"));
throw "Error : "+RED._("rpi-gpio.errors.mustbeexecutable");
}
// the magic to make python print stuff immediately
process.env.PYTHONUNBUFFERED = 1;
var pinsInUse = {};
var pinTypes = {"out":"digital output", "tri":"input", "up":"input with pull up", "down":"input with pull down", "pwm":"PWM output"};
var pinTypes = {"out":RED._("rpi-gpio.errors.digout"), "tri":RED._("rpi-gpio.errors.input"), "up":RED._("rpi-gpio.errors.pullup"), "down":RED._("rpi-gpio.errors.pulldown"), "pwm":RED._("rpi-gpio.errors.pwmout")};
function GPIOInNode(n) {
RED.nodes.createNode(this,n);
@ -56,7 +56,7 @@ module.exports = function(RED) {
}
else {
if ((pinsInUse[this.pin] !== this.intype)||(pinsInUse[this.pin] === "pwm")) {
node.warn("GPIO pin "+this.pin+" already set as "+pinTypes[pinsInUse[this.pin]]);
node.warn(RED._("rpi-gpio.errors.gpiopin")+" "+this.pin+" "+RED._("rpi-gpio.errors.alreadyset")+" "+pinTypes[pinsInUse[this.pin]]);
}
}
@ -67,7 +67,7 @@ module.exports = function(RED) {
node.child = spawn(gpioCommand, ["in",node.pin,node.intype]);
}
node.running = true;
node.status({fill:"green",shape:"dot",text:"OK"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.ok")});
node.child.stdout.on('data', function (data) {
data = data.toString().trim();
@ -88,27 +88,27 @@ module.exports = function(RED) {
node.child.on('close', function (code) {
node.child = null;
node.running = false;
if (RED.settings.verbose) { node.log("closed"); }
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.errors.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"closed"});
node.status({fill:"grey",shape:"ring",text:RED._("common.status.closed")});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"stopped"}); }
else { node.status({fill:"red",shape:"ring",text:RED._("common.status.stopped")}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error('nrgpio command not found'); }
else if (err.errno === "EACCES") { node.error('nrgpio command not executable'); }
else { node.error('error: ' + err.errno); }
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
}
else {
node.warn("Invalid GPIO pin: "+node.pin);
node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin);
}
node.on("close", function(done) {
node.status({fill:"grey",shape:"ring",text:"close"});
node.status({fill:"grey",shape:"ring",text:RED._("common.status.close")});
delete pinsInUse[node.pin];
if (node.child != null) {
node.done = done;
@ -133,7 +133,7 @@ module.exports = function(RED) {
}
else {
if ((pinsInUse[this.pin] !== this.out)||(pinsInUse[this.pin] === "pwm")) {
node.warn("GPIO pin "+this.pin+" already set as "+pinTypes[pinsInUse[this.pin]]);
node.warn(RED._("rpi-gpio.errors.gpiopin")+" "+this.pin+" "+RED._("rpi-gpio.errors.alreadyset")+" "+pinTypes[pinsInUse[this.pin]]);
}
}
@ -150,11 +150,11 @@ module.exports = function(RED) {
node.status({fill:"green",shape:"dot",text:msg.payload.toString()});
}
else {
node.error("nrpgio python command not running",msg);
node.status({fill:"red",shape:"ring",text:"not running"});
node.error(RED._("rpi-gpio.errors.pythoncommandnotfound"),msg);
node.status({fill:"red",shape:"ring",text:RED._("common.status.not-running")});
}
}
else { node.warn("Invalid input: "+out); }
else { node.warn(RED._("rpi-gpio.errors.invalidinput")+": "+out); }
}
if (node.pin !== undefined) {
@ -164,7 +164,7 @@ module.exports = function(RED) {
node.child = spawn(gpioCommand, [node.out,node.pin]);
}
node.running = true;
node.status({fill:"green",shape:"dot",text:"OK"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.ok")});
node.on("input", inputlistener);
@ -179,27 +179,27 @@ module.exports = function(RED) {
node.child.on('close', function (code) {
node.child = null;
node.running = false;
if (RED.settings.verbose) { node.log("closed"); }
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.errors.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"closed"});
node.status({fill:"grey",shape:"ring",text:RED._("common.status.closed")});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"stopped"}); }
else { node.status({fill:"red",shape:"ring",text:RED._("common.status.stopped")}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error('nrgpio command not found'); }
else if (err.errno === "EACCES") { node.error('nrgpio command not executable'); }
else { node.error('error: ' + err.errno); }
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
}
else {
node.warn("Invalid GPIO pin: "+node.pin);
node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin);
}
node.on("close", function(done) {
node.status({fill:"grey",shape:"ring",text:"close"});
node.status({fill:"grey",shape:"ring",text:RED._("common.status.close")});
delete pinsInUse[node.pin];
if (node.child != null) {
node.done = done;
@ -214,14 +214,14 @@ module.exports = function(RED) {
var pitype = { type:"" };
exec(gpioCommand+" rev 0", function(err,stdout,stderr) {
if (err) {
RED.log.info('Version command failed for some reason.');
RED.log.info(RED._("rpi-gpio.errors.version"));
}
else {
if (stdout.trim() == "0") { pitype = { type:"Compute" }; }
else if (stdout.trim() == "1") { pitype = { type:"A/B v1" }; }
else if (stdout.trim() == "2") { pitype = { type:"A/B v2" }; }
else if (stdout.trim() == "3") { pitype = { type:"Model B+" }; }
else { RED.log.info("Saw Pi Type",stdout.trim()); }
else { RED.log.info(RED._("rpi-gpio.errors.sawpitype"),stdout.trim()); }
}
});
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
@ -232,7 +232,7 @@ module.exports = function(RED) {
var node = this;
node.child = spawn(gpioCommand+".py", ["mouse",node.butt]);
node.status({fill:"green",shape:"dot",text:"OK"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.ok")});
node.child.stdout.on('data', function (data) {
data = Number(data);
@ -247,22 +247,22 @@ module.exports = function(RED) {
node.child.on('close', function (code) {
node.child = null;
node.running = false;
if (RED.settings.verbose) { node.log("closed"); }
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.errors.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"closed"});
node.status({fill:"grey",shape:"ring",text:RED._("common.status.closed")});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"stopped"}); }
else { node.status({fill:"red",shape:"ring",text:RED._("common.status.stopped")}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error('nrgpio command not found'); }
else if (err.errno === "EACCES") { node.error('nrgpio ommand not executable'); }
else { node.error('error: ' + err.errno); }
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
node.on("close", function(done) {
node.status({fill:"grey",shape:"ring",text:"close"});
node.status({fill:"grey",shape:"ring",text:RED._("common.status.close")});
if (node.child != null) {
node.done = done;
node.child.kill('SIGINT');

View File

@ -16,16 +16,16 @@
<script type="text/x-red" data-template-name="mqtt in">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> Broker</label>
<label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input type="text" id="node-input-broker">
</div>
<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">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topicph">
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -58,32 +58,32 @@
<script type="text/x-red" data-template-name="mqtt out">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> Broker</label>
<label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input type="text" id="node-input-broker">
</div>
<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">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topicph">
</div>
<div class="form-row">
<label for="node-input-qos"><i class="fa fa-empire"></i> QoS</label>
<label for="node-input-qos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
<select id="node-input-qos" style="width:125px !important">
<option value=""></option>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
&nbsp;&nbsp;<i class="fa fa-history"></i>&nbsp;Retain &nbsp;<select id="node-input-retain" style="width:125px !important">
&nbsp;&nbsp;<i class="fa fa-history"></i>&nbsp;<span data-i18n="mqtt.retain"></span> &nbsp;<select id="node-input-retain" style="width:125px !important">
<option value=""></option>
<option value="false">false</option>
<option value="true">true</option>
<option value="false" data-i18n="mqtt.false"></option>
<option value="true" data-i18n="mqtt.true"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips">Tip: Leave topic, qos or retain blank if you want to set them via msg properties.</div>
<div class="form-tips"><span data-i18n="mqtt.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="mqtt out">
@ -118,21 +118,21 @@
<script type="text/x-red" data-template-name="mqtt-broker">
<div class="form-row node-input-broker">
<label for="node-config-input-broker"><i class="fa fa-globe"></i> Broker</label>
<label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="localhost" style="width: 40%;" >
<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">
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.port"></span></label>
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.portph" style="width:45px">
</div>
<div class="form-row">
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> Client ID</label>
<input type="text" id="node-config-input-clientid" placeholder="Leave blank for auto generated">
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label>
<input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.label.clientidph">
</div>
<div class="form-row">
<label for="node-config-input-user"><i class="fa fa-user"></i> Username</label>
<label for="node-config-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
<input type="text" id="node-config-input-user">
</div>
<div class="form-row">
<label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
<input type="password" id="node-config-input-password">
</div>
</script>

View File

@ -42,7 +42,7 @@ module.exports = function(RED) {
this.broker = n.broker;
this.brokerConfig = RED.nodes.getNode(this.broker);
if (this.brokerConfig) {
this.status({fill:"red",shape:"ring",text:"disconnected"});
this.status({fill:"red",shape:"ring",text:RED._("common.status.disconnected")});
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password);
var node = this;
if (this.topic) {
@ -55,22 +55,22 @@ module.exports = function(RED) {
node.send(msg);
}, this.id);
this.client.on("connectionlost",function() {
node.status({fill:"red",shape:"ring",text:"disconnected"});
node.status({fill:"red",shape:"ring",text:RED._("common.status.disconnected")});
});
this.client.on("connect",function() {
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
});
if (this.client.isConnected()) {
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
} else {
this.client.connect();
}
}
else {
this.error("topic not defined");
this.error(RED._("mqtt.errors.not-defined"));
}
} else {
this.error("missing broker configuration");
this.error(RED._("mqtt.errors.missing-config"));
}
this.on('close', function() {
if (this.client) {
@ -90,7 +90,7 @@ module.exports = function(RED) {
this.brokerConfig = RED.nodes.getNode(this.broker);
if (this.brokerConfig) {
this.status({fill:"red",shape:"ring",text:"disconnected"});
this.status({fill:"red",shape:"ring",text:RED._("common.status.disconnected")});
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password);
var node = this;
this.on("input",function(msg) {
@ -110,22 +110,22 @@ module.exports = function(RED) {
if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist
this.client.publish(msg); // send the message
}
else { node.warn("Invalid topic specified"); }
else { node.warn(RED._("mqtt.errors.invalid-topic")); }
}
});
this.client.on("connectionlost",function() {
node.status({fill:"red",shape:"ring",text:"disconnected"});
node.status({fill:"red",shape:"ring",text:RED._("common.status.disconnected")});
});
this.client.on("connect",function() {
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
});
if (this.client.isConnected()) {
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
} else {
this.client.connect();
}
} else {
this.error("missing broker configuration");
this.error(RED._("mqtt.errors.missing-config"));
}
this.on('close', function() {
if (this.client) {

View File

@ -16,7 +16,7 @@
<script type="text/x-red" data-template-name="http in">
<div class="form-row">
<label for="node-input-method"><i class="fa fa-tasks"></i> Method</label>
<label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
<select type="text" id="node-input-method" style="width:72%;">
<option value="get">GET</option>
<option value="post">POST</option>
@ -25,18 +25,18 @@
</select>
</div>
<div class="form-row">
<label for="node-input-url"><i class="fa fa-globe"></i> url</label>
<input type="text" id="node-input-url" placeholder="/url">
<label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
<input type="text" id="node-input-url" data-i18n="[placeholder]httpin.label.urlph">
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-row row-swagger-doc">
<label for="node-input-name"><i class="fa fa-tag"></i> Doc</label>
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="httpin.label.doc"></span></label>
<input type="text" id="node-input-swaggerDoc">
</div>
<div id="node-input-tip" class="form-tips">The url will be relative to <code><span id="node-input-path"></span></code>.</div>
<div id="node-input-tip" class="form-tips"><span data-i18n="httpin.in-tip"></span><code><span id="node-input-path"></span></code>.</div>
</script>
<script type="text/x-red" data-help-name="http in">
@ -67,10 +67,10 @@
<script type="text/x-red" data-template-name="http response">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips">The messages sent to this node <b>must</b> originate from an <i>http input</i> node</div>
<div class="form-tips"><span data-i18n="[html]httpin.res-tip"></span></div>
</script>
<script type="text/x-red" data-help-name="http response">
@ -86,45 +86,45 @@
<script type="text/x-red" data-template-name="http request">
<div class="form-row">
<label for="node-input-method"><i class="fa fa-tasks"></i> Method</label>
<label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
<select type="text" id="node-input-method" style="width:72%;">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
<option value="use">- set by msg.method -</option>
<option value="use" data-i18n="httpin.setby"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-url"><i class="fa fa-globe"></i> URL</label>
<input type="text" id="node-input-url" placeholder="http://">
<label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
<input type="text" id="node-input-url" data-i18n="[placeholder]httpin.label.httpph">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-useAuth" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-useAuth" style="width: 70%;">Use basic authentication ?</label>
<label for="node-input-useAuth" style="width: 70%;"><span data-i18n="httpin.basicauth"></span></label>
</div>
<div class="form-row node-input-useAuth-row">
<label for="node-input-user"><i class="fa fa-user"></i> Username</label>
<label for="node-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
<input type="text" id="node-input-user">
</div>
<div class="form-row node-input-useAuth-row">
<label for="node-input-password"><i class="fa fa-lock"></i> Password</label>
<label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
<input type="password" id="node-input-password">
</div>
<div class="form-row">
<label for="node-input-ret"><i class="fa fa-arrow-left"></i> Return</label>
<label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label>
<select type="text" id="node-input-ret" style="width:72%;">
<option value="txt">a UTF-8 string</option>
<option value="bin">a binary buffer</option>
<option value="obj">a parsed JSON object</option>
<option value="txt" data-i18n="httpin.utf8"></option>
<option value="bin" data-i18n="httpin.binary"></option>
<option value="obj" data-i18n="httpin.json"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips" id="tip-json" hidden>Tip: If the JSON parse fails the fetched string is returned as-is.</div>
<div class="form-tips" id="tip-json" hidden><span data-i18n="httpin.req-tip"></span></div>
</script>
<script type="text/x-red" data-help-name="http request">
@ -241,7 +241,7 @@
outputs:1,
icon: "white-globe.png",
label: function() {
return this.name||"http request";
return this.name||this._("httpin.httpreq");
},
labelStyle: function() {
return this.name?"node_label_italic":"";

View File

@ -137,7 +137,7 @@ module.exports = function(RED) {
}
});
} else {
this.warn("Cannot create http-in node when httpNodeRoot set to false");
this.warn(RED._("httpin.errors.not-created"));
}
}
RED.nodes.registerType("http in",HTTPIn);
@ -172,7 +172,7 @@ module.exports = function(RED) {
msg.res.send(statusCode,msg.payload);
}
} else {
node.warn("No response object");
node.warn(RED._("httpin.errors.no-response"));
}
});
}
@ -195,16 +195,16 @@ module.exports = function(RED) {
this.on("input",function(msg) {
var preRequestTimestamp = process.hrtime();
node.status({fill:"blue",shape:"dot",text:"requesting"});
node.status({fill:"blue",shape:"dot",text:RED._("common.status.requesting")});
var url = nodeUrl || msg.url;
if (msg.url && nodeUrl && (nodeUrl !== msg.url)) { // revert change below when warning is finally removed
node.warn("Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props");
node.warn(RED._("httpin.errors.not-overridden"));
}
if (isTemplatedUrl) {
url = mustache.render(nodeUrl,msg);
}
if (!url) {
node.error("No url specified",msg);
node.error(RED._("httpin.errors.no-url"),msg);
return;
}
// url must start http:// or https:// so assume http:// if not set
@ -214,7 +214,7 @@ module.exports = function(RED) {
var method = nodeMethod.toUpperCase() || "GET";
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set
node.warn("Warning: msg properties can no longer override fixed node properties. Use explicit override option. See bit.ly/nr-override-msg-props");
node.warn(RED._("httpin.errors.not-overridden"));
}
if (msg.method && n.method && (n.method === "use")) {
method = msg.method.toUpperCase(); // use the msg parameter
@ -312,7 +312,7 @@ module.exports = function(RED) {
}
else if (node.ret === "obj") {
try { msg.payload = JSON.parse(msg.payload); }
catch(e) { node.warn("JSON parse error"); }
catch(e) { node.warn(RED._("httpin.errors.json-error")); }
}
node.send(msg);
node.status({});

View File

@ -1,279 +1,277 @@
<!--
Copyright 2013 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/javascript">
function ws_oneditprepare() {
$("#websocket-client-row").hide();
$("#node-input-mode").change(function(){
if( $("#node-input-mode").val() === 'client') {
$("#websocket-server-row").hide();
$("#websocket-client-row").show();
}
else {
$("#websocket-server-row").show();
$("#websocket-client-row").hide();
}
});
if(this.client) {
$("#node-input-mode").val('client').change();
}
else {
$("#node-input-mode").val('server').change();
}
}
function ws_oneditsave() {
if($("#node-input-mode").val() === 'client') {
$("#node-input-server").append('<option value="">Dummy</option>');
$("#node-input-server").val('');
}
else {
$("#node-input-client").append('<option value="">Dummy</option>');
$("#node-input-client").val('');
}
}
function ws_label() {
var nodeid = (this.client)?this.client:this.server;
var wsNode = RED.nodes.node(nodeid);
return this.name||(wsNode?"[ws] "+wsNode.label():"websocket");
}
function ws_validateserver() {
if($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
return true;
}
else {
return RED.nodes.node(this.server) != null;
}
}
function ws_validateclient() {
if($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
return RED.nodes.node(this.client) != null;
}
else {
return true;
}
}
</script>
<!-- WebSocket Input Node -->
<script type="text/x-red" data-template-name="websocket in">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> Type</label>
<select id="node-input-mode">
<option value="server">Listen on</option>
<option value="client">Connect to</option>
</select>
</div>
<div class="form-row" id="websocket-server-row">
<label for="node-input-server"><i class="fa fa-bookmark"></i> Path</label>
<input type="text" id="node-input-server">
</div>
<div class="form-row" id="websocket-client-row">
<label for="node-input-client"><i class="fa fa-bookmark"></i> URL</label>
<input type="text" id="node-input-client">
</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>
</script>
<script type="text/x-red" data-help-name="websocket in">
<p>WebSocket input node.</p>
<p>By default, the data received from the WebSocket will be in <b>msg.payload</b>.
The socket can be configured to expect a properly formed JSON string, in which
case it will parse the JSON and send on the resulting object as the entire message.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket in',{
category: 'input',
defaults: {
name: {value:""},
server: {type:"websocket-listener", validate: ws_validateserver},
client: {type:"websocket-client", validate: ws_validateclient}
},
color:"rgb(215, 215, 160)",
inputs:0,
outputs:1,
icon: "white-globe.png",
labelStyle: function() {
return this.name?"node_label_italic":"";
},
label: ws_label,
oneditsave: ws_oneditsave,
oneditprepare: ws_oneditprepare
});
</script>
<!-- WebSocket out Node -->
<script type="text/x-red" data-template-name="websocket out">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> Type</label>
<select id="node-input-mode">
<option value="server">Listen on</option>
<option value="client">Connect to</option>
</select>
</div>
<div class="form-row" id="websocket-server-row">
<label for="node-input-server"><i class="fa fa-bookmark"></i> Path</label>
<input type="text" id="node-input-server">
</div>
<div class="form-row" id="websocket-client-row">
<label for="node-input-client"><i class="fa fa-bookmark"></i> URL</label>
<input type="text" id="node-input-client">
</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>
</script>
<script type="text/x-red" data-help-name="websocket out">
<p>WebSocket out node.</p>
<p>By default, <b>msg.payload</b> will be sent over the WebSocket. The socket
can be configured to encode the entire message object as a JSON string and send that
over the WebSocket.</p>
<p>If the message arriving at this node started at a WebSocket In node, the message
will be sent back to the client that triggered the flow. Otherwise, the message
will be broadcast to all connected clients.</p>
<p>If you want to broadcast a message that started at a WebSocket In node, you
should delete the <b>msg._session</b> property within the flow</p>.
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket out',{
category: 'output',
defaults: {
name: {value:""},
server: {type:"websocket-listener", validate: ws_validateserver},
client: {type:"websocket-client", validate: ws_validateclient}
},
color:"rgb(215, 215, 160)",
inputs:1,
outputs:0,
icon: "white-globe.png",
align: "right",
labelStyle: function() {
return this.name?"node_label_italic":"";
},
label: ws_label,
oneditsave: ws_oneditsave,
oneditprepare: ws_oneditprepare
});
</script>
<!-- WebSocket Server configuration node -->
<script type="text/x-red" data-template-name="websocket-listener">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> Path</label>
<input type="text" id="node-config-input-path" placeholder="/ws/example">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg">&nbsp;</label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false">Send/Receive payload</option>
<option value="true">Send/Receive entire message</option>
</select>
</div>
<div class="form-tips">
By default, <code>payload</code> will contain the data to be sent over, or received from a websocket.
The listener can be configured to send or receive the entire message object as a JSON formatted string.
<p id="node-config-ws-tip">This path will be relative to <code><span id="node-config-ws-path"></span></code>.</p>
</div>
</script>
<script type="text/x-red" data-help-name="websocket-listener">
<p>This configuration node creates a WebSocket Server using the specified path</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket-listener',{
category: 'config',
defaults: {
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/) },
wholemsg: {value:"false"}
},
inputs:0,
outputs:0,
label: function() {
var root = RED.settings.httpNodeRoot;
if (root.slice(-1) != "/") {
root = root+"/";
}
if (this.path.charAt(0) == "/") {
root += this.path.slice(1);
} else {
root += this.path;
}
return root;
},
oneditprepare: function() {
var root = RED.settings.httpNodeRoot;
if (root.slice(-1) == "/") {
root = root.slice(0,-1);
}
if (root == "") {
$("#node-config-ws-tip").hide();
} else {
$("#node-config-ws-path").html(root);
$("#node-config-ws-tip").show();
}
}
});
</script>
<!-- WebSocket Client configuration node -->
<script type="text/x-red" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> URL</label>
<input type="text" id="node-config-input-path" placeholder="ws://example.com/ws">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg">&nbsp;</label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false">Send/Receive payload</option>
<option value="true">Send/Receive entire message</option>
</select>
</div>
<div class="form-tips">
<p>URL should use ws:&#47;&#47; or wss:&#47;&#47; scheme and point to an existing websocket listener.</p>
By default, <code>payload</code> will contain the data to be sent over, or received from a websocket.
The client can be configured to send or receive the entire message object as a JSON formatted string.
</div>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<p>This configuration node connects a WebSocket client to the specified URL.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket-client',{
category: 'config',
defaults: {
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/) },
wholemsg: {value:"false"}
},
inputs:0,
outputs:0,
label: function() {
return this.path;
}
});
</script>
<!--
Copyright 2013 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/javascript">
function ws_oneditprepare() {
$("#websocket-client-row").hide();
$("#node-input-mode").change(function(){
if( $("#node-input-mode").val() === 'client') {
$("#websocket-server-row").hide();
$("#websocket-client-row").show();
}
else {
$("#websocket-server-row").show();
$("#websocket-client-row").hide();
}
});
if(this.client) {
$("#node-input-mode").val('client').change();
}
else {
$("#node-input-mode").val('server').change();
}
}
function ws_oneditsave() {
if($("#node-input-mode").val() === 'client') {
$("#node-input-server").append('<option value="">Dummy</option>');
$("#node-input-server").val('');
}
else {
$("#node-input-client").append('<option value="">Dummy</option>');
$("#node-input-client").val('');
}
}
function ws_label() {
var nodeid = (this.client)?this.client:this.server;
var wsNode = RED.nodes.node(nodeid);
return this.name||(wsNode?"[ws] "+wsNode.label():"websocket");
}
function ws_validateserver() {
if($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
return true;
}
else {
return RED.nodes.node(this.server) != null;
}
}
function ws_validateclient() {
if($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
return RED.nodes.node(this.client) != null;
}
else {
return true;
}
}
</script>
<!-- WebSocket Input Node -->
<script type="text/x-red" data-template-name="websocket in">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
<select id="node-input-mode">
<option value="server" data-i18n="websocket.listenon"></option>
<option value="client" data-i18n="websocket.connectto"></option>
</select>
</div>
<div class="form-row" id="websocket-server-row">
<label for="node-input-server"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
<input type="text" id="node-input-server">
</div>
<div class="form-row" id="websocket-client-row">
<label for="node-input-client"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input type="text" id="node-input-client">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
<script type="text/x-red" data-help-name="websocket in">
<p>WebSocket input node.</p>
<p>By default, the data received from the WebSocket will be in <b>msg.payload</b>.
The socket can be configured to expect a properly formed JSON string, in which
case it will parse the JSON and send on the resulting object as the entire message.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket in',{
category: 'input',
defaults: {
name: {value:""},
server: {type:"websocket-listener", validate: ws_validateserver},
client: {type:"websocket-client", validate: ws_validateclient}
},
color:"rgb(215, 215, 160)",
inputs:0,
outputs:1,
icon: "white-globe.png",
labelStyle: function() {
return this.name?"node_label_italic":"";
},
label: ws_label,
oneditsave: ws_oneditsave,
oneditprepare: ws_oneditprepare
});
</script>
<!-- WebSocket out Node -->
<script type="text/x-red" data-template-name="websocket out">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
<select id="node-input-mode">
<option value="server" data-i18n="websocket.listenon"></option>
<option value="client" data-i18n="websocket.connectto"></option>
</select>
</div>
<div class="form-row" id="websocket-server-row">
<label for="node-input-server"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
<input type="text" id="node-input-server">
</div>
<div class="form-row" id="websocket-client-row">
<label for="node-input-client"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input type="text" id="node-input-client">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
<script type="text/x-red" data-help-name="websocket out">
<p>WebSocket out node.</p>
<p>By default, <b>msg.payload</b> will be sent over the WebSocket. The socket
can be configured to encode the entire message object as a JSON string and send that
over the WebSocket.</p>
<p>If the message arriving at this node started at a WebSocket In node, the message
will be sent back to the client that triggered the flow. Otherwise, the message
will be broadcast to all connected clients.</p>
<p>If you want to broadcast a message that started at a WebSocket In node, you
should delete the <b>msg._session</b> property within the flow</p>.
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket out',{
category: 'output',
defaults: {
name: {value:""},
server: {type:"websocket-listener", validate: ws_validateserver},
client: {type:"websocket-client", validate: ws_validateclient}
},
color:"rgb(215, 215, 160)",
inputs:1,
outputs:0,
icon: "white-globe.png",
align: "right",
labelStyle: function() {
return this.name?"node_label_italic":"";
},
label: ws_label,
oneditsave: ws_oneditsave,
oneditprepare: ws_oneditprepare
});
</script>
<!-- WebSocket Server configuration node -->
<script type="text/x-red" data-template-name="websocket-listener">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
<input type="text" id="node-config-input-path" data-i18n="[placeholder]websocket.label.pathph">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg">&nbsp;</label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false" data-i18n="websocket.payload"></option>
<option value="true" data-i18n="websocket.message"></option>
</select>
</div>
<div class="form-tips">
<span data-i18n="[html]websocket.path-tip1"></span>
<p id="node-config-ws-tip"><span data-i18n="[html]websocket.path-tip2"></span><code><span id="node-config-ws-path"></span></code>.</p>
</div>
</script>
<script type="text/x-red" data-help-name="websocket-listener">
<p>This configuration node creates a WebSocket Server using the specified path</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket-listener',{
category: 'config',
defaults: {
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/) },
wholemsg: {value:"false"}
},
inputs:0,
outputs:0,
label: function() {
var root = RED.settings.httpNodeRoot;
if (root.slice(-1) != "/") {
root = root+"/";
}
if (this.path.charAt(0) == "/") {
root += this.path.slice(1);
} else {
root += this.path;
}
return root;
},
oneditprepare: function() {
var root = RED.settings.httpNodeRoot;
if (root.slice(-1) == "/") {
root = root.slice(0,-1);
}
if (root == "") {
$("#node-config-ws-tip").hide();
} else {
$("#node-config-ws-path").html(root);
$("#node-config-ws-tip").show();
}
}
});
</script>
<!-- WebSocket Client configuration node -->
<script type="text/x-red" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input type="text" id="node-config-input-path" data-i18n="[placeholder]websocket.label.urlph">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg">&nbsp;</label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false" data-i18n="websocket.payload"></option>
<option value="true" data-i18n="websocket.message"></option>
</select>
</div>
<div class="form-tips">
<p><span data-i18n="[html]websocket.url-tip1"></span></p>
<span data-i18n="[html]websocket.url-tip2"></span>
</div>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<p>This configuration node connects a WebSocket client to the specified URL.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('websocket-client',{
category: 'config',
defaults: {
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/) },
wholemsg: {value:"false"}
},
inputs:0,
outputs:0,
label: function() {
return this.path;
}
});
</script>

View File

@ -1,232 +1,234 @@
/**
* Copyright 2013,2015 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 ws = require("ws");
var inspect = require("sys").inspect;
// A node red node that sets up a local websocket server
function WebSocketListenerNode(n) {
// Create a RED node
RED.nodes.createNode(this,n);
var node = this;
// Store local copies of the node configuration (as defined in the .html)
node.path = n.path;
node.wholemsg = (n.wholemsg === "true");
node._inputNodes = []; // collection of nodes that want to receive events
node._clients = {};
// match absolute url
node.isServer = !/^ws{1,2}:\/\//i.test(node.path);
node.closing = false;
function startconn() { // Connect to remote endpoint
var socket = new ws(node.path);
node.server = socket; // keep for closing
handleConnection(socket);
}
function handleConnection(/*socket*/socket) {
var id = (1+Math.random()*4294967295).toString(16);
if (node.isServer) { node._clients[id] = socket; node.emit('opened',Object.keys(node._clients).length); }
socket.on('open',function() {
if (!node.isServer) { node.emit('opened',''); }
});
socket.on('close',function() {
if (node.isServer) { delete node._clients[id]; node.emit('opened',Object.keys(node._clients).length); }
else { node.emit('closed'); }
if (!node.closing && !node.isServer) {
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
}
});
socket.on('message',function(data,flags){
node.handleEvent(id,socket,'message',data,flags);
});
socket.on('error', function(err) {
node.emit('erro');
if (!node.closing && !node.isServer) {
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
}
});
}
if (node.isServer) {
var path = RED.settings.httpNodeRoot || "/";
path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
// Workaround https://github.com/einaros/ws/pull/253
// Listen for 'newListener' events from RED.server
node._serverListeners = {};
var storeListener = function(/*String*/event,/*function*/listener){
if(event == "error" || event == "upgrade" || event == "listening"){
node._serverListeners[event] = listener;
}
}
RED.server.addListener('newListener',storeListener);
// Create a WebSocket Server
node.server = new ws.Server({server:RED.server,path:path});
// Workaround https://github.com/einaros/ws/pull/253
// Stop listening for new listener events
RED.server.removeListener('newListener',storeListener);
node.server.on('connection', handleConnection);
}
else {
node.closing = false;
startconn(); // start outbound connection
}
node.on("close", function() {
// Workaround https://github.com/einaros/ws/pull/253
// Remove listeners from RED.server
if (node.isServer) {
var listener = null;
for (var event in node._serverListeners) {
if (node._serverListeners.hasOwnProperty(event)) {
listener = node._serverListeners[event];
if (typeof listener === "function") {
RED.server.removeListener(event,listener);
}
}
}
node._serverListeners = {};
node.server.close();
node._inputNodes = [];
}
else {
node.closing = true;
node.server.close();
if (node.tout) { clearTimeout(tout); }
}
});
}
RED.nodes.registerType("websocket-listener",WebSocketListenerNode);
RED.nodes.registerType("websocket-client",WebSocketListenerNode);
WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) {
this._inputNodes.push(handler);
}
WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){
var msg;
if (this.wholemsg) {
try {
msg = JSON.parse(data);
}
catch(err) {
msg = { payload:data };
}
} else {
msg = {
payload:data
};
}
msg._session = {type:"websocket",id:id};
for (var i = 0; i < this._inputNodes.length; i++) {
this._inputNodes[i].send(msg);
}
}
WebSocketListenerNode.prototype.broadcast = function(data) {
try {
if(this.isServer) {
for (var i = 0; i < this.server.clients.length; i++) {
this.server.clients[i].send(data);
}
}
else {
this.server.send(data);
}
}
catch(e) { // swallow any errors
this.warn("ws:"+i+" : "+e);
}
}
WebSocketListenerNode.prototype.reply = function(id,data) {
var session = this._clients[id];
if (session) {
try {
session.send(data);
}
catch(e) { // swallow any errors
}
}
}
function WebSocketInNode(n) {
RED.nodes.createNode(this,n);
this.server = (n.client)?n.client:n.server;
var node = this;
this.serverConfig = RED.nodes.getNode(this.server);
if (this.serverConfig) {
this.serverConfig.registerInputNode(this);
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
} else {
this.error("Missing server configuration");
}
}
RED.nodes.registerType("websocket in",WebSocketInNode);
function WebSocketOutNode(n) {
RED.nodes.createNode(this,n);
var node = this;
this.server = (n.client)?n.client:n.server;
this.serverConfig = RED.nodes.getNode(this.server);
if (!this.serverConfig) {
this.error("Missing server configuration");
}
else {
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
}
this.on("input", function(msg) {
var payload;
if (this.serverConfig.wholemsg) {
delete msg._session;
payload = JSON.stringify(msg);
} else if (msg.hasOwnProperty("payload")) {
if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string.
payload = RED.util.ensureString(msg.payload);
}
else {
payload = msg.payload;
}
}
if (payload) {
if (msg._session && msg._session.type == "websocket") {
node.serverConfig.reply(msg._session.id,payload);
} else {
node.serverConfig.broadcast(payload,function(error){
if (!!error) {
node.warn("An error occurred while sending:" + inspect(error));
}
});
}
}
});
}
RED.nodes.registerType("websocket out",WebSocketOutNode);
}
/**
* Copyright 2013,2015 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 ws = require("ws");
var inspect = require("sys").inspect;
// A node red node that sets up a local websocket server
function WebSocketListenerNode(n) {
// Create a RED node
RED.nodes.createNode(this,n);
var node = this;
// Store local copies of the node configuration (as defined in the .html)
node.path = n.path;
node.wholemsg = (n.wholemsg === "true");
node._inputNodes = []; // collection of nodes that want to receive events
node._clients = {};
// match absolute url
node.isServer = !/^ws{1,2}:\/\//i.test(node.path);
node.closing = false;
function startconn() { // Connect to remote endpoint
var socket = new ws(node.path);
node.server = socket; // keep for closing
handleConnection(socket);
}
function handleConnection(/*socket*/socket) {
var id = (1+Math.random()*4294967295).toString(16);
if (node.isServer) { node._clients[id] = socket; node.emit('opened',Object.keys(node._clients).length); }
socket.on('open',function() {
if (!node.isServer) { node.emit('opened',''); }
});
socket.on('close',function() {
if (node.isServer) { delete node._clients[id]; node.emit('opened',Object.keys(node._clients).length); }
else { node.emit('closed'); }
if (!node.closing && !node.isServer) {
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
}
});
socket.on('message',function(data,flags){
node.handleEvent(id,socket,'message',data,flags);
});
socket.on('error', function(err) {
node.emit('erro');
if (!node.closing && !node.isServer) {
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
}
});
}
if (node.isServer) {
var path = RED.settings.httpNodeRoot || "/";
path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
// Workaround https://github.com/einaros/ws/pull/253
// Listen for 'newListener' events from RED.server
node._serverListeners = {};
var storeListener = function(/*String*/event,/*function*/listener){
if(event == "error" || event == "upgrade" || event == "listening"){
node._serverListeners[event] = listener;
}
}
RED.server.addListener('newListener',storeListener);
// Create a WebSocket Server
node.server = new ws.Server({server:RED.server,path:path});
// Workaround https://github.com/einaros/ws/pull/253
// Stop listening for new listener events
RED.server.removeListener('newListener',storeListener);
node.server.on('connection', handleConnection);
}
else {
node.closing = false;
startconn(); // start outbound connection
}
node.on("close", function() {
// Workaround https://github.com/einaros/ws/pull/253
// Remove listeners from RED.server
if (node.isServer) {
var listener = null;
for (var event in node._serverListeners) {
if (node._serverListeners.hasOwnProperty(event)) {
listener = node._serverListeners[event];
if (typeof listener === "function") {
RED.server.removeListener(event,listener);
}
}
}
node._serverListeners = {};
node.server.close();
node._inputNodes = [];
}
else {
node.closing = true;
node.server.close();
if (node.tout) { clearTimeout(tout); }
}
});
}
RED.nodes.registerType("websocket-listener",WebSocketListenerNode);
RED.nodes.registerType("websocket-client",WebSocketListenerNode);
WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) {
this._inputNodes.push(handler);
}
WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){
var msg;
if (this.wholemsg) {
try {
msg = JSON.parse(data);
}
catch(err) {
msg = { payload:data };
}
} else {
msg = {
payload:data
};
}
msg._session = {type:"websocket",id:id};
for (var i = 0; i < this._inputNodes.length; i++) {
this._inputNodes[i].send(msg);
}
}
WebSocketListenerNode.prototype.broadcast = function(data) {
try {
if(this.isServer) {
for (var i = 0; i < this.server.clients.length; i++) {
this.server.clients[i].send(data);
}
}
else {
this.server.send(data);
}
}
catch(e) { // swallow any errors
this.warn("ws:"+i+" : "+e);
}
}
WebSocketListenerNode.prototype.reply = function(id,data) {
var session = this._clients[id];
if (session) {
try {
session.send(data);
}
catch(e) { // swallow any errors
}
}
}
function WebSocketInNode(n) {
RED.nodes.createNode(this,n);
this.server = (n.client)?n.client:n.server;
var node = this;
this.serverConfig = RED.nodes.getNode(this.server);
if (this.serverConfig) {
this.serverConfig.registerInputNode(this);
// TODO: nls
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
} else {
this.error(RED._("websocket.errors.missing-conf"));
}
}
RED.nodes.registerType("websocket in",WebSocketInNode);
function WebSocketOutNode(n) {
RED.nodes.createNode(this,n);
var node = this;
this.server = (n.client)?n.client:n.server;
this.serverConfig = RED.nodes.getNode(this.server);
if (!this.serverConfig) {
this.error(RED._("websocket.errors.missing-conf"));
}
else {
// TODO: nls
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
}
this.on("input", function(msg) {
var payload;
if (this.serverConfig.wholemsg) {
delete msg._session;
payload = JSON.stringify(msg);
} else if (msg.hasOwnProperty("payload")) {
if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string.
payload = RED.util.ensureString(msg.payload);
}
else {
payload = msg.payload;
}
}
if (payload) {
if (msg._session && msg._session.type == "websocket") {
node.serverConfig.reply(msg._session.id,payload);
} else {
node.serverConfig.broadcast(payload,function(error){
if (!!error) {
node.warn(RED._("websocket.errors.send-error")+inspect(error));
}
});
}
}
});
}
RED.nodes.registerType("websocket out",WebSocketOutNode);
}

View File

@ -16,14 +16,14 @@
<script type="text/x-red" data-template-name="watch">
<div class="form-row node-input-filename">
<label for="node-input-files"><i class="fa fa-file"></i> File(s)</label>
<input type="text" id="node-input-files" placeholder="File(s) or Directory">
<label for="node-input-files"><i class="fa fa-file"></i> <span data-i18n="watch.label.files"></span></label>
<input type="text" id="node-input-files" data-i18n="[placeholder]watch.label.filesph">
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div id="node-input-tip" class="form-tips">On Windows you must use double back-slashes \\ in any directory names.</div>
<div id="node-input-tip" class="form-tips"><span data-i18n="watch.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="watch">

View File

@ -16,43 +16,43 @@
<script type="text/x-red" data-template-name="tcp in">
<div class="form-row">
<label for="node-input-server"><i class="fa fa-dot-circle-o"></i> Type</label>
<label for="node-input-server"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
<select id="node-input-server" style="width:120px; margin-right:5px;">
<option value="server">Listen on</option>
<option value="client">Connect to</option>
<option value="server" data-i18n="tcpin.listen"></option>
<option value="client" data-i18n="tcpin.connect"></option>
</select>
port <input type="text" id="node-input-port" style="width: 50px">
<span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width: 50px">
</div>
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
at host <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
</div>
<div class="form-row">
<label><i class="fa fa-sign-out"></i> Output</label>
a
<label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label>
<span data-i18n="tcpin.label.a"></span>
<select id="node-input-datamode" style="width:110px;">
<option value="stream">stream of</option>
<option value="single">single</option>
<option value="stream" data-i18n="tcpin.stream"></option>
<option value="single" data-i18n="tcpin.single"></option>
</select>
<select id="node-input-datatype" style="width:140px;">
<option value="buffer">Buffer</option>
<option value="utf8">String</option>
<option value="base64">Base64 String</option>
<option value="buffer" data-i18n="tcpin.buffer"></option>
<option value="utf8" data-i18n="tcpin.string"></option>
<option value="base64" data-i18n="tcpin.base64"></option>
</select>
payload<span id="node-input-datamode-plural">s</span>
<span data-i18n="tcpin.label.payload"></span>
</div>
<div id="node-row-newline" class="form-row hidden" style="padding-left: 110px;">
delimited by <input type="text" id="node-input-newline" style="width: 110px;">
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width: 110px;">
</div>
<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">
<label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topicph">
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -96,14 +96,12 @@
var datamode = $("#node-input-datamode option:selected").val();
var datatype = $("#node-input-datatype option:selected").val();
if (datamode == "stream") {
$("#node-input-datamode-plural").show();
if (datatype == "utf8") {
$("#node-row-newline").show();
} else {
$("#node-row-newline").hide();
}
} else {
$("#node-input-datamode-plural").hide();
$("#node-row-newline").hide();
}
};
@ -118,41 +116,41 @@
<script type="text/x-red" data-template-name="tcp out">
<div class="form-row">
<label for="node-input-beserver"><i class="fa fa-dot-circle-o"></i> Type</label>
<label for="node-input-beserver"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
<select id="node-input-beserver" style="width:150px; margin-right:5px;">
<option value="server">Listen on</option>
<option value="client">Connect to</option>
<option value="reply">Reply to TCP</option>
<option value="server" data-i18n="tcpin.listen"></option>
<option value="client" data-i18n="tcpin.connect"></option>
<option value="reply" data-i18n="tcpin.reply"></option>
</select>
<span id="node-input-port-row">port <input type="text" id="node-input-port" style="width: 50px"></span>
<span id="node-input-port-row"><span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width: 50px"></span>
</div>
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
at host <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
<span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;">
</div>
<div class="form-row hidden" id="node-input-end-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-end" style="width: 70%;">Close connection after each message is sent ?</label>
<label for="node-input-end" style="width: 70%;"><span data-i18n="tcpin.label.close-connection"></span></label>
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 message ?</label>
<label for="node-input-base64" style="width: 70%;"><span data-i18n="tcpin.label.decode-base64"></span></label>
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips hidden" id="fin-tip">
<b>Note:</b> Closing the connection after each message is generally not a good thing - but is useful to indicate an end-of-file for example.
<span data-i18n="[html]tcpin.out-tip1"></span>
</div>
<div class="form-tips hidden" id="fin-tip2">
<b>Note:</b> Closing the connection after each message is generally not a good thing - but is useful to indicate an end-of-file for example. The receiving client will need to reconnect.
<span data-i18n="[html]tcpin.out-tip2"></span>
</div>
</script>
@ -223,27 +221,26 @@
<script type="text/x-red" data-template-name="tcp request">
<div class="form-row">
<label for="node-input-server"><i class="fa fa-globe"></i> Server</label>
<label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="tcpin.label.server"></span></label>
<input type="text" id="node-input-server" placeholder="ip.address" style="width:45%">
&nbsp;port <input type="text" id="node-input-port" placeholder="number" style="width:60px">
&nbsp;port <input type="text" id="node-input-port" data-i18n="[placeholder]tcpin.label.numberph" style="width:60px">
</div>
<div class="form-row">
<label for="node-input-out"><i class="fa fa-sign-out"></i> Return</label>
<label for="node-input-out"><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.return"></span></label>
<select type="text" id="node-input-out" style="width:56%;">
<option value="time">after a fixed timeout of</option>
<option value="char">when character received is</option>
<option value="count">a fixed number of characters</option>
<option value="sit">never. Keep connection open</option>
<option value="time" data-i18n="tcpin.timeout"></option>
<option value="char" data-i18n="tcpin.character"></option>
<option value="count" data-i18n="tcpin.number"></option>
<option value="sit" data-i18n="tcpin.never"></option>
</select>
<input type="text" id="node-input-splitc" style="width:50px;">
<span id="node-units"></span>
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips"><b>Tip:</b> Outputs a binary <b>Buffer</b>, so you may want to .toString() it.</br/>
<b>Tip:</b> Leave host and port blank if you want to overide with msg.host and msg.port properties.</div>
<div class="form-tips"><span data-i18n="[html]tcpin.req-tip"></span></div>
<script>
var previous = null;
$("#node-input-out").on('focus', function () { previous = this.value; }).change(function() {

View File

@ -43,14 +43,14 @@ module.exports = function(RED) {
var reconnectTimeout;
var end = false;
var setupTcpClient = function() {
node.log("connecting to "+node.host+":"+node.port);
node.status({fill:"grey",shape:"dot",text:"connecting"});
node.log(RED._("tcpin.errors.connecting-to")+" "+node.host+":"+node.port);
node.status({fill:"grey",shape:"dot",text:RED._("common.status.connecting")});
var id = (1+Math.random()*4294967295).toString(16);
client = net.connect(node.port, node.host, function() {
buffer = (node.datatype == 'buffer')? new Buffer(0):"";
node.connected = true;
node.log("connected to "+node.host+":"+node.port);
node.status({fill:"green",shape:"dot",text:"connected"});
node.log(RED._("tcpin.errors.connected-to")+" "+node.host+":"+node.port);
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
});
connectionPool[id] = client;
@ -96,14 +96,14 @@ module.exports = function(RED) {
client.on('close', function() {
delete connectionPool[id];
node.connected = false;
node.status({fill:"red",shape:"ring",text:"disconnected"});
node.status({fill:"red",shape:"ring",text:RED._("common.status.disconnected")});
if (!node.closing) {
if (end) { // if we were asked to close then try to reconnect once very quick.
end = false;
reconnectTimeout = setTimeout(setupTcpClient, 20);
}
else {
node.log("connection lost to "+node.host+":"+node.port);
node.log(RED._("tcpin.errors.connection-lost")+" "+node.host+":"+node.port);
reconnectTimeout = setTimeout(setupTcpClient, reconnectTime);
}
} else {
@ -128,7 +128,7 @@ module.exports = function(RED) {
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
var id = (1+Math.random()*4294967295).toString(16);
connectionPool[id] = socket;
node.status({text:++count+" connections"});
node.status({text:++count+" "+RED._("common.status.connections")});
var buffer = (node.datatype == 'buffer')? new Buffer(0):"";
socket.on('data', function (data) {
@ -170,12 +170,12 @@ module.exports = function(RED) {
}
});
socket.on('timeout', function() {
node.log('timeout closed socket port '+node.port);
node.log(RED._("tcpin.errors.timeout")+' '+node.port);
socket.end();
});
socket.on('close', function() {
delete connectionPool[id];
node.status({text:--count+" connections"});
node.status({text:--count+" "+RED._("common.status.connections")});
});
socket.on('error',function(err) {
node.log(err);
@ -183,15 +183,15 @@ module.exports = function(RED) {
});
server.on('error', function(err) {
if (err) {
node.error('unable to listen on port '+node.port+' : '+err);
node.error(RED._("tcpin.errors.cannot-listen")+' '+node.port+' : '+err);
}
});
server.listen(node.port, function(err) {
if (err) {
node.error('unable to listen on port '+node.port+' : '+err);
node.error(RED._("tcpin.errors.cannot-listen")+' '+node.port+' : '+err);
} else {
node.log('listening on port '+node.port);
node.log(RED._("tcpin.errors.listening-port")+' '+node.port);
node.on('close', function() {
for (var c in connectionPool) {
if (connectionPool.hasOwnProperty(c)) {
@ -201,7 +201,7 @@ module.exports = function(RED) {
}
node.closing = true;
server.close();
node.log('stopped listening on port '+node.port);
node.log(RED._("tcpin.errors.stopped-listening")+' '+node.port);
});
}
});
@ -228,20 +228,20 @@ module.exports = function(RED) {
var end = false;
var setupTcpClient = function() {
node.log("connecting to "+node.host+":"+node.port);
node.status({fill:"grey",shape:"dot",text:"connecting"});
node.log(RED._("tcpin.errors.connecting-to")+" "+node.host+":"+node.port);
node.status({fill:"grey",shape:"dot",text:RED._("common.status.connecting")});
client = net.connect(node.port, node.host, function() {
node.connected = true;
node.log("connected to "+node.host+":"+node.port);
node.status({fill:"green",shape:"dot",text:"connected"});
node.log(RED._("tcpin.errors.connected-to")+" "+node.host+":"+node.port);
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
});
client.on('error', function (err) {
node.log(err);
node.log(RED._("tcpin.errors.error")+' : '+err);
});
client.on('end', function (err) {
});
client.on('close', function() {
node.status({fill:"red",shape:"ring",text:"disconnected"});
node.status({fill:"red",shape:"ring",text:RED._("common.status.disconnected")});
node.connected = false;
client.destroy();
if (!node.closing) {
@ -250,7 +250,7 @@ module.exports = function(RED) {
reconnectTimeout = setTimeout(setupTcpClient,20);
}
else {
node.log("connection lost to "+node.host+":"+node.port);
node.log(RED._("tcpin.errors.connection-lost")+" "+node.host+":"+node.port);
reconnectTimeout = setTimeout(setupTcpClient,reconnectTime);
}
} else {
@ -301,26 +301,26 @@ module.exports = function(RED) {
});
} else {
var connectedSockets = [];
node.status({text:"0 connections"});
node.status({text:"0 "+RED._("common.status.connections")});
var server = net.createServer(function (socket) {
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
var remoteDetails = socket.remoteAddress+":"+socket.remotePort;
node.log("connection from "+remoteDetails);
node.log(RED._("tcpin.errors.connection-from")+" "+remoteDetails);
connectedSockets.push(socket);
node.status({text:connectedSockets.length+" connections"});
node.status({text:connectedSockets.length+" "+RED._("common.status.connections")});
socket.on('timeout', function() {
node.log('timeout closed socket port '+node.port);
node.log(RED._("tcpin.errors.timeout")+' '+node.port);
socket.end();
});
socket.on('close',function() {
node.log("connection closed from "+remoteDetails);
node.log(RED._("tcpin.errors.connection-closed")+" "+remoteDetails);
connectedSockets.splice(connectedSockets.indexOf(socket),1);
node.status({text:connectedSockets.length+" connections"});
node.status({text:connectedSockets.length+" "+RED._("common.status.connections")});
});
socket.on('error',function() {
node.log("socket error from "+remoteDetails);
node.log(RED._("tcpin.errors.socket-error")+" "+remoteDetails);
connectedSockets.splice(connectedSockets.indexOf(socket),1);
node.status({text:connectedSockets.length+" connections"});
node.status({text:connectedSockets.length+" "+RED._("common.status.connections")});
});
});
@ -343,15 +343,15 @@ module.exports = function(RED) {
server.on('error', function(err) {
if (err) {
node.error('unable to listen on port '+node.port+' : '+err);
node.error(RED._("tcpin.errors.cannot-listen")+' '+node.port+' : '+err);
}
});
server.listen(node.port, function(err) {
if (err) {
node.error('unable to listen on port '+node.port+' : '+err);
node.error(RED._("tcpin.errors.cannot-listen")+' '+node.port+' : '+err);
} else {
node.log('listening on port '+node.port);
node.log(RED._("tcpin.errors.listening-port")+' '+node.port);
node.on('close', function() {
for (var c in connectedSockets) {
if (connectedSockets.hasOwnProperty(c)) {
@ -360,7 +360,7 @@ module.exports = function(RED) {
}
}
server.close();
node.log('stopped listening on port '+node.port);
node.log(RED._("tcpin.errors.stopped-listening")+' '+node.port);
});
}
});
@ -401,18 +401,17 @@ module.exports = function(RED) {
if (host && port) {
client.connect(port, host, function() {
//node.log('client connected');
node.status({fill:"green",shape:"dot",text:"connected"});
//node.log(RED._("tcpin.errors.client-connected"));
node.status({fill:"green",shape:"dot",text:RED._("common.status.connected")});
node.connected = true;
client.write(msg.payload);
});
}
else {
node.warn("Host and/or port not set");
node.warn(RED._("tcpin.errors.no-host"));
}
client.on('data', function(data) {
//node.log("data:"+ data.length+":"+ data);
if (node.out == "sit") { // if we are staying connected just send the buffer
node.send({"payload": data});
}
@ -477,18 +476,17 @@ module.exports = function(RED) {
});
client.on('error', function() {
node.error('connect failed',msg);
node.status({fill:"red",shape:"ring",text:"error"});
node.error(RED._("tcpin.errors.connect-fail"),msg);
node.status({fill:"red",shape:"ring",text:RED._("common.status.error")});
if (client) { client.end(); }
});
client.on('timeout',function() {
node.warn('connect timeout');
node.warn(RED._("tcpin.errors.timeout"));
if (client) {
client.end();
setTimeout(function() {
client.connect(port, host, function() {
//node.log('client connected');
node.connected = true;
client.write(msg.payload);
});

View File

@ -17,54 +17,41 @@
<!-- The Input Node -->
<script type="text/x-red" data-template-name="udp in">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> Listen for</label>
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.listen"></span></label>
<select id="node-input-multicast" style='width:62%'>
<option value="false">udp messages</option>
<option value="true">multicast messages</option>
<option value="false" data-i18n="udp.udpmsgs"></option>
<option value="true" data-i18n="udp.mcmsgs"></option>
</select>
</div>
<div class="form-row node-input-group">
<label for="node-input-group"><i class="fa fa-list"></i> Group</label>
<label for="node-input-group"><i class="fa fa-list"></i> <span data-i18n="udp.label.group"></span></label>
<input type="text" id="node-input-group" placeholder="225.0.18.83">
</div>
<div class="form-row node-input-iface">
<label for="node-input-iface"><i class="fa fa-random"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="(optional) ip address of eth0">
<label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interfaceph">
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> on Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 80px">
&nbsp;&nbsp;using <select id="node-input-ipv" style="width:80px">
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.onport"></span></label>
<input type="text" id="node-input-port" data-i18n="[placeholder]udp.label.onportph" style="width: 80px">
&nbsp;&nbsp;<span data-i18n="udp.label.using"></span> <select id="node-input-ipv" style="width:80px">
<option value="udp4">ipv4</option>
<option value="udp6">ipv6</option>
</select>
</div>
<div class="form-row">
<label for="node-input-datatype"><i class="fa fa-sign-out"></i> Output</label>
<label for="node-input-datatype"><i class="fa fa-sign-out"></i> <span data-i18n="udp.label.output"></span></label>
<select id="node-input-datatype" style="width: 70%;">
<option value="buffer">a Buffer</option>
<option value="utf8">a String</option>
<option value="base64">a Base64 encoded string</option>
<option value="buffer" data-i18n="udp.buffer"></option>
<option value="utf8" data-i18n="udp.string"></option>
<option value="base64" data-i18n="udp.base64"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips">Tip: Make sure your firewall will allow the data in.</div>
<script>
$("#node-input-multicast").change(function() {
var id = $("#node-input-multicast option:selected").val();
if (id == "false") {
$(".node-input-group").hide();
$(".node-input-iface").hide();
}
else {
$(".node-input-group").show();
$(".node-input-iface").show();
}
});
</script>
<div class="form-tips"><span data-i18n="udp.in-tip"></span></div>
</script>
<script type="text/x-red" data-help-name="udp in">
@ -97,6 +84,20 @@
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
$("#node-input-multicast").change(function() {
var id = $("#node-input-multicast option:selected").val();
if (id == "false") {
$(".node-input-group").hide();
$(".node-input-iface").hide();
}
else {
$(".node-input-group").show();
$(".node-input-iface").show();
}
});
$("#node-input-multicast").change();
}
});
</script>
@ -105,62 +106,44 @@
<!-- The Output Node -->
<script type="text/x-red" data-template-name="udp out">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-envelope"></i> Send a</label>
<label for="node-input-port"><i class="fa fa-envelope"></i> <span data-i18n="udp.label.send"></span></label>
<select id="node-input-multicast" style="width:40%">
<option value="false">udp message</option>
<option value="broad">broadcast message</option>
<option value="multi">multicast message</option>
<option value="false" data-i18n="udp.udpmsg"></option>
<option value="broad" data-i18n="udp.bcmsg"></option>
<option value="multi" data-i18n="udp.mcmsg"></option>
</select>
to port <input type="text" id="node-input-port" placeholder="port" style="width: 70px">
<span data-i18n="udp.label.toport"></span> <input type="text" id="node-input-port" data-i18n="[placeholder]udp.label.toportph" style="width: 70px">
</div>
<div class="form-row node-input-addr">
<label for="node-input-addr" id="node-input-addr-label"><i class="fa fa-list"></i> Address</label>
<input type="text" id="node-input-addr" placeholder="destination ip" style="width: 50%;">
<label for="node-input-addr" id="node-input-addr-label"><i class="fa fa-list"></i> <span data-i18n="udp.label.address"></span></label>
<input type="text" id="node-input-addr" data-i18n="[placeholder]udp.label.addressph" style="width: 50%;">
<select id="node-input-ipv" style="width:70px">
<option value="udp4">ipv4</option>
<option value="udp6">ipv6</option>
</select>
</div>
<div class="form-row node-input-iface">
<label for="node-input-iface"><i class="fa fa-random"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="(optional) ip address of eth0">
<label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interfaceph">
</div>
<div class="form-row">
<label for="node-input-outport-type">&nbsp;</label>
<select id="node-input-outport-type">
<option id="node-input-outport-type-random" value="random">use random local port</option>
<option value="fixed">bind to local port</option>
<option id="node-input-outport-type-random" value="random" data-i18n="udp.bindrandom"></option>
<option value="fixed" data-i18n="udp.bindlocal"></option>
</select>
<input type="text" id="node-input-outport" style="width: 70px;" placeholder="port">
<input type="text" id="node-input-outport" style="width: 70px;" data-i18n="[placeholder]udp.label.outportph">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 encoded payload ?</label>
<label for="node-input-base64" style="width: 70%;"><span data-i18n="udp.label.decode-base64"></span></label>
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips">Tip: leave address and port blank if you want to set using <b>msg.ip</b> and <b>msg.port</b>.</div>
<script>
$("#node-input-multicast").change(function() {
var id = $("#node-input-multicast option:selected").val();
if (id !== "multi") {
$(".node-input-iface").hide();
$("#node-input-addr-label").html('<i class="fa fa-list"></i> Address');
$("#node-input-addr")[0].placeholder = 'destination ip';
}
else {
$(".node-input-iface").show();
$("#node-input-addr-label").html('<i class="fa fa-list"></i> Group');
$("#node-input-addr")[0].placeholder = '225.0.18.83';
}
if (id === "broad") {
$("#node-input-addr")[0].placeholder = '255.255.255.255';
}
});
</script>
<div class="form-tips"><span data-i18n="[html]udp.out-tip"></span></div>
</script>
<script type="text/x-red" data-help-name="udp out">
@ -195,6 +178,12 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var addresslabel = this._("udp.label.address");
var addressph = this._("udp.label.addressph");
var grouplabel = this._("udp.label.group");
var bindrandom = this._("udp.bindrandom");
var bindtarget = this._("udp.bindtarget");
var type = this.outport==""?"random":"fixed";
$("#node-input-outport-type option").filter(function() {
return $(this).val() == type;
@ -208,15 +197,30 @@
$("#node-input-outport").show();
}
});
$("#node-input-outport-type").change();
$("#node-input-multicast").change(function() {
var id = $("#node-input-multicast option:selected").val();
if (id === "multi") {
$(".node-input-iface").show();
$("#node-input-addr-label").html('<i class="fa fa-list"></i> ' + grouplabel);
$("#node-input-addr")[0].placeholder = '225.0.18.83';
}
else if (id === "broad") {
$(".node-input-iface").hide();
$("#node-input-addr-label").html('<i class="fa fa-list"></i> ' + addresslabel);
$("#node-input-addr")[0].placeholder = '255.255.255.255';
}
else {
$(".node-input-iface").hide();
$("#node-input-addr-label").html('<i class="fa fa-list"></i> ' + addresslabel);
$("#node-input-addr")[0].placeholder = addressph;
}
var type = $(this).children("option:selected").val();
if (type == "false") {
$("#node-input-outport-type-random").html("bind to random local port");
$("#node-input-outport-type-random").html(bindrandom);
} else {
$("#node-input-outport-type-random").html("bind to target port");
$("#node-input-outport-type-random").html(bindtarget);
}
});
$("#node-input-multicast").change();

View File

@ -33,9 +33,9 @@ module.exports = function(RED) {
server.on("error", function (err) {
if ((err.code == "EACCES") && (node.port < 1024)) {
node.error("UDP access error, you may need root access for ports below 1024");
node.error(RED._("udp.errors.access-error"));
} else {
node.error("UDP error : "+err.code);
node.error(RED._("udp.errors.udp-error")+" : "+err.code);
}
server.close();
});
@ -54,20 +54,20 @@ module.exports = function(RED) {
server.on('listening', function () {
var address = server.address();
node.log('udp listener at ' + address.address + ":" + address.port);
node.log(RED._("udp.errors.listener-at") + ' ' + address.address + ":" + address.port);
if (node.multicast == "true") {
server.setBroadcast(true);
try {
server.setMulticastTTL(128);
server.addMembership(node.group,node.iface);
node.log("udp multicast group "+node.group);
node.log(RED._("udp.errors.mc-group")+" "+node.group);
} catch (e) {
if (e.errno == "EINVAL") {
node.error("Bad Multicast Address");
node.error(RED._("udp.errors.bad-mcaddress"));
} else if (e.errno == "ENODEV") {
node.error("Must be ip address of the required interface");
node.error(RED._("udp.errors.interface"));
} else {
node.error("Error :"+e.errno);
node.error(RED._("udp.errors.error")+" :"+e.errno);
}
}
}
@ -76,7 +76,7 @@ module.exports = function(RED) {
node.on("close", function() {
try {
server.close();
node.log('udp listener stopped');
node.log(RED._("udp.errors.listener-stopped"));
} catch (err) {
node.error(err);
}
@ -118,25 +118,25 @@ module.exports = function(RED) {
try {
sock.setMulticastTTL(128);
sock.addMembership(node.addr,node.iface); // Add to the multicast group
node.log('udp multicast ready : '+node.outport+' -> '+node.addr+":"+node.port);
node.log(RED._("udp.errors.mc-ready")+' : '+node.outport+' -> '+node.addr+":"+node.port);
} catch (e) {
if (e.errno == "EINVAL") {
node.error("Bad Multicast Address");
node.error(RED._("udp.errors.bad-mcaddress"));
} else if (e.errno == "ENODEV") {
node.error("Must be ip address of the required interface");
node.error(RED._("udp.errors.interface"));
} else {
node.error("Error :"+e.errno);
node.error(RED._("udp.errors.error")+" :"+e.errno);
}
}
} else {
node.log('udp broadcast ready : '+node.outport+' -> '+node.addr+":"+node.port);
node.log(RED._("udp.errors.bc-ready")+' : '+node.outport+' -> '+node.addr+":"+node.port);
}
});
} else if (node.outport != "") {
sock.bind(node.outport);
node.log('udp ready : '+node.outport+' -> '+node.addr+":"+node.port);
node.log(RED._("udp.errors.ready")+' : '+node.outport+' -> '+node.addr+":"+node.port);
} else {
node.log('udp ready : '+node.addr+":"+node.port);
node.log(RED._("udp.errors.ready")+' : '+node.addr+":"+node.port);
}
node.on("input", function(msg) {
@ -144,11 +144,11 @@ module.exports = function(RED) {
var add = node.addr || msg.ip || "";
var por = node.port || msg.port || 0;
if (add == "") {
node.warn("udp: ip address not set");
node.warn(RED._("udp.errors.ip-notset"));
} else if (por == 0) {
node.warn("udp: port not set");
node.warn(RED._("udp.errors.port-notset"));
} else if (isNaN(por) || (por < 1) || (por > 65535)) {
node.warn("udp: port number not valid");
node.warn(RED._("udp.errors.port-invalid"));
} else {
var message;
if (node.base64) {
@ -171,7 +171,7 @@ module.exports = function(RED) {
node.on("close", function() {
try {
sock.close();
node.log('udp output stopped');
node.log(RED._("udp.errors.output-stopped"));
} catch (err) {
node.error(err);
}

View File

@ -1,37 +1,65 @@
{
"common": {
"label": {
"payload": "Payload",
"topic": "Topic",
"name":"Name"
"topicph": "topic",
"name": "Name",
"nameph": "name",
"username": "Username",
"password": "Password"
},
"status": {
"connected": "connected",
"not-connected": "not connected",
"disconnected": "disconnected",
"connecting": "connecting",
"connections": "connections",
"requesting": "requesting",
"unknown": "unknown",
"error": "error",
"ok": "OK",
"closed": "closed",
"stopped": "stopped",
"close": "close",
"not-running": "not running",
"sending": "sending",
"sendfail": "send failed",
"fetching": "fetching",
"foldererror": "fetch folder error",
"messageerror": "fetch message error",
"connecterror": "connect error",
"neterror": "net error",
"joined": "joined",
"quit": "quit",
"noconnection": "no connection",
"tweeting": "tweeting",
"failed": "failed"
}
},
"inject": {
"inject":"inject",
"inject": "inject",
"repeat": "repeat = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "stopped",
"failed": "Inject failed: __error__",
"label": {
"repeat": "Repeat"
},
"timestamp": "timestamp",
"string": "string",
"blank": "blank",
"none":"none",
"interval":"interval",
"interval-time":"interval between times",
"time":"at a specific time",
"seconds":"seconds",
"minutes":"minutes",
"hours":"hours",
"between":"between",
"at":"at",
"and":"and",
"every":"every",
"none": "none",
"interval": "interval",
"interval-time": "interval between times",
"time": "at a specific time",
"seconds": "seconds",
"minutes": "minutes",
"hours": "hours",
"between": "between",
"at": "at",
"and": "and",
"every": "every",
"days": [
"Monday",
"Tuesday",
@ -41,8 +69,8 @@
"Saturday",
"Sunday"
],
"on":"on",
"onstart":"Inject once at start?",
"on": "on",
"onstart": "Inject once at start?",
"tip": "<b>Note:</b> \"interval between times\" and \"at a specific time\" will use cron.<br/>See info box for details.",
"success": "Successfully injected: __label__",
"error": "<strong>Error</strong>: __message__",
@ -52,5 +80,732 @@
"no-response": "no response from server",
"unexpected": "unexpected error (__status__) __message__"
}
},
"catch": {
"catch": "catch"
},
"debug": {
"debug": "debug",
"output": "Output",
"msgprop": "message property",
"msgobj": "complete msg object",
"to": "to",
"debtab": "debug tab",
"tabcon": "debug tab and console"
},
"exec": {
"exec": "exec",
"spawnerr": "Spawn command must be just the command - no spaces or extra parameters",
"badstdout": "Bad STDOUT",
"command": "Command",
"commandph": "command",
"append": "Append",
"extraparams": "extra input parameters",
"spawn": "Use spawn() instead of exec() ?",
"tip": "Tip: <i>spawn</i> expects only one command word - and appended args to be comma separated."
},
"function": {
"function": "function",
"functionlabel": "Function",
"outputs": "Outputs",
"tip": "See the Info tab for help writing functions."
},
"template": {
"template": "template",
"templatelabel": "Template",
"property": "Property",
"templatevalue": "This is the payload: {{payload}} !"
},
"delay": {
"delay": "delay",
"action": "Action",
"for": "For",
"delaymsg": "Delay message",
"ramdomdelay": "Random delay",
"limitrate": "Limit rate to",
"fairqueue": "Topic based fair queue",
"milisecs": "Miliseconds",
"secs": "Seconds",
"sec": "Second",
"mins": "Minutes",
"min": "Minute",
"hours": "Hours",
"hour": "Hour",
"days": "Days",
"day": "Day",
"between": "Between",
"rate": "Rate",
"msgper": "msg(s) per",
"dropmsg": "drop intermediate messages",
"delaylabel": "delay",
"limitlabel": "limit",
"randomlabel": "ramdom",
"queuelabel": "queue",
"msgperlabel": "msg/",
"buffererr": "buffer exceeded 1000 messages"
},
"trigger": {
"trigger": "trigger",
"output1": "Output",
"wait": "then wait",
"output2": "output",
"and": "and",
"below": "the value below",
"payload": "the existing payload",
"nothing": "nothing (no output)",
"milisecs": "Miliseconds",
"secs": "Seconds",
"mins": "Minutes",
"hours": "Hours",
"notext": "don't extend the timer if retriggered",
"extend": "extend the timer if retriggered",
"tip": "Setting the timeout to 0 sets an infinite timeout = single shot.",
"triggerlabel": "trigger",
"triggeroncelabel": "trigger once & infinite"
},
"comment": {
"comment": "comment",
"title": "Title",
"body": "Body - will be rendered in info tab.",
"tip1": "Tip: The text here can be styled as ",
"tip2": "Github flavoured Markdown",
"commentph": "comment",
"commentnode": "Comment node",
"commentinfo": "Use this node to add simple documentation.\n\nAnything you add will be rendered in this info panel.\n\nYou may use Markdown syntax to **enhance** the *presentation*."
},
"unknown": {
"unknown": "unknown",
"label": {
"unknownlabel": "unknown"
},
"tip": "<p>This node is a type unknown to your installation of Node-RED.</p><p><i>If you deploy with the node in this state, it's configuration will be preserved, but the flow will not start until the missing type is installed.</i></p><p>See the Info side bar for more help</p>"
}
"mqtt": {
"mqtt": "mqtt",
"label": {
"broker": "Broker",
"qos": "QoS",
"clientid": "Client ID",
"clientidph": "Leave blank for auto generated"
},
"retain": "Retain",
"true": "true",
"false": "false",
"tip": "Tip: Leave topic, qos or retain blank if you want to set them via msg properties.",
"port": "Port",
"portph": "port",
"errors": {
"not-defined": "topic not defined",
"missing-config": "missing broker configuration",
"invalid-topic": "Invalid topic specified"
}
},
"httpin": {
"httpin": "httpin",
"label": {
"method": "Method",
"url": "URL",
"urlph": "/url",
"httpph": "http://",
"doc": "Doc",
"return": "Return"
},
"setby": "- set by msg.method -",
"basicauth": "Use basic authentication ?",
"utf8": "a UTF-8 string",
"binary": "a binary buffer",
"json": "a parsed JSON object",
"in-tip": "The url will be relative to ",
"res-tip": "The messages sent to this node <b>must</b> originate from an <i>http input</i> node",
"req-tip": "Tip: If the JSON parse fails the fetched string is returned as-is.",
"httpreq": "http request",
"errors": {
"not-created": "Cannot create http-in node when httpNodeRoot set to false",
"no-response": "No response object",
"not-overridden": "Warning: msg properties can no longer override fixed node properties. Use explicit override option. See bit.ly/nr-override-msg-props",
"json-error": "JSON parse error",
"no-url": "No url specified"
}
},
"websocket": {
"websocket": "websocket",
"label": {
"type": "Type",
"path": "Path",
"url": "URL",
"pathph": "/ws/example",
"urlph": "ws://example.com/ws"
},
"listenon": "Listen on",
"connectto": "Connect to",
"payload": "Send/Receive payload",
"message": "Send/Receive entire message",
"path-tip1": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The listener can be configured to send or receive the entire message object as a JSON formatted string.",
"path-tip2": "This path will be relative to ",
"url-tip1": "URL should use ws:&#47;&#47; or wss:&#47;&#47; scheme and point to an existing websocket listener.",
"url-tip2": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The client can be configured to send or receive the entire message object as a JSON formatted string.",
"errors": {
"connect-error": "An error occured on the ws connection: ",
"send-error": "An error occurred while sending: ",
"missing-conf": "Missing server configuration"
}
},
"watch": {
"watch": "watch",
"label": {
"files": "File(s)",
"filesph": "File(s) or Directory"
},
"tip": "On Windows you must use double back-slashes \\\\ in any directory names."
},
"serial": {
"serial": "serial",
"label": {
"serialport": "Serial Port",
"serialportph": "/dev/ttyUSB0/",
"settings": "Settings",
"baudrate": "Baud Rate",
"databits": "Data Bits",
"parity": "Parity",
"stopbits": "Stop Bits",
"input": "Input",
"split": "Split input",
"deliver": "and deliver",
"output": "Output",
"serial": "serial",
"none": "none"
},
"none": "None",
"even": "Even",
"mark": "Mark",
"odd": "Odd",
"space": "Space",
"character": "on the character",
"timeout": "after a timeout of",
"length": "into fixed lengths of",
"ascii": "ascii strings",
"binary": "binary buffers",
"addsplit": "add split character to output messages",
"split-tip": "Tip: the \"Split on\" character is used to split the input into separate messages. It can also be added to every message sent out to the serial port.",
"timeout-tip": "Tip: In timeout mode timeout starts from arrival of first character.",
"errors": {
"missing-conf": "missing serial config",
"never-get": "should never get here",
"serial-port": "serial port",
"error": "error",
"unexpected-close": "closed unexpectedly",
"opened": "opened at",
"baud": "baud",
"gone-away": "gone away",
"closed": "closed"
}
},
"tcpin": {
"tcpin": "tcpin",
"label": {
"type": "Type",
"output": "Output",
"port": "port",
"host": "at host",
"a": "a",
"payload": "payload(s)",
"delimited": "delimited by",
"close-connection": "Close connection after each message is sent ?",
"decode-base64": "Decode Base64 message ?",
"server": "Server",
"return": "Return",
"numberph": "number"
},
"listen": "Listen on",
"connect": "Connect to",
"reply": "Reply to TCP",
"stream": "stream of",
"single": "single",
"buffer": "Buffer",
"string": "String",
"base64": "Base64 String",
"timeout": "after a fixed timeout of",
"character": "when character received is",
"number": "a fixed number of characters",
"never": "never. Keep connection open",
"out-tip1": "Closing the connection after each message is generally not a good thing - but is useful to indicate an end-of-file for example.",
"out-tip2": "Closing the connection after each message is generally not a good thing - but is useful to indicate an end-of-file for example. The receiving client will need to reconnect.",
"req-tip": "<b>Tip:</b> Outputs a binary <b>Buffer</b>, so you may want to .toString() it.</br/><b>Tip:</b> Leave host and port blank if you want to overide with msg.host and msg.port properties.",
"errors": {
"connecting-to": "connecting to",
"connected-to": "connected to",
"connection-lost": "connection lost to",
"timeout": "timeout closed socket port",
"listening-port": "listening on port",
"stopped-listening": "stopped listening on port",
"error": "error",
"connection-from": "connection from",
"connection-closed": "connection closed from",
"socket-error": "socket error from",
"client-connected": "client connected",
"data": "data:",
"client-disconnected": "client disconnected",
"no-host": "Host and/or port not set",
"timeout": "connect timeout",
"cannot-listen": "unable to listen on port",
"connect-fail": "connect failed"
}
},
"udp": {
"udp": "udp",
"label": {
"listen": "Listen for",
"onport": "on Port",
"using": "using",
"output": "Output",
"onportph": "port",
"group": "Group",
"interface": "Interface",
"interfaceph": "(optional) ip address of eth0",
"send": "Send a",
"toport": "to port",
"toportph": "port",
"address": "Address",
"addressph": "destination ip",
"decode-base64": "Decode Base64 encoded payload ?",
"outportph": "port"
},
"udpmsgs": "udp messages",
"mcmsgs": "multicast messages",
"udpmsg": "udp message",
"bcmsg": "broadcast message",
"mcmsg": "multicast message",
"buffer": "a Buffer",
"string": "a String",
"base64": "a Base64 encoded string",
"bindrandom": "bind to random local port",
"bindlocal": "bind to local port",
"bindtarget": "bind to target port",
"in-tip": "Tip: Make sure your firewall will allow the data in.",
"out-tip": "Tip: leave address and port blank if you want to set using <b>msg.ip</b> and <b>msg.port</b>.",
"errors": {
"listener-at": "udp listener at",
"mc-group": "udp multicast group",
"listener-stopped": "udp listener stopped",
"mc-ready": "udp multicast ready",
"bc-ready": "udp broadcast ready",
"ready": "udp ready",
"output-stopped": "udp output stopped",
"ip-notset": "udp: ip address not set",
"port-notset": "udp: port not set",
"port-invalid": "udp: port number not valid",
"access-error": "UDP access error, you may need root access for ports below 1024",
"udp-error": "UDP error",
"bad-mcaddress": "Bad Multicast Address",
"interface": "Must be ip address of the required interface",
"error": "Error"
}
},
"switch": {
"switch": "switch",
"label": {
"rule": "rule",
"switchlabel": "switch"
},
"checkall": "checking all rules",
"stopfirst": "stopping after first match"
},
"change": {
"change": "change",
"label": {
"rules": "Rules",
"rule": "rule"
},
"set": "Set",
"change": "Change",
"delete": "Delete",
"to": "to",
"search": "Search for",
"replace": "Replace with",
"regex": "Use regular expressions",
"errors": {
"invalid-from": "Invalid 'from' property:"
}
},
"range": {
"range": "range",
"label": {
"action": "Action",
"inputrange": "Map the input range",
"resultrange": "to the result range",
"from": "from",
"to": "to",
"eg0ph": "e.g. 0",
"eg99ph": "e.g. 99",
"eg255ph": "e.g. 255",
"roundresult": "Round result to the nearest integer?",
"rangelabel": "range"
},
"scale-payload": "Scale msg.payload",
"scale-limit": "Scale and limit to the target range",
"scale-wrap": "Scale and wrap within the target range",
"tip": "Tip: This node ONLY works with numbers.",
"errors": {
"notnumber": "Not a number"
}
},
"csv": {
"csv": "csv",
"label": {
"columns": "Columns",
"columnsph": "comma-separated column names",
"separator": "Separator",
"c2o": "CSV-to-Object options",
"o2c": "Object-to-CSV options",
"input": "Input",
"firstrow": "first row contains column names",
"output": "Output",
"includerow": "include column name row",
"newline": "Newline"
},
"comma": "comma",
"tab": "tab",
"space": "space",
"semicolon": "semicolon",
"colon": "colon",
"hashtag": "hashtag",
"other": "other...",
"row": "a message per row",
"array": "a single message [array]",
"linux": "Linux (\\n)",
"mac": "Mac (\\r)",
"windows": "Windows (\\r\\n)",
"errors": {
"csv_js": "This node only handles csv strings or js objects."
}
},
"html": {
"html": "html",
"label": {
"select": "Select",
"output": "Output"
},
"htmlcontent": "the html content of the elements",
"textcontent": "only the text content of the elements",
"single": "as a single message containing an array",
"multi": "as multiple messages, one for each element",
"tip": "Tip: The <b>Select</b> value is a <a href=\"https://github.com/fb55/CSSselect#user-content-supported-selectors\" target=\"_new\"><i><u>CSS Selector</u></i></a>, similar to a jQuery selector."
},
"json": {
"json": "json",
"errors": {
"dropped": "Dropped"
}
},
"xml": {
"xml": "xml",
"label": {
"represent": "Represent XML tag attributes as a property named",
"prefix": "Prefix to access character content",
"advanced": "Advanced options"
},
"tip": "There is no simple way to convert XML attributes to JSON so the approach taken here is to add a property, named $ by default, to the JSON structure.",
"errors": {
"xml_js": "This node only handles xml strings or js objects."
}
},
"sentiment": {
"sentiment": "sentiment",
"label": {
"sentimentlabel": "sentiment"
}
},
"arduino": {
"arduino": "arduino",
"label": {
"arduino": "Arduino",
"pin": "Pin",
"type": "Type",
"port": "Port",
"portph": "e.g. /dev/ttyUSB0 COM1"
},
"digitalpin": "Digital pin",
"analoguepin": "Analogue pin",
"digital": "Digital (0/1)",
"analogue": "Analogue (0-255)",
"servo": "Servo (0-180)",
"io-tip": "<b>Note:</b> You cannot use the same pin for both output and input.",
"conf-tip": "<b>Tip:</b> Use search to list serial ports, or leave blank to connect to first device found.",
"status": {
"connectfirst": "connecting to first board found",
"connect": "connecting to __device__",
"connected": "connected to __device__",
"version": "version: __version__",
"portclosed": "port closed"
},
"errors": {
"portnotconf": "port not configured",
"devnotfound": "device __dev__ not found. Trying to find board."
}
},
"rpi-gpio": {
"rpi-gpio": "rpi-gpio",
"label": {
"gpiopin": "GPIO Pin",
"selectpin": "select pin",
"registor": "Registor ?",
"readinitial": "Read initial state of pin on deploy/restart ?",
"type": "Type",
"initpin": "Initialise pin state ?",
"button": "Button",
"pimouse": "Pi Mouse",
"left": "Left",
"right": "Right",
"middle": "Middle"
},
"none": "none",
"pullup": "pullup",
"pulldown": "pulldown",
"digout": "Digital output",
"pwmout": "PWM output",
"initpin0": "initial level of pin - low (0)",
"initpin1": "initial level of pin - high (1)",
"left": "left",
"right": "right",
"middle": "middle",
"any": "any",
"pinname": "Pin",
"alreadyuse": "already in use",
"alreadyset": "already set as",
"pin-tip": "<b>Pins in Use</b>: ",
"in-tip": "Tip: Only Digital Input is supported - input must be 0 or 1.",
"dig-tip": "<b>Tip</b>: For digital output - input must be 0 or 1.",
"pwm-tip": "<b>Tip</b>: For PWM output - input must be between 0 and 100.",
"errors": {
"digout": "digital output",
"input": "input",
"pullup": "input with pull up",
"pulldown": "input with pull down",
"pwmout": "PWM output",
"ignorenode": "Ignoring Raspberry Pi specific node.",
"closed": "closed",
"version": "Version command failed for some reason.",
"sawpitype": "Saw Pi Type",
"libnotfound": "Can't find Pi RPi.GPIO python library.",
"gpiopin": "GPIO pin",
"alreadyset": "already set as",
"invalidpin": "Invalid GPIO pin",
"invalidinput": "Invalid input",
"needtobeexecutable": "needs to be executable.",
"mustbeexecutable": "nrgpio must to be executable.",
"commandnotfound": "nrgpio command not found",
"commandnotexecutable": "nrgpio command not executable",
"error": "error",
"pythoncommandnotfound": "nrpgio python command not running"
}
},
"tail": {
"tail": "tail",
"label": {
"filename": "Filename",
"filenameph": "filename",
"splitlines": "Split lines if we see \\n ?"
},
"errors": {
"windowsnotsupport": "Info : Currently not supported on Windows."
}
},
"file": {
"file": "file",
"label": {
"filename": "Filename",
"filenameph": "filename",
"action": "Action",
"addnewline": "Add newline (\\n) to each payload ?",
"outputas": "Ourput as",
"filelabel": "file",
"deletelabel": "delete"
},
"append": "append to file",
"overwrite": "overwrite file",
"delete": "delete file",
"utf8": "a utf8 string",
"buffer": "a Buffer",
"errors": {
"wrotefile": "wrote to file",
"deletedfile": "deleted file",
"appendedfile": "appended to file",
"nooverride": "Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props",
"nofilename": "No filename specified",
"invaliddelete": "Warning: Invalid delete. Please use specific delete option in config dialog.",
"deletefail": "failed to delete file",
"writefail": "failed to write to file",
"appendfail": "failed to append to file"
}
},
"redisout": {
"redis": "redis",
"label": {
"host": "Host",
"port": "Port",
"key": "Key",
"keyph": "Redis Key",
"type": "Type"
},
"string": "String",
"hash": "Hash",
"set": "Set",
"list": "List",
"tip": "If key is blank, the topic will be used as the key.<br>If type is hash, payload should be an object or field=value string.",
"errors": {
"connectedto": "connected to",
"invalidpayload": "Invalid payload for redis hash",
"nokey": "No key or topic set"
}
},
"mongodb": {
"mongodb": "mongodb",
"label": {
"host": "Host",
"port": "Port",
"database": "Database",
"username": "Username",
"password": "Password",
"server": "Server",
"collection": "Collection",
"collectionph": "collection",
"operation": "Operation",
"onlystore": "Only store msg.payload object",
"createnew": "Create a new document if no match found",
"updateall": "Update all matching documents"
},
"save": "save",
"insert": "insert",
"update": "update",
"remove": "remove",
"find": "find",
"count": "count",
"aggregate": "aggregate",
"tip": "<b> Tip:</b> If no collection is set, ensure <b>msg.collection</b> will contain the collection name",
"errors": {
"nocollection": "No collection defined",
"missingconfig": "missing mongodb configuration"
}
},
"feedparse": {
"feedparse": "feedparse",
"label": {
"feedurl": "Feed url",
"repeat": "Repeat",
"repeatph": "minutes",
"min": "(M)"
},
"errors": {
"badstatuscode": "error - Bad status code",
"invalidurl": "Invalid url"
}
},
"email": {
"email": "email",
"label": {
"to": "To",
"server": "Server",
"port": "Port",
"userid": "Userid",
"password": "Password",
"repeat": "Check Repeat (S)",
"folder": "Folder"
},
"inbox": "IIINBOX",
"cred-tip": "<b>Note:</b> Copied credentials from global emailkeys.js file.",
"recent-tip": "Tip: <b>ONLY</b> retrieves the single most recent email.",
"errors": {
"messagesent": "Message sent",
"repeat": "repeat",
"message": "message",
"finished": "Finished",
"newemail": "received new email",
"duplicate": "duplicate not sent",
"inboxzero": "you have achieved inbox zero",
"error": "error",
"nooverride": "Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props",
"nocredentials": "No Email credentials found. See info panel.",
"nopayload": "No payload to send",
"messageerror": "fetch message error",
"nouserid": "No e-mail userid set",
"nopassword": "No e-mail password set",
"fetchfail": "Failed to fetch folder",
"yourfile": "Your file from Node-RED is attached"
}
},
"irc": {
"irc": "irc",
"label": {
"ircserver": "IRC Server",
"channel": "Channel",
"action": "Action",
"port": "Port",
"portph": "port",
"ssl": "Use Secure SSL connection ?",
"self": "Allow self-signed certificates ?",
"nickname": "Nickname"
},
"payload": "Send payload to channel(s)",
"topic": "Use msg.topic to set nickname or channel(s)",
"msg": "Send complete msg object to channel(s)",
"in-tip": "The channel to join must start with a # (as per normal irc rules...)<br/>You may join multiple channels by comma separating a list - #chan1,#chan2,etc.",
"out-tip": "The channel to join must start with a # (as per normal irc rules...)<br/>Sending the complete object will stringify the whole msg object before sending.",
"errors": {
"connect": "CONNECT",
"err": "ERR",
"net": "NET",
"connected": "CONNECTED",
"online": "ONLINE",
"joined": "JOINED",
"ping": "PING from",
"quit": "QUIT",
"restart": "restart",
"connectionlost": "CONNECTION LOST ?",
"hasjoined": "has joined",
"sentinvite": "sent invite to",
"hasleft": "has left",
"hasquit": "has quit",
"kickedfrom": "was kicked from",
"rawcommand": "RAW command",
"topicnotset": "msg.topic not set"
}
},
"twitter": {
"twitter": "twitter",
"label": {
"loginas": "Log in as",
"search": "Search",
"for": "for",
"forph": "comma-separated words, @ids, #tags",
"user": "User",
"userph": "comma-separated @twitter handles",
"dmslabel": "DMs",
"tweetslabel": "tweets",
"twitter": "Twitter",
"clickhere": "Click here to authenticate with Twitter.",
"twitterid": "Twitter ID"
},
"public": "all public tweets",
"follow": "the tweets of who you follow",
"user": "the tweets of specific users",
"direct": "your direct messages",
"tip": "Tip: Use commas without spaces between multiple search terms. Comma = OR, Space = AND.<br/>The Twitter API WILL NOT deliver 100% of all tweets.<br/>Tweets of who you follow will include their retweets and favourites.",
"errors": {
"badgeo":"possible bad geo area format. Should be lower-left lon, lat, upper-right lon, lat",
"oauthbroke":"something in twitter oauth broke.",
"ratelimit":"tweet rate limit hit",
"streamerror":"Stream error",
"enexpectedend":"twitter ended unexpectedly",
"truncated":"Tweet greater than 140 : truncated",
"nopayload":"No payload to tweet",
"invalidtag":"Invalid tag property",
"missingcredentials":"missing twitter credentials",
"sendfail":"Send tweet failed",
"oautherror1": "<h2>Oh no!</h2>",
"oautherror2": "<p>Something went wrong with the authentication process. The following error was returned:</p>",
"oautherror3": "<p><b>__statusCode__</b>: __errorData__</p>",
"oautherror4": "<p>One known cause of this type of failure is if the clock is wrong on system running Node-RED.",
"authorized": "<html><head></head><body>Authorised - you can close this window and return to Node-RED</body></html>"
}
}
}

View File

@ -16,8 +16,8 @@
<script type="text/x-red" data-template-name="switch">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-row">
If msg.<input type="text" id="node-input-property" style="width: 200px;"/>
@ -28,12 +28,12 @@
</div>
</div>
<div class="form-row">
<a href="#" class="btn btn-mini" id="node-input-add-rule" style="margin-top: 4px;"><i class="fa fa-plus"></i> rule</a>
<a href="#" class="btn btn-mini" id="node-input-add-rule" style="margin-top: 4px;"><i class="fa fa-plus"></i> <span data-i18n="switch.label.rule"></span></a>
</div>
<div class="form-row">
<select id="node-input-checkall" style="width:100%; margin-right:5px;">
<option value="true">checking all rules</option>
<option value="false">stopping after first match</option>
<option value="true" data-i18n="switch.checkall"></option>
<option value="false" data-i18n="switch.stopfirst"></option>
</select>
</div>
</script>
@ -61,7 +61,7 @@
outputs: 1,
icon: "switch.png",
label: function() {
return this.name||"switch";
return this.name||this._("switch.label.switchlabel");
},
oneditprepare: function() {

View File

@ -16,11 +16,11 @@
<script type="text/x-red" data-template-name="change">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-row" style="margin-bottom:0;">
<label><i class="fa fa-list"></i> Rules</label>
<label><i class="fa fa-list"></i> <span data-i18n="change.label.rules"></span></label>
</div>
<div class="form-row node-input-rule-container-row" style="margin-bottom: 0px;">
<div id="node-input-rule-container-div" style="box-sizing: border-box; border-radius: 5px; height: 300px; padding: 5px; border: 1px solid #ccc; overflow-y:scroll;">
@ -28,7 +28,7 @@
</div>
</div>
<div class="form-row">
<a href="#" class="btn btn-mini" id="node-input-add-rule" style="margin-top: 4px;"><i class="fa fa-plus"></i> rule</a>
<a href="#" class="btn btn-mini" id="node-input-add-rule" style="margin-top: 4px;"><i class="fa fa-plus"></i> <span data-i18n="change.label.rule"></span></a>
</div>
</script>
@ -92,6 +92,13 @@
return this.name ? "node_label_italic" : "";
},
oneditprepare: function() {
var set = this._("change.set");
var change = this._("change.change");
var del = this._("change.delete");
var to = this._("change.to");
var search = this._("change.search");
var replace = this._("change.replace");
var regex = this._("change.regex");
if (this.reg === null) { $("#node-input-reg").prop('checked', true); }
$("#node-input-action").change();
@ -104,7 +111,7 @@
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width: 100px"}).appendTo(row1);
var selectOptions = [{v:"set",l:"Set"},{v:"change",l:"Change"},{v:"delete",l:"Delete"}];
var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del}];
for (var i=0;i<3;i++) {
selectField.append($("<option></option>").val(selectOptions[i].v).text(selectOptions[i].l));
}
@ -118,21 +125,21 @@
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text("to").appendTo(row2);
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text(to).appendTo(row2);
var propertyValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-value",type:"text"}).appendTo(row2);
var row3_1 = $('<div/>').appendTo(row3);
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text("Search for").appendTo(row3_1);
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text(search).appendTo(row3_1);
var fromValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-search-value",type:"text"}).appendTo(row3_1);
var row3_2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text("replace with").appendTo(row3_2);
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text(replace).appendTo(row3_2);
var toValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-replace-value",type:"text"}).appendTo(row3_2);
var row3_3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
var id = "node-input-rule-property-regex-"+Math.floor(Math.random()*10000);
var useRegExp = $('<input/>',{id:id,class:"node-input-rule-property-re",type:"checkbox", style:"margin-left: 150px; margin-right: 10px; display: inline-block; width: auto; vertical-align: top;"}).appendTo(row3_3);
$('<label/>',{for:id,style:"width: auto;"}).text("Use regular expressions").appendTo(row3_3);
$('<label/>',{for:id,style:"width: auto;"}).text(regex).appendTo(row3_3);
selectField.change(function() {

View File

@ -52,7 +52,7 @@ module.exports = function(RED) {
rule.from = new RegExp(rule.from, "g");
} catch (e) {
valid = false;
this.error("Invalid 'from' property: "+e.message);
this.error(RED._("change.errors.invalid-from")+" "+e.message);
}
}
}

View File

@ -16,35 +16,35 @@
<script type="text/x-red" data-template-name="range">
<div class="form-row">
<label for="node-input-action"><i class="fa fa-dot-circle-o"></i> Action</label>
<label for="node-input-action"><i class="fa fa-dot-circle-o"></i> <span data-i18n="range.label.action"></span></label>
<select id="node-input-action" style="width:70%; margin-right:5px;">
<option value="scale">Scale msg.payload</option>
<option value="clamp">Scale and limit to the target range</option>
<option value="roll">Scale and wrap within the target range</option>
<option value="scale" data-i18n="range.scale-payload"></option>
<option value="clamp" data-i18n="range.scale-limit"></option>
<option value="roll" data-i18n="range.scale-wrap"></option>
</select>
</div>
<br/>
<div class="form-row"><i class="fa fa-sign-in"></i> Map the input range:</div>
<div class="form-row"><i class="fa fa-sign-in"></i> <span data-i18n="range.label.inputrange"></span>:</div>
<div class="form-row"><label></label>
from: <input type="text" id="node-input-minin" placeholder="e.g. 0" style="width:100px;"/>
&nbsp;&nbsp;to: <input type="text" id="node-input-maxin" placeholder="e.g. 99" style="width:100px;"/>
<span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minin" data-i18n="[placeholder]range.label.eg0ph" style="width:100px;"/>
&nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxin" data-i18n="[placeholder]range.label.eg99ph" style="width:100px;"/>
</div>
<div class="form-row"><i class="fa fa-sign-out"></i> to the result range:</div>
<div class="form-row"><i class="fa fa-sign-out"></i> <span data-i18n="range.label.resultrange"></span>:</div>
<div class="form-row"><label></label>
from: <input type="text" id="node-input-minout" placeholder="e.g. 0" style="width:100px;"/>
&nbsp;&nbsp;to: <input type="text" id="node-input-maxout" placeholder="e.g. 255" style="width:100px;"/>
<span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minout" data-i18n="[placeholder]range.label.eg0ph" style="width:100px;"/>
&nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxout" data-i18n="[placeholder]range.label.eg255ph" style="width:100px;"/>
</div>
<br/>
<div class="form-row"><label></label>
<input type="checkbox" id="node-input-round" style="display: inline-block; width: auto; vertical-align: top;">
<label style="width: auto;" for="node-input-round">Round result to the nearest integer?</label></input>
<label style="width: auto;" for="node-input-round"><span data-i18n="range.label.roundresult"></span></label></input>
</div>
<br/>
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips" id="node-tip">Tip: This node ONLY works with numbers.</div>
<div class="form-tips" id="node-tip"><span data-i18n="range.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="range">
@ -72,7 +72,7 @@
outputs: 1,
icon: "range.png",
label: function() {
return this.name || "range";
return this.name || this._("range.label.rangelabel");
},
labelStyle: function() {
return this.name ? "node_label_italic" : "";

View File

@ -42,7 +42,7 @@ module.exports = function(RED) {
if (node.round) { msg.payload = Math.round(msg.payload); }
node.send(msg);
}
else { node.log("Not a number: "+msg.payload); }
else { node.log(RED._("range.errors.notnumber")+": "+msg.payload); }
}
else { node.send(msg); } // If no payload - just pass it on.
});

View File

@ -17,52 +17,52 @@
<script type="text/x-red" data-template-name="csv">
<div class="form-row">
<label for="node-input-temp"><i class="fa fa-list"></i> Columns</label>
<input type="text" id="node-input-temp" placeholder="comma-separated column names">
<label for="node-input-temp"><i class="fa fa-list"></i> <span data-i18n="csv.label.columns"></span></label>
<input type="text" id="node-input-temp" data-i18n="[placeholder]csv.label.columnsph">
</div>
<div class="form-row">
<label for="node-input-select-sep"><i class="fa fa-text-width"></i> Separator</label>
<label for="node-input-select-sep"><i class="fa fa-text-width"></i> <span data-i18n="csv.label.separator"></span></label>
<select style="width: 150px" id="node-input-select-sep">
<option value=",">comma</option>
<option value="\t">tab</option>
<option value=" ">space</option>
<option value=";">semicolon</option>
<option value=":">colon</option>
<option value="#">hashtag</option>
<option value="">other...</option>
<option value="," data-i18n="csv.comma"></option>
<option value="\t" data-i18n="csv.tab"></option>
<option value=" " data-i18n="csv.space"></option>
<option value=";" data-i18n="csv.semicolon"></option>
<option value=":" data-i18n="csv.colon"></option>
<option value="#" data-i18n="csv.hashtag"></option>
<option value="" data-i18n="csv.other"></option>
</select>
<input style="width: 40px;" type="text" id="node-input-sep" pattern=".">
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<hr align="middle"/>
<div class="form-row">
<label style="width: 100%;"><i class="fa fa-gears"></i> CSV-to-Object options</label>
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-in"></i> Input</label>
<input style="width: 30px" type="checkbox" id="node-input-hdrin"><label style="width: auto;" for="node-input-hdrin">first row contains column names</span>
<label style="width: 100%;"><i class="fa fa-gears"></i> <span data-i18n="csv.label.c2o"></span></label>
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label>
<input style="width: 30px" type="checkbox" id="node-input-hdrin"><label style="width: auto;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></span>
</div>
<div class="form-row">
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-out"></i> Output</label>
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
<select type="text" id="node-input-multi" style="width: 250px;">
<option value="one">a message per row</option>
<option value="mult">a single message [array]</option>
<option value="one" data-i18n="csv.row"></option>
<option value="mult" data-i18n="csv.array"></option>
</select>
</div>
<hr align="middle"/>
<div class="form-row">
<label style="width: 100%;"><i class="fa fa-gears"></i> Object-to-CSV options</label>
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-in"></i> Output</label>
<input style="width: 30px" type="checkbox" id="node-input-hdrout"><label style="width: auto;" for="node-input-hdrout">include column name row</span>
<label style="width: 100%;"><i class="fa fa-gears"></i> <span data-i18n="csv.label.o2c"></span></label>
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.output"></span></label>
<input style="width: 30px" type="checkbox" id="node-input-hdrout"><label style="width: auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span>
</div>
<div class="form-row">
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-align-left"></i> Newline</label>
<label style="margin-left: 10px; margin-right: -10px;"><i class="fa fa-align-left"></i> <span data-i18n="csv.label.newline"></span></label>
<select style="width: 150px" id="node-input-ret">
<option value='\n'>Linux (\n)</option>
<option value='\r'>Mac (\r)</option>
<option value='\r\n'>Windows (\r\n)</option>
<option value='\n' data-i18n="csv.linux"></option>
<option value='\r' data-i18n="csv.mac"></option>
<option value='\r\n' data-i18n="csv.windows"></option>
</select>
</div>
</script>

View File

@ -163,7 +163,7 @@ module.exports = function(RED) {
}
catch(e) { node.error(e,msg); }
}
else { node.warn("This node only handles csv strings or js objects."); }
else { node.warn(RED._("csv.errors.csv_js")); }
}
else { node.send(msg); } // If no payload - just pass it on.
});

View File

@ -16,31 +16,31 @@
<script type="text/x-red" data-template-name="html">
<div class="form-row">
<label for="node-input-tag"><i class="fa fa-filter"></i> Select</label>
<input type="text" id="node-input-tag" placeholder="h1">
<label for="node-input-tag"><i class="fa fa-filter"></i> <span data-i18n="html.label.select"></span></label>
<input type="text" id="node-input-tag" style="width:73% !important" placeholder="h1">
</div>
<div class="form-row">
<label for="node-input-ret"><i class="fa fa-sign-out"></i> Output</label>
<select id="node-input-ret" style="width:73% !important">
<option value="html">the html content of the elements</option>
<option value="text">only the text content of the elements</option>
<label for="node-input-ret"><i class="fa fa-sign-out"></i> <span data-i18n="html.label.output"></span></label>
<select id="node-input-ret" style="width:76% !important">
<option value="html" data-i18n="html.htmlcontent"></option>
<option value="text"data-i18n="html.textcontent"></option>
<!-- <option value="attr">an object of any attributes</option> -->
<!-- <option value="val">return the value from a form element</option> -->
</select>
</div>
<div class="form-row">
<label for="node-input-as">&nbsp;</label>
<select id="node-input-as" style="width:73% !important">
<option value="single">as a single message containing an array</option>
<option value="multi">as multiple messages, one for each element</option>
<select id="node-input-as" style="width:76% !important">
<option value="single" data-i18n="html.single"></option>
<option value="multi" data-i18n="html.multi"></option>
</select>
</div>
<br/>
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" style="width:73% !important" data-i18n="[placeholder]common.label.nameph">
</div>
<div class="form-tips">Tip: The <b>Select</b> value is a <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_new"><i><u>CSS Selector</u></i></a>, similar to a jQuery selector.</div>
<div class="form-tips"><span data-i18n="[html]html.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="html">

View File

@ -16,8 +16,8 @@
<script type="text/x-red" data-template-name="json">
<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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>

View File

@ -35,9 +35,9 @@ module.exports = function(RED) {
msg.payload = JSON.stringify(msg.payload);
node.send(msg);
}
else { node.warn("Dropped: "+msg.payload); }
else { node.warn(RED._("json.errors.dropped")+": "+msg.payload); }
}
else { node.warn("Dropped: "+msg.payload); }
else { node.warn(RED._("json.errors.dropped")+": "+msg.payload); }
}
else { node.send(msg); } // If no payload - just pass it on.
});

View File

@ -16,37 +16,20 @@
<script type="text/x-red" data-template-name="xml">
<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" style="width:280px !important">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph" style="width:280px !important">
</div>
<div class="form-row" id="advanced">
</div>
<div id="advanced-options">
<div class="form-row">
<i class="fa fa-key"></i> Represent XML tag attributes as a property named <input type="text" id="node-input-attr" style="width:20px !important">
<i class="fa fa-key"></i> <span data-i18n="xml.label.represent"></span> <input type="text" id="node-input-attr" style="width:20px !important">
</div>
<div class="form-row">
<i class="fa fa-key"></i> Prefix to access character content <input type="text" id="node-input-chr" style="width:20px !important">
<i class="fa fa-key"></i> <span data-i18n="xml.label.prefix"></span> <input type="text" id="node-input-chr" style="width:20px !important">
</div>
<div class="form-tips">There is no simple way to convert XML attributes to JSON
so the approach taken here is to add a property, named $ by default, to the JSON structure.</div>
<div class="form-tips"><span data-i18n="xml.tip"></span></div>
</div>
<script> {
var showadvanced = showadvanced || true;
var showall = function() {
showadvanced = !showadvanced;
if (showadvanced) {
$("#advanced-options").show();
$("#advanced").html('<label for="node-advanced" style="width:200px !important"><i class="fa fa-minus-square"></i> Advanced options</label>');
}
else {
$("#advanced-options").hide();
$("#advanced").html('<label for="node-advanced" style="width:200px !important"><i class="fa fa-plus-square"></i> Advanced options ...</label>');
}
};
showall();
$("#advanced").click( function() { showall(); });
} </script>
</script>
<script type="text/x-red" data-help-name="xml">
@ -73,6 +56,23 @@
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var showadvanced = showadvanced || true;
var advanced = this._("xml.label.advanced");
var showall = function() {
showadvanced = !showadvanced;
if (showadvanced) {
$("#advanced-options").show();
$("#advanced").html('<label for="node-advanced" style="width:200px !important"><i class="fa fa-minus-square"></i> '+advanced+'</label>');
}
else {
$("#advanced-options").hide();
$("#advanced").html('<label for="node-advanced" style="width:200px !important"><i class="fa fa-plus-square"></i> '+advanced+' ...</label>');
}
};
showall();
$("#advanced").click( function() { showall(); });
}
});
</script>

View File

@ -40,7 +40,7 @@ module.exports = function(RED) {
}
});
}
else { node.warn("This node only handles xml strings or js objects."); }
else { node.warn(RED._("xml.errors.xml_js")); }
}
else { node.send(msg); } // If no payload - just pass it on.
});

View File

@ -16,17 +16,17 @@
<script type="text/x-red" data-template-name="tail">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="fa fa-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="tail.label.filename"></span></label>
<input type="text" id="node-input-filename" data-i18n="[placeholder]tail.label.filenameph">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-split" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-split" style="width: 70%;">Split lines if we see \n ?</label>
<label for="node-input-split" style="width: 70%;"><span data-i18n="tail.label.splitlines"></span></label>
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
<!-- <div class="form-tips">WON'T work on Windows.</div> -->
</script>

View File

@ -20,7 +20,7 @@ module.exports = function(RED) {
var plat = require('os').platform();
if (plat.match(/^win/)) {
throw "Info : Currently not supported on Windows.";
throw RED._("tail.errors.windowsnotsupport");
}
function TailNode(n) {

View File

@ -16,25 +16,25 @@
<script type="text/x-red" data-template-name="file">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="fa fa-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
<input type="text" id="node-input-filename" data-i18n="[placeholder]file.label.filenameph">
</div>
<div class="form-row">
<label for="node-input-overwriteFile"><i class="fa fa-random"></i> Action</label>
<label for="node-input-overwriteFile"><i class="fa fa-random"></i> <span data-i18n="file.label.action"></span></label>
<select type="text" id="node-input-overwriteFile" style="display: inline-block; width: 250px; vertical-align: top;">
<option value="false">append to file</option>
<option value="true">overwrite file</option>
<option value="delete">delete file</option>
<option value="false" data-i18n="file.append"></option>
<option value="true" data-i18n="file.overwrite"></option>
<option value="delete" data-i18n="file.delete"></option>
</select>
</div>
<div class="form-row" id="node-appline">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-appendNewline" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-appendNewline" style="width: 70%;">Add newline (\n) to each payload ?</label>
<label for="node-input-appendNewline" style="width: 70%;"><span data-i18n="file.label.addnewline"></span></label>
</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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -48,19 +48,19 @@
<script type="text/x-red" data-template-name="file in">
<div class="form-row">
<label for="node-input-filename"><i class="fa fa-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
<input type="text" id="node-input-filename" data-i18n="[placeholder]file.label.filenameph">
</div>
<div class="form-row">
<label for="node-input-format"><i class="fa fa-sign-out"></i> Output as</label>
<label for="node-input-format"><i class="fa fa-sign-out"></i> <span data-i18n="file.label.outputas"></span></label>
<select id="node-input-format">
<option value="utf8">a utf8 string</option>
<option value="">a Buffer</option>
<option value="utf8" data-i18n="file.utf8"></option>
<option value="" data-i18n="file.buffer"></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">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div>
</script>
@ -84,8 +84,8 @@
icon: "file.png",
align: "right",
label: function() {
if (this.overwriteFile === "delete") { return this.name||"delete "+this.filename; }
else { return this.name||this.filename||"file"; }
if (this.overwriteFile === this._("file.label.deletelabel")) { return this.name||this._("file.label.deletelabel")+" "+this.filename; }
else { return this.name||this.filename||this._("file.label.filelabel"); }
},
labelStyle: function() {
return this.name?"node_label_italic":"";
@ -110,7 +110,7 @@
outputs:1,
icon: "file.png",
label: function() {
return this.name||this.filename||"file";
return this.name||this.filename||this._("file.label.filelabel");
},
labelStyle: function() {
return this.name?"node_label_italic":"";

View File

@ -27,17 +27,17 @@ module.exports = function(RED) {
this.on("input",function(msg) {
var filename = this.filename || msg.filename || "";
if (msg.filename && n.filename && (n.filename !== msg.filename)) {
node.warn("Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props");
node.warn(RED._("file.errors.nooverride"));
}
if (!this.filename) {
node.status({fill:"grey",shape:"dot",text:filename});
}
if (filename === "") {
node.warn('No filename specified');
node.warn(RED._("file.errors.nofilename"));
} else if (msg.hasOwnProperty('delete')) { // remove warning at some point in future
node.warn("Warning: Invalid delete. Please use specific delete option in config dialog.");
node.warn(RED._("file.errors.invaliddelete"));
//fs.unlink(filename, function (err) {
//if (err) { node.error('failed to delete file : '+err,msg); }
//if (err) { node.error(RED._("file.errors.deletefail")+' : '+err,msg); }
//});
} else if (msg.payload && (typeof msg.payload != "undefined")) {
var data = msg.payload;
@ -50,22 +50,22 @@ module.exports = function(RED) {
// using "binary" not {encoding:"binary"} to be 0.8 compatible for a while
//fs.writeFile(filename, data, {encoding:"binary"}, function (err) {
fs.writeFile(filename, data, "binary", function (err) {
if (err) { node.error('failed to write to file : '+err,msg); }
else if (RED.settings.verbose) { node.log('wrote to file: '+filename); }
if (err) { node.error(RED._("file.errors.writefail")+' : '+err,msg); }
else if (RED.settings.verbose) { node.log(RED._("file.errors.wrotefile")+': '+filename); }
});
}
else if (this.overwriteFile === "delete") {
fs.unlink(filename, function (err) {
if (err) { node.error('failed to delete file : '+err,msg); }
else if (RED.settings.verbose) { node.log("deleted file: "+filename); }
if (err) { node.error(RED._("file.errors.deletefail")+' : '+err,msg); }
else if (RED.settings.verbose) { node.log(RED._("file.errors.deletedfile")+": "+filename); }
});
}
else {
// using "binary" not {encoding:"binary"} to be 0.8 compatible for a while longer
//fs.appendFile(filename, data, {encoding:"binary"}, function (err) {
fs.appendFile(filename, data, "binary", function (err) {
if (err) { node.error('failed to append to file : '+err,msg); }
else if (RED.settings.verbose) { node.log('appended to file: '+filename); }
if (err) { node.error(RED._("file.errors.appendfail")+' : '+err,msg); }
else if (RED.settings.verbose) { node.log(RED._("file.errors.appendedfile")+': '+filename); }
});
}
}
@ -87,13 +87,13 @@ module.exports = function(RED) {
this.on("input",function(msg) {
var filename = this.filename || msg.filename || "";
if (msg.filename && n.filename && (n.filename !== msg.filename)) {
node.warn("Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props");
node.warn(RED._("file.errors.nooverride"));
}
if (!this.filename) {
node.status({fill:"grey",shape:"dot",text:filename});
}
if (filename === "") {
node.warn('No filename specified');
node.warn(RED._("file.errors.nofilename"));
} else {
msg.filename = filename;
fs.readFile(filename,options,function(err,data) {

View File

@ -489,7 +489,7 @@ describe('websocket Node', function() {
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Missing server configuration");
logEvents[0][0].msg.toString().should.startWith("websocket.errors.missing-conf");
done();
});
});
@ -505,7 +505,7 @@ describe('websocket Node', function() {
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Missing server configuration");
logEvents[0][0].msg.toString().should.startWith("websocket.errors.missing-conf");
done();
});
});

View File

@ -134,7 +134,7 @@ describe('range Node', function() {
var sinon = require('sinon');
sinon.stub(rangeNode1, 'log', function(log) {
if(log.indexOf("Not a number") > -1) {
if(log.indexOf("notnumber") > -1) {
done();
} else {
try {

View File

@ -254,9 +254,9 @@ describe('CSV node', function() {
});
logEvents.should.have.length(2);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith('This node only handles csv strings or js objects.');
logEvents[0][0].msg.toString().should.startWith('csv.errors.csv_js');
logEvents[1][0].should.have.a.property('msg');
logEvents[1][0].msg.toString().should.startWith('This node only handles csv strings or js objects.');
logEvents[1][0].msg.toString().should.startWith('csv.errors.csv_js');
done();
} catch(err) {
done(err);

View File

@ -105,13 +105,13 @@ describe('JSON node', function() {
//console.log(logEvents);
logEvents.should.have.length(4);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith('Dropped: ');
logEvents[0][0].msg.toString().should.startWith('json.errors.dropped: ');
logEvents[1][0].should.have.a.property('msg');
logEvents[1][0].msg.toString().should.startWith('Dropped: ');
logEvents[1][0].msg.toString().should.startWith('json.errors.dropped: ');
logEvents[2][0].should.have.a.property('msg');
logEvents[2][0].msg.toString().should.startWith('Dropped: ');
logEvents[2][0].msg.toString().should.startWith('json.errors.dropped: ');
logEvents[3][0].should.have.a.property('msg');
logEvents[3][0].msg.toString().should.startWith('Dropped: ');
logEvents[3][0].msg.toString().should.startWith('json.errors.dropped: ');
done();
} catch(err) {
done(err);

View File

@ -111,7 +111,7 @@ describe('XML node', function() {
return evt[0].type == "xml";
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg',"This node only handles xml strings or js objects.");
logEvents[0][0].should.have.a.property('msg',"xml.errors.xml_js");
done();
} catch(err) {
done(err);

View File

@ -133,7 +133,7 @@ describe('file Nodes', function() {
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.equal("No filename specified");
logEvents[0][0].msg.toString().should.equal("file.errors.nofilename");
done();
}
},wait);
@ -180,7 +180,7 @@ describe('file Nodes', function() {
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("failed to write");
logEvents[0][0].msg.toString().should.startWith("file.errors.writefail");
done();
}
catch(e) { done(e); }
@ -205,7 +205,7 @@ describe('file Nodes', function() {
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("failed to append");
logEvents[0][0].msg.toString().should.startWith("file.errors.appendfail");
done();
}
catch(e) { done(e); }
@ -230,7 +230,7 @@ describe('file Nodes', function() {
//console.log(logEvents);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("failed to delete");
logEvents[0][0].msg.toString().should.startWith("file.errors.deletefail");
done();
}
catch(e) { done(e); }
@ -318,7 +318,7 @@ describe('file Nodes', function() {
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Warning: msg ");
logEvents[0][0].msg.toString().should.startWith("file.errors.nooverride");
done();
});
n1.receive({payload:"",filename:"foo.txt"});
@ -335,7 +335,7 @@ describe('file Nodes', function() {
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.equal("No filename specified");
logEvents[0][0].msg.toString().should.equal("file.errors.nofilename");
done();
},wait);
n1.receive({});
@ -350,9 +350,11 @@ describe('file Nodes', function() {
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "file in";
});
console.log(logEvents[0][0].msg);
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.equal("Error: ENOENT, open 'badfile'");
//logEvents[0][0].msg.toString().should.equal("Error: ENOENT, open 'badfile'");
logEvents[0][0].msg.toString().should.startWith("Error: ENOENT, open");
done();
},wait);
n1.receive({payload:""});