1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

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"> <script type="text/x-red" data-template-name="sentiment">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
</script> </script>
@ -40,7 +40,7 @@
outputs:1, outputs:1,
icon: "arrow-in.png", icon: "arrow-in.png",
label: function() { label: function() {
return this.name||"sentiment"; return this.name||this._("sentiment.label.sentimentlabel");
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; 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> <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%"> <select id="node-input-payloadType" style="width:73%">
<option value="date" data-i18n="inject.timestamp"></option> <option value="date" data-i18n="inject.timestamp"></option>
<option value="string" data-i18n="inject.string">string</option> <option value="string" data-i18n="inject.string"></option>
<option value="none" data-i18n="inject.blank">blank</option> <option value="none" data-i18n="inject.blank"></option>
</select> </select>
</div> </div>
@ -122,7 +122,7 @@
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> <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>
<div class="form-tips" data-i18n="[html]inject.tip"></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"> <script type="text/x-red" data-template-name="catch">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
</script> </script>
<script type="text/x-red" data-help-name="catch"> <script type="text/x-red" data-help-name="catch">
@ -53,7 +53,7 @@
outputs:1, outputs:1,
icon: "alert.png", icon: "alert.png",
label: function() { label: function() {
return this.name||"catch"; return this.name||this._("catch.catch");
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";

View File

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

View File

@ -16,28 +16,28 @@
<script type="text/x-red" data-template-name="exec"> <script type="text/x-red" data-template-name="exec">
<div class="form-row"> <div class="form-row">
<label for="node-input-command"><i class="fa fa-file"></i> Command</label> <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" placeholder="command"> <input type="text" id="node-input-command" data-i18n="[placeholder]exec.commandph">
</div> </div>
<div class="form-row"> <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;"> <input type="checkbox" id="node-input-addpay" style="display: inline-block; width: auto; vertical-align: top;">
&nbsp;msg.payload &nbsp;msg.payload
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-append"> </label> <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>
<div class="form-row"> <div class="form-row">
<label>&nbsp;</label> <label>&nbsp;</label>
<input type="checkbox" id="node-input-useSpawn" placeholder="spawn" style="display: inline-block; width: auto; vertical-align: top;"> <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>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="exec"> <script type="text/x-red" data-help-name="exec">

View File

@ -63,7 +63,7 @@ module.exports = function(RED) {
node.error(code,msg); 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 { else {
var cl = node.cmd; var cl = node.cmd;
@ -75,7 +75,7 @@ module.exports = function(RED) {
try { try {
if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); } if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
} catch(e) { } catch(e) {
node.log("Bad STDOUT"); node.log(RED._("exec.badstdout"));
} }
var msg2 = {payload:stderr}; var msg2 = {payload:stderr};
var msg3 = null; var msg3 = null;

View File

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

View File

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

View File

@ -18,58 +18,58 @@
<script type="text/x-red" data-template-name="delay"> <script type="text/x-red" data-template-name="delay">
<div class="form-row"> <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"> <select id="node-input-pauseType" style="width:270px !important">
<option value="delay">Delay message</option> <option value="delay" data-i18n="delay.delaymsg"></option>
<option value="random">Random delay</option> <option value="random" data-i18n="delay.ramdomdelay"></option>
<option value="rate">Limit rate to</option> <option value="rate" data-i18n="delay.limitrate"></option>
<option value="queue">Topic based fair queue</option> <option value="queue" data-i18n="delay.fairqueue"></option>
</select> </select>
</div> </div>
<div id="delay-details" class="form-row"> <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"> <input type="text" id="node-input-timeout" placeholder="Time" style="direction:rtl; width:50px !important">
<select id="node-input-timeoutUnits" style="width:200px !important"> <select id="node-input-timeoutUnits" style="width:200px !important">
<option value="milliseconds">Milliseconds</option> <option value="milliseconds" data-i18n="delay.milisecs"></option>
<option value="seconds">Seconds</option> <option value="seconds" data-i18n="delay.secs"></option>
<option value="minutes">Minutes</option> <option value="minutes" data-i18n="delay.mins"></option>
<option value="hours">Hours</option> <option value="hours" data-i18n="delay.hours"></option>
<option value="days">Days</option> <option value="days" data-i18n="delay.days"></option>
</select> </select>
</div> </div>
<div id="rate-details" class="form-row"> <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"> <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"> <select id="node-input-rateUnits" style="width:140px !important">
<option value="second">Second</option> <option value="second" data-i18n="delay.sec"></option>
<option value="minute">Minute</option> <option value="minute" data-i18n="delay.min"></option>
<option value="hour">Hour</option> <option value="hour" data-i18n="delay.hour"></option>
<option value="day">Day</option> <option value="day" data-i18n="delay.day"></option>
</select> </select>
<br/> <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>
<div id="random-details" class="form-row"> <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"> <input type="text" id="node-input-randomFirst" placeholder="" style="directon:rtl; width:30px !important">
<label for="node-input-randomLast" style="width:20px"> &amp; </label> <label for="node-input-randomLast" style="width:20px"> &amp; </label>
<input type="text" id="node-input-randomLast" placeholder="" style="directon:rtl; width:30px !important"> <input type="text" id="node-input-randomLast" placeholder="" style="directon:rtl; width:30px !important">
<select id="node-input-randomUnits" style="width:140px !important"> <select id="node-input-randomUnits" style="width:140px !important">
<option value="milliseconds">Milliseconds</option> <option value="milliseconds" data-i18n="delay.milisecs"></option>
<option value="seconds">Seconds</option> <option value="seconds" data-i18n="delay.secs"></option>
<option value="minutes">Minutes</option> <option value="minutes" data-i18n="delay.mins"></option>
<option value="hours">Hours</option> <option value="hours" data-i18n="delay.hours"></option>
<option value="days">Days</option> <option value="days" data-i18n="delay.days"></option>
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
</script> </script>
@ -109,16 +109,16 @@
if (this.pauseType == "delay") { if (this.pauseType == "delay") {
var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s"; var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
if (this.timeoutUnits == "milliseconds") { units = "ms"; } 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") { } else if (this.pauseType == "rate") {
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s"; 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") { } else if (this.pauseType == "random") {
return this.name || "random"; return this.name || this._("delay.randomlabel");
} }
else { else {
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s"; 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 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}); node.status({text:node.buffer.length});
} }
if (node.buffer.length > 1000) { if (node.buffer.length > 1000) {
node.warn(this.name + " buffer exceeded 1000 messages"); node.warn(this.name + " " + RED._("delay.buffererr"));
} }
} else { } else {
node.send(msg); 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -16,48 +16,64 @@
<script type="text/x-red" data-template-name="trigger"> <script type="text/x-red" data-template-name="trigger">
<div class="form-row"> <div class="form-row">
Send <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:200px !important"> <select id="node-input-op1type" style="width:73% !important">
<option value="val">the string payload</option> <option value="val" data-i18n="trigger.below"></option>
<option value="pay">the existing message</option> <option value="pay" data-i18n="trigger.payload"></option>
<option value="nul">nothing</option> <option value="nul" data-i18n="trigger.nothing"></option>
</select> </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>
<div class="form-row"> <div class="form-row">
then <label for="node-input-duration"><i class="fa fa-clock-o"></i> <span data-i18n="trigger.wait"></span></label>
<select id="node-then-type" style="width:150px;"> <input type="text" id="node-input-duration" placeholder="250" style="direction:rtl; width:70px !important">
<option value="block">wait to be reset</option> <select id="node-input-units" style="width:140px !important">
<option value="wait">wait for</option> <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> </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>
<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"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <label for="node-input-op2type"><i class="fa fa-arrow-down"></i> <span data-i18n="trigger.output2"></span></label>
<input type="text" id="node-input-name" placeholder="name"> <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>
<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>
<script type="text/x-red" data-help-name="trigger"> <script type="text/x-red" data-help-name="trigger">
@ -95,60 +111,20 @@
icon: "trigger.png", icon: "trigger.png",
label: function() { label: function() {
if (this.duration > 0) { if (this.duration > 0) {
return this.name|| "trigger"+" "+this.duration+this.units; return this.name|| this._("trigger.triggerlabel")+" "+this.duration+this.units;
} }
else { else {
return this.name|| "trigger & block"; return this.name|| this._("trigger.triggeroncelabel");
} }
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";
}, },
oneditprepare: function() { oneditprepare: function() {
$("#node-then-type").change(function() { $( "#node-input-duration" ).spinner({
if ($(this).val() == "block") { min:1,
$(".node-type-wait").hide(); increment:25
$(".form-tips").show();
} else {
$(".node-type-wait").show();
$(".form-tips").hide();
}
}); });
$("#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> </script>

View File

@ -16,17 +16,17 @@
<script type="text/x-red" data-template-name="comment"> <script type="text/x-red" data-template-name="comment">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-comment"></i> Title</label> <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" placeholder="Comment"> <input type="text" id="node-input-name" data-i18n="[placeholder]comment.commentph">
</div> </div>
<div class="form-row" style="margin-bottom: 0px;"> <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"> <input type="hidden" id="node-input-info" autofocus="autofocus">
</div> </div>
<div class="form-row node-text-editor-row"> <div class="form-row node-text-editor-row">
<div style="height: 250px;" class="node-text-editor" id="node-input-info-editor" ></div> <div style="height: 250px;" class="node-text-editor" id="node-input-info-editor" ></div>
</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>
<script type="text/x-red" data-help-name="comment"> <script type="text/x-red" data-help-name="comment">
@ -51,8 +51,8 @@
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";
}, },
info: function() { info: function() {
var t = this.name || "Comment node"; var t = this.name || this._("comment.commentnode");
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 b = this.info || this._("comment.commentinfo");
return "### "+t+"\n"+b; return "### "+t+"\n"+b;
}, },
oneditprepare: function() { oneditprepare: function() {

View File

@ -15,10 +15,7 @@
--> -->
<script type="text/x-red" data-template-name="unknown"> <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> <div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
<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>
</script> </script>
<script type="text/x-red" data-help-name="unknown"> <script type="text/x-red" data-help-name="unknown">
@ -42,7 +39,7 @@
outputs:1, outputs:1,
icon: "", icon: "",
label: function() { label: function() {
return "("+this.name+")"||"unknown"; return "("+this.name+")"||this._("unknown.label.unknownlabel");
}, },
labelStyle: function() { labelStyle: function() {
return "node_label_unknown"; return "node_label_unknown";

View File

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

View File

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

View File

@ -16,16 +16,16 @@
<script type="text/x-red" data-template-name="mqtt in"> <script type="text/x-red" data-template-name="mqtt in">
<div class="form-row"> <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"> <input type="text" id="node-input-broker">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label> <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" placeholder="Topic"> <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topicph">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
</script> </script>
@ -58,32 +58,32 @@
<script type="text/x-red" data-template-name="mqtt out"> <script type="text/x-red" data-template-name="mqtt out">
<div class="form-row"> <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"> <input type="text" id="node-input-broker">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label> <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" placeholder="Topic"> <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topicph">
</div> </div>
<div class="form-row"> <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"> <select id="node-input-qos" style="width:125px !important">
<option value=""></option> <option value=""></option>
<option value="0">0</option> <option value="0">0</option>
<option value="1">1</option> <option value="1">1</option>
<option value="2">2</option> <option value="2">2</option>
</select> </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=""></option>
<option value="false">false</option> <option value="false" data-i18n="mqtt.false"></option>
<option value="true">true</option> <option value="true" data-i18n="mqtt.true"></option>
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="mqtt out"> <script type="text/x-red" data-help-name="mqtt out">
@ -118,21 +118,21 @@
<script type="text/x-red" data-template-name="mqtt-broker"> <script type="text/x-red" data-template-name="mqtt-broker">
<div class="form-row node-input-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%;" > <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> <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" placeholder="Port" style="width:45px"> <input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.portph" style="width:45px">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> Client ID</label> <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" placeholder="Leave blank for auto generated"> <input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.label.clientidph">
</div> </div>
<div class="form-row"> <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"> <input type="text" id="node-config-input-user">
</div> </div>
<div class="form-row"> <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"> <input type="password" id="node-config-input-password">
</div> </div>
</script> </script>

View File

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

View File

@ -16,7 +16,7 @@
<script type="text/x-red" data-template-name="http in"> <script type="text/x-red" data-template-name="http in">
<div class="form-row"> <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%;"> <select type="text" id="node-input-method" style="width:72%;">
<option value="get">GET</option> <option value="get">GET</option>
<option value="post">POST</option> <option value="post">POST</option>
@ -25,18 +25,18 @@
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-url"><i class="fa fa-globe"></i> url</label> <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" placeholder="/url"> <input type="text" id="node-input-url" data-i18n="[placeholder]httpin.label.urlph">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<div class="form-row row-swagger-doc"> <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"> <input type="text" id="node-input-swaggerDoc">
</div> </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>
<script type="text/x-red" data-help-name="http in"> <script type="text/x-red" data-help-name="http in">
@ -67,10 +67,10 @@
<script type="text/x-red" data-template-name="http response"> <script type="text/x-red" data-template-name="http response">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="http response"> <script type="text/x-red" data-help-name="http response">
@ -86,45 +86,45 @@
<script type="text/x-red" data-template-name="http request"> <script type="text/x-red" data-template-name="http request">
<div class="form-row"> <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%;"> <select type="text" id="node-input-method" style="width:72%;">
<option value="GET">GET</option> <option value="GET">GET</option>
<option value="POST">POST</option> <option value="POST">POST</option>
<option value="PUT">PUT</option> <option value="PUT">PUT</option>
<option value="DELETE">DELETE</option> <option value="DELETE">DELETE</option>
<option value="use">- set by msg.method -</option> <option value="use" data-i18n="httpin.setby"></option>
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-url"><i class="fa fa-globe"></i> URL</label> <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" placeholder="http://"> <input type="text" id="node-input-url" data-i18n="[placeholder]httpin.label.httpph">
</div> </div>
<div class="form-row"> <div class="form-row">
<label>&nbsp;</label> <label>&nbsp;</label>
<input type="checkbox" id="node-input-useAuth" style="display: inline-block; width: auto; vertical-align: top;"> <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>
<div class="form-row node-input-useAuth-row"> <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"> <input type="text" id="node-input-user">
</div> </div>
<div class="form-row node-input-useAuth-row"> <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"> <input type="password" id="node-input-password">
</div> </div>
<div class="form-row"> <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%;"> <select type="text" id="node-input-ret" style="width:72%;">
<option value="txt">a UTF-8 string</option> <option value="txt" data-i18n="httpin.utf8"></option>
<option value="bin">a binary buffer</option> <option value="bin" data-i18n="httpin.binary"></option>
<option value="obj">a parsed JSON object</option> <option value="obj" data-i18n="httpin.json"></option>
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="http request"> <script type="text/x-red" data-help-name="http request">
@ -241,7 +241,7 @@
outputs:1, outputs:1,
icon: "white-globe.png", icon: "white-globe.png",
label: function() { label: function() {
return this.name||"http request"; return this.name||this._("httpin.httpreq");
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";

View File

@ -137,7 +137,7 @@ module.exports = function(RED) {
} }
}); });
} else { } 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); RED.nodes.registerType("http in",HTTPIn);
@ -172,7 +172,7 @@ module.exports = function(RED) {
msg.res.send(statusCode,msg.payload); msg.res.send(statusCode,msg.payload);
} }
} else { } 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) { this.on("input",function(msg) {
var preRequestTimestamp = process.hrtime(); 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; var url = nodeUrl || msg.url;
if (msg.url && nodeUrl && (nodeUrl !== msg.url)) { // revert change below when warning is finally removed 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) { if (isTemplatedUrl) {
url = mustache.render(nodeUrl,msg); url = mustache.render(nodeUrl,msg);
} }
if (!url) { if (!url) {
node.error("No url specified",msg); node.error(RED._("httpin.errors.no-url"),msg);
return; return;
} }
// url must start http:// or https:// so assume http:// if not set // 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"; var method = nodeMethod.toUpperCase() || "GET";
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set 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")) { if (msg.method && n.method && (n.method === "use")) {
method = msg.method.toUpperCase(); // use the msg parameter method = msg.method.toUpperCase(); // use the msg parameter
@ -312,7 +312,7 @@ module.exports = function(RED) {
} }
else if (node.ret === "obj") { else if (node.ret === "obj") {
try { msg.payload = JSON.parse(msg.payload); } 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.send(msg);
node.status({}); node.status({});

View File

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

View File

@ -1,232 +1,234 @@
/** /**
* Copyright 2013,2015 IBM Corp. * Copyright 2013,2015 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
**/ **/
module.exports = function(RED) { module.exports = function(RED) {
"use strict"; "use strict";
var ws = require("ws"); var ws = require("ws");
var inspect = require("sys").inspect; var inspect = require("sys").inspect;
// A node red node that sets up a local websocket server // A node red node that sets up a local websocket server
function WebSocketListenerNode(n) { function WebSocketListenerNode(n) {
// Create a RED node // Create a RED node
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
var node = this; var node = this;
// Store local copies of the node configuration (as defined in the .html) // Store local copies of the node configuration (as defined in the .html)
node.path = n.path; node.path = n.path;
node.wholemsg = (n.wholemsg === "true"); node.wholemsg = (n.wholemsg === "true");
node._inputNodes = []; // collection of nodes that want to receive events node._inputNodes = []; // collection of nodes that want to receive events
node._clients = {}; node._clients = {};
// match absolute url // match absolute url
node.isServer = !/^ws{1,2}:\/\//i.test(node.path); node.isServer = !/^ws{1,2}:\/\//i.test(node.path);
node.closing = false; node.closing = false;
function startconn() { // Connect to remote endpoint function startconn() { // Connect to remote endpoint
var socket = new ws(node.path); var socket = new ws(node.path);
node.server = socket; // keep for closing node.server = socket; // keep for closing
handleConnection(socket); handleConnection(socket);
} }
function handleConnection(/*socket*/socket) { function handleConnection(/*socket*/socket) {
var id = (1+Math.random()*4294967295).toString(16); var id = (1+Math.random()*4294967295).toString(16);
if (node.isServer) { node._clients[id] = socket; node.emit('opened',Object.keys(node._clients).length); } if (node.isServer) { node._clients[id] = socket; node.emit('opened',Object.keys(node._clients).length); }
socket.on('open',function() { socket.on('open',function() {
if (!node.isServer) { node.emit('opened',''); } if (!node.isServer) { node.emit('opened',''); }
}); });
socket.on('close',function() { socket.on('close',function() {
if (node.isServer) { delete node._clients[id]; node.emit('opened',Object.keys(node._clients).length); } if (node.isServer) { delete node._clients[id]; node.emit('opened',Object.keys(node._clients).length); }
else { node.emit('closed'); } else { node.emit('closed'); }
if (!node.closing && !node.isServer) { if (!node.closing && !node.isServer) {
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ? node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
} }
}); });
socket.on('message',function(data,flags){ socket.on('message',function(data,flags){
node.handleEvent(id,socket,'message',data,flags); node.handleEvent(id,socket,'message',data,flags);
}); });
socket.on('error', function(err) { socket.on('error', function(err) {
node.emit('erro'); node.emit('erro');
if (!node.closing && !node.isServer) { if (!node.closing && !node.isServer) {
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ? node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
} }
}); });
} }
if (node.isServer) { if (node.isServer) {
var path = RED.settings.httpNodeRoot || "/"; var path = RED.settings.httpNodeRoot || "/";
path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path); path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
// Workaround https://github.com/einaros/ws/pull/253 // Workaround https://github.com/einaros/ws/pull/253
// Listen for 'newListener' events from RED.server // Listen for 'newListener' events from RED.server
node._serverListeners = {}; node._serverListeners = {};
var storeListener = function(/*String*/event,/*function*/listener){ var storeListener = function(/*String*/event,/*function*/listener){
if(event == "error" || event == "upgrade" || event == "listening"){ if(event == "error" || event == "upgrade" || event == "listening"){
node._serverListeners[event] = listener; node._serverListeners[event] = listener;
} }
} }
RED.server.addListener('newListener',storeListener); RED.server.addListener('newListener',storeListener);
// Create a WebSocket Server // Create a WebSocket Server
node.server = new ws.Server({server:RED.server,path:path}); node.server = new ws.Server({server:RED.server,path:path});
// Workaround https://github.com/einaros/ws/pull/253 // Workaround https://github.com/einaros/ws/pull/253
// Stop listening for new listener events // Stop listening for new listener events
RED.server.removeListener('newListener',storeListener); RED.server.removeListener('newListener',storeListener);
node.server.on('connection', handleConnection); node.server.on('connection', handleConnection);
} }
else { else {
node.closing = false; node.closing = false;
startconn(); // start outbound connection startconn(); // start outbound connection
} }
node.on("close", function() { node.on("close", function() {
// Workaround https://github.com/einaros/ws/pull/253 // Workaround https://github.com/einaros/ws/pull/253
// Remove listeners from RED.server // Remove listeners from RED.server
if (node.isServer) { if (node.isServer) {
var listener = null; var listener = null;
for (var event in node._serverListeners) { for (var event in node._serverListeners) {
if (node._serverListeners.hasOwnProperty(event)) { if (node._serverListeners.hasOwnProperty(event)) {
listener = node._serverListeners[event]; listener = node._serverListeners[event];
if (typeof listener === "function") { if (typeof listener === "function") {
RED.server.removeListener(event,listener); RED.server.removeListener(event,listener);
} }
} }
} }
node._serverListeners = {}; node._serverListeners = {};
node.server.close(); node.server.close();
node._inputNodes = []; node._inputNodes = [];
} }
else { else {
node.closing = true; node.closing = true;
node.server.close(); node.server.close();
if (node.tout) { clearTimeout(tout); } if (node.tout) { clearTimeout(tout); }
} }
}); });
} }
RED.nodes.registerType("websocket-listener",WebSocketListenerNode); RED.nodes.registerType("websocket-listener",WebSocketListenerNode);
RED.nodes.registerType("websocket-client",WebSocketListenerNode); RED.nodes.registerType("websocket-client",WebSocketListenerNode);
WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) { WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) {
this._inputNodes.push(handler); this._inputNodes.push(handler);
} }
WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){ WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){
var msg; var msg;
if (this.wholemsg) { if (this.wholemsg) {
try { try {
msg = JSON.parse(data); msg = JSON.parse(data);
} }
catch(err) { catch(err) {
msg = { payload:data }; msg = { payload:data };
} }
} else { } else {
msg = { msg = {
payload:data payload:data
}; };
} }
msg._session = {type:"websocket",id:id}; msg._session = {type:"websocket",id:id};
for (var i = 0; i < this._inputNodes.length; i++) { for (var i = 0; i < this._inputNodes.length; i++) {
this._inputNodes[i].send(msg); this._inputNodes[i].send(msg);
} }
} }
WebSocketListenerNode.prototype.broadcast = function(data) { WebSocketListenerNode.prototype.broadcast = function(data) {
try { try {
if(this.isServer) { if(this.isServer) {
for (var i = 0; i < this.server.clients.length; i++) { for (var i = 0; i < this.server.clients.length; i++) {
this.server.clients[i].send(data); this.server.clients[i].send(data);
} }
} }
else { else {
this.server.send(data); this.server.send(data);
} }
} }
catch(e) { // swallow any errors catch(e) { // swallow any errors
this.warn("ws:"+i+" : "+e); this.warn("ws:"+i+" : "+e);
} }
} }
WebSocketListenerNode.prototype.reply = function(id,data) { WebSocketListenerNode.prototype.reply = function(id,data) {
var session = this._clients[id]; var session = this._clients[id];
if (session) { if (session) {
try { try {
session.send(data); session.send(data);
} }
catch(e) { // swallow any errors catch(e) { // swallow any errors
} }
} }
} }
function WebSocketInNode(n) { function WebSocketInNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.server = (n.client)?n.client:n.server; this.server = (n.client)?n.client:n.server;
var node = this; var node = this;
this.serverConfig = RED.nodes.getNode(this.server); this.serverConfig = RED.nodes.getNode(this.server);
if (this.serverConfig) { if (this.serverConfig) {
this.serverConfig.registerInputNode(this); this.serverConfig.registerInputNode(this);
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); }); // TODO: nls
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); }); this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); }); this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
} else { this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
this.error("Missing server configuration"); } else {
} this.error(RED._("websocket.errors.missing-conf"));
} }
RED.nodes.registerType("websocket in",WebSocketInNode); }
RED.nodes.registerType("websocket in",WebSocketInNode);
function WebSocketOutNode(n) {
RED.nodes.createNode(this,n); function WebSocketOutNode(n) {
var node = this; RED.nodes.createNode(this,n);
this.server = (n.client)?n.client:n.server; var node = this;
this.serverConfig = RED.nodes.getNode(this.server); this.server = (n.client)?n.client:n.server;
if (!this.serverConfig) { this.serverConfig = RED.nodes.getNode(this.server);
this.error("Missing server configuration"); if (!this.serverConfig) {
} this.error(RED._("websocket.errors.missing-conf"));
else { }
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); }); else {
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); }); // TODO: nls
this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); }); 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.on("input", function(msg) { this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
var payload; }
if (this.serverConfig.wholemsg) { this.on("input", function(msg) {
delete msg._session; var payload;
payload = JSON.stringify(msg); if (this.serverConfig.wholemsg) {
} else if (msg.hasOwnProperty("payload")) { delete msg._session;
if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string. payload = JSON.stringify(msg);
payload = RED.util.ensureString(msg.payload); } else if (msg.hasOwnProperty("payload")) {
} if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string.
else { payload = RED.util.ensureString(msg.payload);
payload = msg.payload; }
} else {
} payload = msg.payload;
if (payload) { }
if (msg._session && msg._session.type == "websocket") { }
node.serverConfig.reply(msg._session.id,payload); if (payload) {
} else { if (msg._session && msg._session.type == "websocket") {
node.serverConfig.broadcast(payload,function(error){ node.serverConfig.reply(msg._session.id,payload);
if (!!error) { } else {
node.warn("An error occurred while sending:" + inspect(error)); node.serverConfig.broadcast(payload,function(error){
} if (!!error) {
}); node.warn(RED._("websocket.errors.send-error")+inspect(error));
} }
} });
}); }
} }
RED.nodes.registerType("websocket out",WebSocketOutNode); });
} }
RED.nodes.registerType("websocket out",WebSocketOutNode);
}

View File

@ -16,14 +16,14 @@
<script type="text/x-red" data-template-name="watch"> <script type="text/x-red" data-template-name="watch">
<div class="form-row node-input-filename"> <div class="form-row node-input-filename">
<label for="node-input-files"><i class="fa fa-file"></i> File(s)</label> <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" placeholder="File(s) or Directory"> <input type="text" id="node-input-files" data-i18n="[placeholder]watch.label.filesph">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="watch"> <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"> <script type="text/x-red" data-template-name="tcp in">
<div class="form-row"> <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;"> <select id="node-input-server" style="width:120px; margin-right:5px;">
<option value="server">Listen on</option> <option value="server" data-i18n="tcpin.listen"></option>
<option value="client">Connect to</option> <option value="client" data-i18n="tcpin.connect"></option>
</select> </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>
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;"> <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>
<div class="form-row"> <div class="form-row">
<label><i class="fa fa-sign-out"></i> Output</label> <label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label>
a <span data-i18n="tcpin.label.a"></span>
<select id="node-input-datamode" style="width:110px;"> <select id="node-input-datamode" style="width:110px;">
<option value="stream">stream of</option> <option value="stream" data-i18n="tcpin.stream"></option>
<option value="single">single</option> <option value="single" data-i18n="tcpin.single"></option>
</select> </select>
<select id="node-input-datatype" style="width:140px;"> <select id="node-input-datatype" style="width:140px;">
<option value="buffer">Buffer</option> <option value="buffer" data-i18n="tcpin.buffer"></option>
<option value="utf8">String</option> <option value="utf8" data-i18n="tcpin.string"></option>
<option value="base64">Base64 String</option> <option value="base64" data-i18n="tcpin.base64"></option>
</select> </select>
payload<span id="node-input-datamode-plural">s</span> <span data-i18n="tcpin.label.payload"></span>
</div> </div>
<div id="node-row-newline" class="form-row hidden" style="padding-left: 110px;"> <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>
<div class="form-row"> <div class="form-row">
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label> <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" placeholder="Topic"> <input type="text" id="node-input-topic" data-i18n="[placeholder]common.label.topicph">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
</script> </script>
@ -96,14 +96,12 @@
var datamode = $("#node-input-datamode option:selected").val(); var datamode = $("#node-input-datamode option:selected").val();
var datatype = $("#node-input-datatype option:selected").val(); var datatype = $("#node-input-datatype option:selected").val();
if (datamode == "stream") { if (datamode == "stream") {
$("#node-input-datamode-plural").show();
if (datatype == "utf8") { if (datatype == "utf8") {
$("#node-row-newline").show(); $("#node-row-newline").show();
} else { } else {
$("#node-row-newline").hide(); $("#node-row-newline").hide();
} }
} else { } else {
$("#node-input-datamode-plural").hide();
$("#node-row-newline").hide(); $("#node-row-newline").hide();
} }
}; };
@ -118,41 +116,41 @@
<script type="text/x-red" data-template-name="tcp out"> <script type="text/x-red" data-template-name="tcp out">
<div class="form-row"> <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;"> <select id="node-input-beserver" style="width:150px; margin-right:5px;">
<option value="server">Listen on</option> <option value="server" data-i18n="tcpin.listen"></option>
<option value="client">Connect to</option> <option value="client" data-i18n="tcpin.connect"></option>
<option value="reply">Reply to TCP</option> <option value="reply" data-i18n="tcpin.reply"></option>
</select> </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>
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;"> <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>
<div class="form-row hidden" id="node-input-end-row"> <div class="form-row hidden" id="node-input-end-row">
<label>&nbsp;</label> <label>&nbsp;</label>
<input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;"> <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>
<div class="form-row"> <div class="form-row">
<label>&nbsp;</label> <label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;"> <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>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<div class="form-tips hidden" id="fin-tip"> <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>
<div class="form-tips hidden" id="fin-tip2"> <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> </div>
</script> </script>
@ -223,27 +221,26 @@
<script type="text/x-red" data-template-name="tcp request"> <script type="text/x-red" data-template-name="tcp request">
<div class="form-row"> <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%"> <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>
<div class="form-row"> <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%;"> <select type="text" id="node-input-out" style="width:56%;">
<option value="time">after a fixed timeout of</option> <option value="time" data-i18n="tcpin.timeout"></option>
<option value="char">when character received is</option> <option value="char" data-i18n="tcpin.character"></option>
<option value="count">a fixed number of characters</option> <option value="count" data-i18n="tcpin.number"></option>
<option value="sit">never. Keep connection open</option> <option value="sit" data-i18n="tcpin.never"></option>
</select> </select>
<input type="text" id="node-input-splitc" style="width:50px;"> <input type="text" id="node-input-splitc" style="width:50px;">
<span id="node-units"></span> <span id="node-units"></span>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<div class="form-tips"><b>Tip:</b> Outputs a binary <b>Buffer</b>, so you may want to .toString() it.</br/> <div class="form-tips"><span data-i18n="[html]tcpin.req-tip"></span></div>
<b>Tip:</b> Leave host and port blank if you want to overide with msg.host and msg.port properties.</div>
<script> <script>
var previous = null; var previous = null;
$("#node-input-out").on('focus', function () { previous = this.value; }).change(function() { $("#node-input-out").on('focus', function () { previous = this.value; }).change(function() {

View File

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

View File

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

View File

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

View File

@ -1,37 +1,65 @@
{ {
"common": { "common": {
"label": { "label": {
"payload": "Payload", "payload": "Payload",
"topic": "Topic", "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": "inject",
"repeat": "repeat = __repeat__", "repeat": "repeat = __repeat__",
"crontab": "crontab = __crontab__", "crontab": "crontab = __crontab__",
"stopped": "stopped", "stopped": "stopped",
"failed": "Inject failed: __error__", "failed": "Inject failed: __error__",
"label": { "label": {
"repeat": "Repeat" "repeat": "Repeat"
}, },
"timestamp": "timestamp", "timestamp": "timestamp",
"string": "string", "string": "string",
"blank": "blank", "blank": "blank",
"none":"none", "none": "none",
"interval":"interval", "interval": "interval",
"interval-time":"interval between times", "interval-time": "interval between times",
"time":"at a specific time", "time": "at a specific time",
"seconds":"seconds", "seconds": "seconds",
"minutes":"minutes", "minutes": "minutes",
"hours":"hours", "hours": "hours",
"between":"between", "between": "between",
"at":"at", "at": "at",
"and":"and", "and": "and",
"every":"every", "every": "every",
"days": [ "days": [
"Monday", "Monday",
"Tuesday", "Tuesday",
@ -41,8 +69,8 @@
"Saturday", "Saturday",
"Sunday" "Sunday"
], ],
"on":"on", "on": "on",
"onstart":"Inject once at start?", "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.", "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__", "success": "Successfully injected: __label__",
"error": "<strong>Error</strong>: __message__", "error": "<strong>Error</strong>: __message__",
@ -52,5 +80,732 @@
"no-response": "no response from server", "no-response": "no response from server",
"unexpected": "unexpected error (__status__) __message__" "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"> <script type="text/x-red" data-template-name="switch">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<div class="form-row"> <div class="form-row">
If msg.<input type="text" id="node-input-property" style="width: 200px;"/> If msg.<input type="text" id="node-input-property" style="width: 200px;"/>
@ -28,12 +28,12 @@
</div> </div>
</div> </div>
<div class="form-row"> <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>
<div class="form-row"> <div class="form-row">
<select id="node-input-checkall" style="width:100%; margin-right:5px;"> <select id="node-input-checkall" style="width:100%; margin-right:5px;">
<option value="true">checking all rules</option> <option value="true" data-i18n="switch.checkall"></option>
<option value="false">stopping after first match</option> <option value="false" data-i18n="switch.stopfirst"></option>
</select> </select>
</div> </div>
</script> </script>
@ -61,7 +61,7 @@
outputs: 1, outputs: 1,
icon: "switch.png", icon: "switch.png",
label: function() { label: function() {
return this.name||"switch"; return this.name||this._("switch.label.switchlabel");
}, },
oneditprepare: function() { oneditprepare: function() {

View File

@ -16,11 +16,11 @@
<script type="text/x-red" data-template-name="change"> <script type="text/x-red" data-template-name="change">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<div class="form-row" style="margin-bottom:0;"> <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>
<div class="form-row node-input-rule-container-row" style="margin-bottom: 0px;"> <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;"> <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> </div>
<div class="form-row"> <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> </div>
</script> </script>
@ -92,6 +92,13 @@
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";
}, },
oneditprepare: function() { 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); } if (this.reg === null) { $("#node-input-reg").prop('checked', true); }
$("#node-input-action").change(); $("#node-input-action").change();
@ -104,7 +111,7 @@
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container); var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width: 100px"}).appendTo(row1); 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++) { for (var i=0;i<3;i++) {
selectField.append($("<option></option>").val(selectOptions[i].v).text(selectOptions[i].l)); selectField.append($("<option></option>").val(selectOptions[i].v).text(selectOptions[i].l));
} }
@ -118,21 +125,21 @@
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton); $('<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 propertyValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-value",type:"text"}).appendTo(row2);
var row3_1 = $('<div/>').appendTo(row3); 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 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); 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 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 row3_3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
var id = "node-input-rule-property-regex-"+Math.floor(Math.random()*10000); 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); 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() { selectField.change(function() {

View File

@ -52,7 +52,7 @@ module.exports = function(RED) {
rule.from = new RegExp(rule.from, "g"); rule.from = new RegExp(rule.from, "g");
} catch (e) { } catch (e) {
valid = false; 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"> <script type="text/x-red" data-template-name="range">
<div class="form-row"> <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;"> <select id="node-input-action" style="width:70%; margin-right:5px;">
<option value="scale">Scale msg.payload</option> <option value="scale" data-i18n="range.scale-payload"></option>
<option value="clamp">Scale and limit to the target range</option> <option value="clamp" data-i18n="range.scale-limit"></option>
<option value="roll">Scale and wrap within the target range</option> <option value="roll" data-i18n="range.scale-wrap"></option>
</select> </select>
</div> </div>
<br/> <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> <div class="form-row"><label></label>
from: <input type="text" id="node-input-minin" placeholder="e.g. 0" 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;to: <input type="text" id="node-input-maxin" placeholder="e.g. 99" 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>
<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> <div class="form-row"><label></label>
from: <input type="text" id="node-input-minout" placeholder="e.g. 0" 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;to: <input type="text" id="node-input-maxout" placeholder="e.g. 255" 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> </div>
<br/> <br/>
<div class="form-row"><label></label> <div class="form-row"><label></label>
<input type="checkbox" id="node-input-round" style="display: inline-block; width: auto; vertical-align: top;"> <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> </div>
<br/> <br/>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="range"> <script type="text/x-red" data-help-name="range">
@ -72,7 +72,7 @@
outputs: 1, outputs: 1,
icon: "range.png", icon: "range.png",
label: function() { label: function() {
return this.name || "range"; return this.name || this._("range.label.rangelabel");
}, },
labelStyle: function() { labelStyle: function() {
return this.name ? "node_label_italic" : ""; 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); } if (node.round) { msg.payload = Math.round(msg.payload); }
node.send(msg); 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. 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"> <script type="text/x-red" data-template-name="csv">
<div class="form-row"> <div class="form-row">
<label for="node-input-temp"><i class="fa fa-list"></i> Columns</label> <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" placeholder="comma-separated column names"> <input type="text" id="node-input-temp" data-i18n="[placeholder]csv.label.columnsph">
</div> </div>
<div class="form-row"> <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"> <select style="width: 150px" id="node-input-select-sep">
<option value=",">comma</option> <option value="," data-i18n="csv.comma"></option>
<option value="\t">tab</option> <option value="\t" data-i18n="csv.tab"></option>
<option value=" ">space</option> <option value=" " data-i18n="csv.space"></option>
<option value=";">semicolon</option> <option value=";" data-i18n="csv.semicolon"></option>
<option value=":">colon</option> <option value=":" data-i18n="csv.colon"></option>
<option value="#">hashtag</option> <option value="#" data-i18n="csv.hashtag"></option>
<option value="">other...</option> <option value="" data-i18n="csv.other"></option>
</select> </select>
<input style="width: 40px;" type="text" id="node-input-sep" pattern="."> <input style="width: 40px;" type="text" id="node-input-sep" pattern=".">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<hr align="middle"/> <hr align="middle"/>
<div class="form-row"> <div class="form-row">
<label style="width: 100%;"><i class="fa fa-gears"></i> CSV-to-Object options</label> <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> Input</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">first row contains column names</span> <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>
<div class="form-row"> <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;"> <select type="text" id="node-input-multi" style="width: 250px;">
<option value="one">a message per row</option> <option value="one" data-i18n="csv.row"></option>
<option value="mult">a single message [array]</option> <option value="mult" data-i18n="csv.array"></option>
</select> </select>
</div> </div>
<hr align="middle"/> <hr align="middle"/>
<div class="form-row"> <div class="form-row">
<label style="width: 100%;"><i class="fa fa-gears"></i> Object-to-CSV options</label> <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> Output</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">include column name row</span> <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>
<div class="form-row"> <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"> <select style="width: 150px" id="node-input-ret">
<option value='\n'>Linux (\n)</option> <option value='\n' data-i18n="csv.linux"></option>
<option value='\r'>Mac (\r)</option> <option value='\r' data-i18n="csv.mac"></option>
<option value='\r\n'>Windows (\r\n)</option> <option value='\r\n' data-i18n="csv.windows"></option>
</select> </select>
</div> </div>
</script> </script>

View File

@ -163,7 +163,7 @@ module.exports = function(RED) {
} }
catch(e) { node.error(e,msg); } 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. 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"> <script type="text/x-red" data-template-name="html">
<div class="form-row"> <div class="form-row">
<label for="node-input-tag"><i class="fa fa-filter"></i> Select</label> <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" placeholder="h1"> <input type="text" id="node-input-tag" style="width:73% !important" placeholder="h1">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-ret"><i class="fa fa-sign-out"></i> Output</label> <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:73% !important"> <select id="node-input-ret" style="width:76% !important">
<option value="html">the html content of the elements</option> <option value="html" data-i18n="html.htmlcontent"></option>
<option value="text">only the text content of the elements</option> <option value="text"data-i18n="html.textcontent"></option>
<!-- <option value="attr">an object of any attributes</option> --> <!-- <option value="attr">an object of any attributes</option> -->
<!-- <option value="val">return the value from a form element</option> --> <!-- <option value="val">return the value from a form element</option> -->
</select> </select>
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-as">&nbsp;</label> <label for="node-input-as">&nbsp;</label>
<select id="node-input-as" style="width:73% !important"> <select id="node-input-as" style="width:76% !important">
<option value="single">as a single message containing an array</option> <option value="single" data-i18n="html.single"></option>
<option value="multi">as multiple messages, one for each element</option> <option value="multi" data-i18n="html.multi"></option>
</select> </select>
</div> </div>
<br/> <br/>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" style="width:73% !important" data-i18n="[placeholder]common.label.nameph">
</div> </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>
<script type="text/x-red" data-help-name="html"> <script type="text/x-red" data-help-name="html">

View File

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

View File

@ -35,9 +35,9 @@ module.exports = function(RED) {
msg.payload = JSON.stringify(msg.payload); msg.payload = JSON.stringify(msg.payload);
node.send(msg); 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. 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"> <script type="text/x-red" data-template-name="xml">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name" style="width:280px !important"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph" style="width:280px !important">
</div> </div>
<div class="form-row" id="advanced"> <div class="form-row" id="advanced">
</div> </div>
<div id="advanced-options"> <div id="advanced-options">
<div class="form-row"> <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>
<div class="form-row"> <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>
<div class="form-tips">There is no simple way to convert XML attributes to JSON <div class="form-tips"><span data-i18n="xml.tip"></span></div>
so the approach taken here is to add a property, named $ by default, to the JSON structure.</div>
</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>
<script type="text/x-red" data-help-name="xml"> <script type="text/x-red" data-help-name="xml">
@ -73,6 +56,23 @@
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; 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> </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. 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"> <script type="text/x-red" data-template-name="tail">
<div class="form-row node-input-filename"> <div class="form-row node-input-filename">
<label for="node-input-filename"><i class="fa fa-file"></i> Filename</label> <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" placeholder="Filename"> <input type="text" id="node-input-filename" data-i18n="[placeholder]tail.label.filenameph">
</div> </div>
<div class="form-row"> <div class="form-row">
<label>&nbsp;</label> <label>&nbsp;</label>
<input type="checkbox" id="node-input-split" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;"> <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>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> <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" placeholder="Name"> <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.nameph">
</div> </div>
<!-- <div class="form-tips">WON'T work on Windows.</div> --> <!-- <div class="form-tips">WON'T work on Windows.</div> -->
</script> </script>

View File

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

View File

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

View File

@ -27,17 +27,17 @@ module.exports = function(RED) {
this.on("input",function(msg) { this.on("input",function(msg) {
var filename = this.filename || msg.filename || ""; var filename = this.filename || msg.filename || "";
if (msg.filename && n.filename && (n.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) { if (!this.filename) {
node.status({fill:"grey",shape:"dot",text:filename}); node.status({fill:"grey",shape:"dot",text:filename});
} }
if (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 } 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) { //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")) { } else if (msg.payload && (typeof msg.payload != "undefined")) {
var data = msg.payload; 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 // 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, {encoding:"binary"}, function (err) {
fs.writeFile(filename, data, "binary", function (err) { fs.writeFile(filename, data, "binary", function (err) {
if (err) { node.error('failed to write to file : '+err,msg); } if (err) { node.error(RED._("file.errors.writefail")+' : '+err,msg); }
else if (RED.settings.verbose) { node.log('wrote to file: '+filename); } else if (RED.settings.verbose) { node.log(RED._("file.errors.wrotefile")+': '+filename); }
}); });
} }
else if (this.overwriteFile === "delete") { else if (this.overwriteFile === "delete") {
fs.unlink(filename, function (err) { 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 (RED.settings.verbose) { node.log("deleted file: "+filename); } else if (RED.settings.verbose) { node.log(RED._("file.errors.deletedfile")+": "+filename); }
}); });
} }
else { else {
// using "binary" not {encoding:"binary"} to be 0.8 compatible for a while longer // 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, {encoding:"binary"}, function (err) {
fs.appendFile(filename, data, "binary", function (err) { fs.appendFile(filename, data, "binary", function (err) {
if (err) { node.error('failed to append to file : '+err,msg); } if (err) { node.error(RED._("file.errors.appendfail")+' : '+err,msg); }
else if (RED.settings.verbose) { node.log('appended to file: '+filename); } 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) { this.on("input",function(msg) {
var filename = this.filename || msg.filename || ""; var filename = this.filename || msg.filename || "";
if (msg.filename && n.filename && (n.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) { if (!this.filename) {
node.status({fill:"grey",shape:"dot",text:filename}); node.status({fill:"grey",shape:"dot",text:filename});
} }
if (filename === "") { if (filename === "") {
node.warn('No filename specified'); node.warn(RED._("file.errors.nofilename"));
} else { } else {
msg.filename = filename; msg.filename = filename;
fs.readFile(filename,options,function(err,data) { fs.readFile(filename,options,function(err,data) {

View File

@ -489,7 +489,7 @@ describe('websocket Node', function() {
}); });
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
}); });
}); });
@ -505,7 +505,7 @@ describe('websocket Node', function() {
//console.log(logEvents); //console.log(logEvents);
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
}); });
}); });

View File

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

View File

@ -254,9 +254,9 @@ describe('CSV node', function() {
}); });
logEvents.should.have.length(2); logEvents.should.have.length(2);
logEvents[0][0].should.have.a.property('msg'); 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].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(); done();
} catch(err) { } catch(err) {
done(err); done(err);

View File

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

View File

@ -111,7 +111,7 @@ describe('XML node', function() {
return evt[0].type == "xml"; return evt[0].type == "xml";
}); });
logEvents.should.have.length(1); 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(); done();
} catch(err) { } catch(err) {
done(err); done(err);

View File

@ -133,7 +133,7 @@ describe('file Nodes', function() {
//console.log(logEvents); //console.log(logEvents);
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
} }
},wait); },wait);
@ -180,7 +180,7 @@ describe('file Nodes', function() {
//console.log(logEvents); //console.log(logEvents);
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
} }
catch(e) { done(e); } catch(e) { done(e); }
@ -205,7 +205,7 @@ describe('file Nodes', function() {
//console.log(logEvents); //console.log(logEvents);
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
} }
catch(e) { done(e); } catch(e) { done(e); }
@ -230,7 +230,7 @@ describe('file Nodes', function() {
//console.log(logEvents); //console.log(logEvents);
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
} }
catch(e) { done(e); } catch(e) { done(e); }
@ -318,7 +318,7 @@ describe('file Nodes', function() {
}); });
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
}); });
n1.receive({payload:"",filename:"foo.txt"}); n1.receive({payload:"",filename:"foo.txt"});
@ -335,7 +335,7 @@ describe('file Nodes', function() {
}); });
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
},wait); },wait);
n1.receive({}); n1.receive({});
@ -350,9 +350,11 @@ describe('file Nodes', function() {
var logEvents = helper.log().args.filter(function(evt) { var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "file in"; return evt[0].type == "file in";
}); });
console.log(logEvents[0][0].msg);
logEvents.should.have.length(1); logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg'); 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(); done();
},wait); },wait);
n1.receive({payload:""}); n1.receive({payload:""});