Compare commits

...

31 Commits

Author SHA1 Message Date
Nick O'Leary
c6ad2c9ad2 Don't force reconnect mqtt client if message arrives
Fixes the annoying mqtt connect/disconnect cycle
2016-02-19 22:52:43 +00:00
Nick O'Leary
3b44d9972e Bump package version 2016-02-19 21:21:22 +00:00
Nick O'Leary
af736c98f2 Add -p/--port option to override listening port 2016-02-19 21:18:50 +00:00
Nick O'Leary
f1377fa217 Invert toggle button colours so state is more obvious 2016-02-18 10:28:22 +00:00
Dave Conway-Jones
2ba146b9ff Add timeout to httprequest node
and override 2 min default in settings.js.
to Close #801
2016-02-15 09:45:58 +00:00
Dave Conway-Jones
2361607aa3 file node info to same style as others 2016-02-14 13:45:14 +00:00
Nick O'Leary
86ffc80098 Tidy up spinner css 2016-02-14 11:52:33 +00:00
Dave Conway-Jones
7f6915eb59 tcp node add reply (to all) capability
if no _session present.
2016-02-12 13:17:50 +00:00
Dave Conway-Jones
d69bcad028 hardware, logic, storage nodes info updates
(and udp)
2016-02-12 13:17:21 +00:00
Dave Conway-Jones
4cb45e2712 parser node info updates 2016-02-12 13:16:28 +00:00
Dave Conway-Jones
b7a0ad703a io and analysis nodes info updates 2016-02-12 13:15:53 +00:00
Dave Conway-Jones
7610b9a975 core nodes info updates 2016-02-12 13:15:05 +00:00
Dave Conway-Jones
7d95f621df update UDP node info to be more correct. 2016-02-11 22:21:12 +00:00
Nick O'Leary
bba210e112 Allow the template node to be treated as plain text 2016-02-11 13:16:15 +00:00
Nick O'Leary
3a97e20bde Validate MQTT In topics
Fixes #792
2016-02-10 22:38:59 +00:00
Nick O'Leary
4fe7ea00b0 httpNodeAuth should not block http options requests
Fixes #793#793#793
2016-02-10 21:57:46 +00:00
Nick O'Leary
3ec8ecd4de Disable perMessageDeflate on WS servers
Workaround for this issue: https://github.com/websockets/ws/pull/632
as it has been fixed in the 1.x release that drops support for
node 0.10...
2016-02-10 21:43:37 +00:00
Dave Conway-Jones
401e65e852 Merge pull request #799 from natcl/patch-1
Fix typo in delay node: replaced ramdom to random
Thanks @natcl
2016-02-09 23:09:10 +00:00
Nathanaël Lécaudé
e7c5b691a0 More ramdom --> random 2016-02-09 18:03:27 -05:00
Nathanaël Lécaudé
9f3ea8da67 Fix typo in delay node: replaced ramdom to random 2016-02-09 17:57:50 -05:00
Dave Conway-Jones
4d84d624b1 clear trigger status icon on re-deploy 2016-02-04 22:13:08 +00:00
Nick O'Leary
633a6a0ee6 Fix inject test to use a proper type 2016-02-04 21:52:27 +00:00
Nick O'Leary
c7bcd3f438 Don't default inject payload to blank string 2016-02-04 21:43:18 +00:00
Dave Conway-Jones
d3a29a6f16 fix trigger tests 2016-02-04 21:22:52 +00:00
Dave Conway-Jones
827711ca89 Fix util jshint as we need the behaviour. 2016-02-04 21:21:57 +00:00
Dave Conway-Jones
76e98f74fa let new typed-inputs return correctly
0 now returns correct type for boolean and number when required.
2016-02-04 21:06:20 +00:00
Dave Conway-Jones
fb09f4b22d trigger node, add configurable reset
and make it do strings when it says so, and numbers if you want.
2016-02-04 21:05:15 +00:00
Dave Conway-Jones
bb06585748 another tidy up on Pi GPIO node 2016-02-03 21:10:44 +00:00
Nick O'Leary
c76ba1dcc7 Allow function properties in settings
Fixes #790
2016-01-29 11:56:16 +00:00
Nick O'Leary
a115301b04 Fix order of config dialog calls to save/creds/validate 2016-01-29 11:56:16 +00:00
Dave Conway-Jones
72917117a9 Add debounce to Pi GPIO node 2016-01-25 09:56:35 +00:00
46 changed files with 374 additions and 248 deletions

View File

@@ -66,4 +66,4 @@ For more open-source projects from IBM, head over [here](http://ibm.github.io).
## Copyright and license
Copyright 2013, 2015 IBM Corp. under [the Apache 2.0 license](LICENSE).
Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).

View File

@@ -951,20 +951,20 @@ RED.editor = (function() {
RED.nodes.add(editing_config_node);
}
updateConfigNodeSelect(configProperty,configType,editing_config_node.id);
if (configTypeDef.credentials) {
updateNodeCredentials(editing_config_node,configTypeDef.credentials,"node-config-input");
}
if (configTypeDef.oneditsave) {
configTypeDef.oneditsave.call(editing_config_node);
}
if (configTypeDef.credentials) {
updateNodeCredentials(editing_config_node,configTypeDef.credentials,"node-config-input");
}
validateNode(editing_config_node);
for (var i=0;i<editing_config_node.users.length;i++) {
var user = editing_config_node.users[i];
validateNode(user);
}
updateConfigNodeSelect(configProperty,configType,editing_config_node.id);
RED.nodes.dirty(true);
RED.view.redraw(true);
$(this).dialog("close");

View File

@@ -20,8 +20,8 @@ RED.sidebar.config = (function() {
content.className = "sidebar-node-config"
$('<div class="button-group sidebar-header">'+
'<a class="sidebar-header-button selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+
'<a class="sidebar-header-button" id="workspace-config-node-filter-unused" href="#"><span data-i18n="sidebar.config.filterUnused"></span></a> '+
'<a class="sidebar-header-button-toggle selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+
'<a class="sidebar-header-button-toggle" id="workspace-config-node-filter-unused" href="#"><span data-i18n="sidebar.config.filterUnused"></span></a> '+
'</div>'
).appendTo(content);

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2015 IBM Corp.
* Copyright 2015, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -76,3 +76,23 @@
.ui-dialog .ui-dialog-buttonpane {
padding: .3em 1em .5em 1em;
}
.ui-spinner {
border-radius: 4px;
padding: 0;
border: 1px solid $form-input-border-color;
}
.ui-spinner input {
margin: 0 17px 0 0;
padding: 6px;
border: none;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
&:focus {
outline: none;
}
}

View File

@@ -30,9 +30,9 @@
@mixin workspace-button {
@include disable-selection;
color: $workspace-button-color;
box-sizing: border-box;
display: inline-block;
color: $workspace-button-color;
background: $workspace-button-background;
border: 1px solid $secondary-border-color;
text-align: center;
@@ -67,6 +67,17 @@
border-left: none;
}
}
@mixin workspace-button-toggle {
@include workspace-button;
color: $workspace-button-color-selected;
background: $workspace-button-background-active;
&.selected:not(.disabled) {
color: $workspace-button-color;
background: $workspace-button-background;
}
}
@mixin component-footer {
border-top: 1px solid $primary-border-color;

View File

@@ -83,6 +83,12 @@
line-height: 13px;
padding: 5px 8px;
}
.sidebar-header-button-toggle {
@include workspace-button-toggle;
font-size: 13px;
line-height: 13px;
padding: 5px 8px;
}
.sidebar-header-button:not(:first-child) {
border-left: none;
}

View File

@@ -22,10 +22,11 @@
</script>
<script type="text/x-red" data-help-name="sentiment">
<p>Analyses the <b>msg.payload</b> and adds a <b>msg.sentiment</b> object that contains the resulting AFINN-111 sentiment score as <b>msg.sentiment.score</b>.</p>
<p>Analyses the <code>msg.payload</code> and adds a <code>msg.sentiment</code> object
that contains the resulting AFINN-111 sentiment score as <code>msg.sentiment.score</code>.</p>
<p>A score greater than zero is positive and less than zero is negative.</p>
<p>The score typically ranges from -5 to +5, but can go higher and lower.</p>
<p>An object of word score overrides can be supplied as <b>msg.overrides</b>.</p>
<p>An object of word score overrides can be supplied as <code>msg.overrides</code>.</p>
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_new">the Sentiment docs here</a>.</p>
</script>

View File

@@ -142,13 +142,7 @@
.inject-time-times {
width: 90px;
}
.inject-time-row > .ui-spinner {
height: 28px;
margin: 3px 0;
border-color: rgb(204, 204, 204);
}
#inject-time-time {
margin-top: 3px;
width: 75px;
}
.inject-time-count {
@@ -157,11 +151,11 @@
</style>
<script type="text/x-red" data-help-name="inject">
<p>Pressing the button on the left side of the node allows a message on a topic
to be injected into the flow. This is mainly for test purposes.</p>
to be injected into the flow.</p>
<p>The payload defaults to the current time in millisecs since 1970, but can
also be set to a String or left blank.</p>
also be set to various other javascript types.</p>
<p>The repeat function allows the payload to be sent on the required schedule.</p>
<p>The <i>Fire once at start</i> option actually waits a short interval before firing
<p>The <i>Inject once at start</i> option actually waits a short interval before firing
to give other nodes a chance to instantiate properly.</p>
<p><b>Note: </b>"Interval between times" and "at a specific time" uses cron.
This means that 20 minutes will be at the next hour, 20 minutes past and

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2013, 2015 IBM Corp.
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,7 +58,7 @@ module.exports = function(RED) {
} else if (this.payloadType == 'none') {
msg.payload = "";
} else {
msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg)||"";
msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg);
}
this.send(msg);
msg = null;

View File

@@ -39,13 +39,14 @@
</script>
<script type="text/x-red" data-help-name="debug">
<p>The Debug node can be connected to the output of any node. It can be used to display the output of any message property in the debug tab of the sidebar. The default is to display <b>msg.payload</b>.</p>
<p>Each message will also display the timestamp, <b>msg.topic</b> and the property chosen to output.</p>
<p>The Debug node can be connected to the output of any node. It can be used to display the output of any message
property in the debug tab of the sidebar. The default is to display <code>msg.payload</code>.</p>
<p>Each message will also display the timestamp, <code>msg.topic</code> and the type of property chosen to output.</p>
<p>The sidebar can be accessed under the options drop-down in the top right corner.</p>
<p>The button to the right of the node will toggle its output on and off so you can de-clutter the debug window.</p>
<p>If the payload is an object or buffer it will be stringified first for display and indicate that by saying "(Object)" or "(Buffer)".</p>
<p>Selecting any particular message will highlight (in red) the debug node that reported it. This is useful if you wire up multiple debug nodes.</p>
<p>Optionally can show the complete <b>msg</b> object.</p>
<p>Optionally can show the complete <code>msg</code> object, and send messages to the console log.</p>
<p>In addition any calls to node.warn or node.error will appear here.</p>
</script>

View File

@@ -44,7 +44,7 @@
<p>Provides 3 outputs... stdout, stderr, and return code.</p>
<p>By default uses exec() which calls the command, then gets a callback on completion, returning the complete result in one message, along with any errors.</p>
<p>Optionally can use spawn() instead, which returns the output from stdout and stderr as the command runs (usually one line at a time). On completion it then returns a return code (on the 3rd output).</p>
<p>The optional append gets added to the command after the <b>msg.payload</b> - so you can do things like pipe the result to another command.</p>
<p>The optional append gets added to the command after the <code>msg.payload</code> - so you can do things like pipe the result to another command.</p>
<p>Parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p>
<p>If stdout is binary a <i>buffer</i> is returned - otherwise returns a <i>string</i>.</p>
<p>The blue status icon will be visible while the node is active.</p>

View File

@@ -1,5 +1,5 @@
<!--
Copyright 2013, 2015 IBM Corp.
Copyright 2013, 2016 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
</div>
<div class="form-row">
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
<input id="node-input-outputs" style="width: 60px; height: 1.7em;" value="1">
<input id="node-input-outputs" style="width: 60px;" value="1">
</div>
<div class="form-tips"><span data-i18n="function.tip"></span></div>
</script>

View File

@@ -1,5 +1,5 @@
<!--
Copyright 2013, 2015 IBM Corp.
Copyright 2013, 2016 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -24,12 +24,12 @@
<input type="text" id="node-input-field" placeholder="payload" style="width:250px;">
<input type="hidden" id="node-input-fieldType">
</div>
<div class="form-row" style="margin-bottom: 0px;">
<div class="form-row" style="position: relative; margin-bottom: 0px;">
<label for="node-input-template"><i class="fa fa-file-code-o"></i> <span data-i18n="template.label.template"></span></label>
<input type="hidden" id="node-input-template" autofocus="autofocus">
<div style="float:right;margin-bottom: 3px; font-size: 0.8em;">
<div style="position: absolute; right:0;display:inline-block; text-align: right; font-size: 0.8em;">
<span data-i18n="template.label.format"></span>:
<select id="node-input-format" style=" height: 20px; font-size: 1em !important; width:110px;">
<select id="node-input-format" style="width:110px; font-size: 10px !important; height: 24px; padding:0;">
<option value="handlebars">mustache</option>
<option value="html">HTML</option>
<option value="json">JSON</option>
@@ -41,11 +41,19 @@
<div class="form-row node-text-editor-row">
<div style="height: 250px;" class="node-text-editor" id="node-input-template-editor" ></div>
</div>
<div class="form-row">
<label for="node-input-syntax"><i class="fa fa-code"></i> <span data-i18n="template.label.syntax"></span></label>
<select id="node-input-syntax" style="width:180px;">
<option value="mustache" data-i18n="template.label.mustache"></option>
<option value="plain" data-i18n="template.label.plain"></option>
</select>
</div>
</script>
<script type="text/x-red" data-help-name="template">
<p>Sets a property based on the provided template.</p>
<p>This uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_new">mustache</a></i> format.</p>
<p>By default this uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_new">mustache</a></i>
format, but this can be switched off if required.</p>
<p>For example, when a template of:
<pre>Hello {{name}}. Today is {{date}}</pre>
<p>receives a message containing:
@@ -69,6 +77,7 @@
field: {value:"payload"},
fieldType: {value:"msg"},
format: {value:"handlebars"},
syntax: {value:"mustache"},
template: {value:"This is the payload: {{payload}} !"},
},
inputs:1,
@@ -82,6 +91,10 @@
if (!this.fieldType) {
this.fieldType = 'msg';
}
if (!this.syntax) {
this.syntax = 'mustache';
$("#node-input-syntax").val(this.syntax);
}
$("#node-input-field").typedInput({
default: 'msg',
types: ['msg','flow','global'],

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2013 IBM Corp.
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,12 +23,18 @@ module.exports = function(RED) {
this.name = n.name;
this.field = n.field || "payload";
this.template = n.template;
this.syntax = n.syntax || "mustache";
this.fieldType = n.fieldType || "msg";
var node = this;
node.on("input", function(msg) {
try {
var value = mustache.render(node.template,msg);
var value;
if (node.syntax === "mustache") {
value = mustache.render(node.template,msg);
} else {
value = node.template;
}
if (node.fieldType === 'msg') {
RED.util.setMessageProperty(msg,node.field,value);
} else if (node.fieldType === 'flow') {

View File

@@ -14,14 +14,13 @@
limitations under the License.
-->
<!-- First, the content of the edit dialog is defined. -->
<script type="text/x-red" data-template-name="delay">
<div class="form-row">
<label for="node-input-pauseType"><i class="fa fa-tasks"></i> <span data-i18n="delay.action"></span></label>
<select id="node-input-pauseType" style="width:270px !important">
<option value="delay" data-i18n="delay.delaymsg"></option>
<option value="random" data-i18n="delay.ramdomdelay"></option>
<option value="random" data-i18n="delay.randomdelay"></option>
<option value="rate" data-i18n="delay.limitrate"></option>
<option value="queue" data-i18n="delay.fairqueue"></option>
</select>
@@ -74,25 +73,23 @@
</script>
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="delay">
<p>Introduces a delay into a flow or rate limits messages.</p>
<p>The default delay is 5 seconds and rate limit of 1 msg/second, but both can be configured.</p>
<p>When set to rate limit messages, they are spread across the configured time period. It can
also be set to discard any intermediate messages that arrive.</p>
<p>The "topic based fair queue" adds messages to a release queue tagged by their <b>msg.topic</b> property.
<p>The "topic based fair queue" adds messages to a release queue tagged by their <code>msg.topic</code> property.
At each "tick", derived from the rate, the next "topic" is released.
Any messages arriving on the same topic before release replace those in that position in the queue.
So each "topic" gets a turn - but the most recent value is always the one sent.</p>
</script>
<!-- Finally, the node type is registered along with all of its properties -->
<script type="text/javascript">
RED.nodes.registerType('delay',{
category: 'function', // the palette category
category: 'function',
color:"#E6E0F8",
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
defaults: {
name: {value:""},
pauseType: {value:"delay", required:true},
timeout: {value:"5", required:true, validate:RED.validators.number()},
timeoutUnits: {value:"seconds"},
@@ -103,10 +100,10 @@
randomUnits: {value: "seconds"},
drop: {value:false}
},
inputs:1, // set the number of inputs - only 0 or 1
outputs:1, // set the number of outputs - 0 to n
icon: "timer.png", // set the icon (held in public/icons)
label: function() { // sets the default label contents
inputs:1,
outputs:1,
icon: "timer.png",
label: function() {
if (this.pauseType == "delay") {
var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
if (this.timeoutUnits == "milliseconds") { units = "ms"; }
@@ -122,7 +119,7 @@
return this.name || this._("delay.label.queue")+" "+this.rate+" msg/"+units;
}
},
labelStyle: function() { // sets the class to apply to the label
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {

View File

@@ -19,6 +19,7 @@
<span data-i18n="trigger.send"></span>
<select id="node-input-op1type" style="width:200px !important">
<option value="val" data-i18n="trigger.output.string"></option>
<option value="num" data-i18n="trigger.output.number"></option>
<option value="pay" data-i18n="trigger.output.existing"></option>
<option value="nul" data-i18n="trigger.output.nothing"></option>
</select>
@@ -44,13 +45,17 @@
<span data-i18n="trigger.then-send"></span>
<select id="node-input-op2type" style="width:200px !important">
<option value="val" data-i18n="trigger.output.string"></option>
<option value="num" data-i18n="trigger.output.number"></option>
<option value="pay" data-i18n="trigger.output.existing"></option>
<option value="nul" data-i18n="trigger.output.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" data-i18n="trigger.extend"></label>
<input type="checkbox" id="node-input-extend" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-extend" data-i18n="trigger.extend"></label>
</div>
<div class="form-row">
<span data-i18n="trigger.label.reset"></span><input type="text" id="node-input-reset" style="width:240px" data-i18n="[placeholder]trigger.label.resetprompt">
</div>
<br/>
<div class="form-row">
@@ -61,18 +66,20 @@
</script>
<script type="text/x-red" data-help-name="trigger">
<p>Creates two messages on the output separated by a timeout whenever ANY <b>msg</b> arrives on the input.</p>
<p>Creates two messages on the output separated by a timeout whenever <i>any</i> <code>msg</code> arrives on the input.</p>
<p>For example, this can be used to toggle a Raspberry PI GPIO pin on and off.</p>
<p>The two output states can be specified as can the duration of the timer.
Either output can be set to a value, or templated from the inbound
<b>msg</b> using mustache syntax. <pre>The payload is {{payload}}</pre></p>
<p>If the payload is an object then setting the output to <i>existing payload</i> will pass the complete payload object through.</p>
<code>msg</code> using mustache syntax. <pre>The payload is {{payload}}</pre></p>
<p>If the <code>msg.payload</code> is an object then setting the output to
<i>existing payload</i> will pass the complete payload object through.</p>
<p>Optionally the timer can be extended by being retriggered... or not.</p>
<p>By setting the first output to <i>nothing</i>, and selecting extend timer - a watchdog timer can be created.
No output will happen as long as repeated inputs occur within the timeout period.</p>
<p>Setting the timer to 0 creates an "infinite" timeout - the first output will happen but the second
never will, and neither can the first be retriggered - so a true one shot.</p>
<p>If a <b>msg.reset</b> property is present any timeout currently in progress
<p>If a <code>msg.reset</code> property is present, or the <code>msg.payload</code>
matches the optional reset value, any timeout currently in progress
will be cleared and the second output will not happen.</p>
<p>The blue status icon will be visible while the node is active.</p>
</script>
@@ -88,7 +95,8 @@
op2type: {value:"val"},
duration: {value:"250",required:true,validate:RED.validators.number()},
extend: {value:"false"},
units: {value: "ms"},
units: {value:"ms"},
reset: {value:""},
name: {value:""}
},
inputs:1,
@@ -116,14 +124,14 @@
}
});
$("#node-input-op1type").change(function() {
if ($(this).val() == "val") {
if (($(this).val() == "val")||($(this).val() == "num")) {
$("#node-input-op1").show();
} else {
$("#node-input-op1").hide();
}
});
$("#node-input-op2type").change(function() {
if ($(this).val() == "val") {
if (($(this).val() == "val")||($(this).val() == "num")) {
$("#node-input-op2").show();
} else {
$("#node-input-op2").hide();

View File

@@ -25,6 +25,7 @@ module.exports = function(RED) {
this.op2type = n.op2type || "val";
this.extend = n.extend || "false";
this.units = n.units || "ms";
this.reset = n.reset || '';
this.duration = n.duration || 250;
if (this.duration <= 0) { this.duration = 0; }
else {
@@ -34,24 +35,24 @@ module.exports = function(RED) {
}
this.op1Templated = this.op1.indexOf("{{") != -1;
this.op2Templated = this.op2.indexOf("{{") != -1;
if (!isNaN(this.op1)) { this.op1 = Number(this.op1); }
if (!isNaN(this.op2)) { this.op2 = Number(this.op2); }
if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
if ((this.op2type === "num") && (!isNaN(this.op2))) { this.op2 = Number(this.op2); }
if (this.op1 == "true") { this.op1 = true; }
if (this.op2 == "true") { this.op2 = true; }
if (this.op1 == "false") { this.op1 = false; }
if (this.op2 == "false") { this.op2 = false; }
if (this.op1 == "null") { this.op1 = null; }
if (this.op2 == "null") { this.op2 = null; }
try { this.op1 = JSON.parse(this.op1); }
catch(e) { this.op1 = this.op1; }
try { this.op2 = JSON.parse(this.op2); }
catch(e) { this.op2 = this.op2; }
//try { this.op1 = JSON.parse(this.op1); }
//catch(e) { this.op1 = this.op1; }
//try { this.op2 = JSON.parse(this.op2); }
//catch(e) { this.op2 = this.op2; }
var node = this;
var tout = null;
var m2;
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset")) {
if (msg.hasOwnProperty("reset") || ((node.reset !== '')&&(msg.payload == node.reset)) ) {
clearTimeout(tout);
tout = null;
node.status({});
@@ -90,8 +91,8 @@ module.exports = function(RED) {
this.on("close", function() {
if (tout) {
clearTimeout(tout);
node.status({});
}
node.status({});
});
}
RED.nodes.registerType("trigger",TriggerNode);

View File

@@ -1,5 +1,5 @@
<!--
Copyright 2013, 2015 IBM Corp.
Copyright 2013, 2016 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -55,9 +55,6 @@
},
oneditprepare: function() {
var that = this;
$( "#node-input-outputs" ).spinner({
min:1
});
this.editor = RED.editor.createEditor({
id: 'node-input-info-editor',
mode: 'ace/mode/markdown',

View File

@@ -41,16 +41,18 @@
</div>
<div class="form-row">
<label for="node-input-intype"><i class="fa fa-level-up"></i> <span data-i18n="rpi-gpio.label.resistor"></span></label>
<select type="text" id="node-input-intype" style="width: 150px;">
<select type="text" id="node-input-intype" style="width:100px;">
<option value="tri" data-i18n="rpi-gpio.resistor.none"></option>
<option value="up" data-i18n="rpi-gpio.resistor.pullup"></option>
<option value="down" data-i18n="rpi-gpio.resistor.pulldown"></option>
</select>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="rpi-gpio.label.debounce"></span>
<input type="text" id="node-input-debounce" style="width:47px; text-align:right"/>&nbsp;mS
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-read" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-read" style="width: 70%;"><span data-i18n="rpi-gpio.label.readinitial"></span></label>
<label for="node-input-read" style="width:70%;"><span data-i18n="rpi-gpio.label.readinitial"></span></label>
</div>
<br/>
<div class="form-row">
@@ -62,10 +64,11 @@
</script>
<script type="text/x-red" data-help-name="rpi-gpio in">
<p>Raspberry Pi input node. Generates a <b>msg.payload</b> with either a 0 or 1 depending on the state of the input pin. Requires the gpio command to work.</p>
<p>Raspberry Pi input node. Generates a <code>msg.payload</code> with either a
0 or 1 depending on the state of the input pin.</p>
<p>You may also enable the input pullup resistor or the pulldown resistor.</p>
<p>The <b>msg.topic</b> is set to <i>pi/{the pin number}</i></p>
<p>Requires the RPi.GPIO python library version 0.5.8 (or better) in order to work.</p>
<p>The <code>msg.topic</code> is set to <i>pi/{the pin number}</i></p>
<p>Requires the RPi.GPIO python library version 0.5.10 (or better) in order to work.</p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
</script>
@@ -77,8 +80,9 @@
color:"#c6dbef",
defaults: {
name: { value:"" },
pin: { value:"",required:true,validate:RED.validators.number() },
intype: { value: "in" },
pin: { value:"tri",required:true,validate:RED.validators.number() },
intype: { value:"in" },
debounce: { value:"25" },
read: { value:false }
},
inputs:0,
@@ -104,7 +108,7 @@
var alreadyset = this._("rpi-gpio.alreadyset");
$.getJSON('rpi-gpio/'+this.id,function(data) {
$('#pitype').text(data.type);
if ((data.type === "Model B+") || (data.type === "Model A+")) {
if ((data.type !== "Model B") && (data.type !== "Model A")) {
$('#node-input-pin').append($("<option></option>").attr("value",27).text("27 - SDA0 - BCM0"));
$('#node-input-pin').append($("<option></option>").attr("value",28).text("28 - SCL0 - BCM1"));
$('#node-input-pin').append($("<option></option>").attr("value",29).text("29 - GPIO21 - BCM5"));
@@ -200,13 +204,14 @@
</script>
<script type="text/x-red" data-help-name="rpi-gpio out">
<p>Raspberry Pi output node. Expects a <b>msg.payload</b> with either a 0 or 1 (or true or false). Requires the gpio command to work.</p>
<p>Will set the selected physical pin high or low depending on the value passed in.</p>
<p>Raspberry Pi output node. Can be used in Digital, PWM or Servo modes.
<p>Digital mode expects a <code>msg.payload</code> with either a 0 or 1 (or true or false),
and will set the selected physical pin high or low depending on the value passed in.</p>
<p>The initial value of the pin at deploy time can also be set to 0 or 1.</p>
<p>When using PWM mode - expects an input value of a number 0 - 100. It can be floating point.</p>
<p>PWM mode can be used to drive a servo using input values between 10 and 20 only.
The GPIO2 pin is best for this as it uses hardware to do the PWM.</p>
<p>Requires the RPi.GPIO python library version 0.5.8 (or better) in order to work.</p>
<p>Requires the RPi.GPIO python library version 0.5.10 (or better) in order to work.</p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
</script>
@@ -249,7 +254,7 @@
if (!$("#node-input-out").val()) { $("#node-input-out").val("out"); }
$.getJSON('rpi-gpio/'+this.id,function(data) {
$('#pitype').text(data.type);
if ((data.type === "Model B+") || (data.type === "Model A+")) {
if ((data.type !== "Model B") && (data.type !== "Model A")) {
$('#node-input-pin').append($("<option></option>").attr("value",27).text("27 - SDA0 - BCM0"));
$('#node-input-pin').append($("<option></option>").attr("value",28).text("28 - SCL0 - BCM1"));
$('#node-input-pin').append($("<option></option>").attr("value",29).text("29 - GPIO21 - BCM5"));
@@ -336,12 +341,12 @@
<script type="text/x-red" data-help-name="rpi-mouse">
<p>Raspberry Pi mouse button node. Requires a USB mouse.</p>
<p>Generates a <b>msg.payload</b> with
<p>Generates a <code>msg.payload</code> with
either a 1 or 0 when the selected mouse button is pressed and released</p>
<p>Also sets <b>msg.button</b> to the code value
<p>Also sets <code>msg.button</code> to the code value
<ul><li>1 = left</li><li>2 = right</li><li>4 = middle</li></ul>
so you can work out which button or combination was pressed.</p>
<p>And sets <b>msg.topic</b> to <i>pi/mouse</i>.</p>
<p>And sets <code>msg.topic</code> to <i>pi/mouse</i>.</p>
</script>
<script type="text/javascript">
@@ -378,9 +383,9 @@
<script type="text/x-red" data-help-name="rpi-keyboard">
<p>Raspberry Pi keyboard handling node. Requires a USB keyboard.</p>
<p>Generates a <b>msg.payload</b> with a keycode, and <b>msg.action</b> set to
<p>Generates a <code>msg.payload</code> with a keycode, and <code>msg.action</code> set to
<i>up, down</i> or <i>repeat</i> whenever a key is pressed or released.</p>
<p>It also sets <b>msg.topic</b> to <i>pi/key</i>.</p>
<p>It also sets <code>msg.topic</code> to <i>pi/key</i>.</p>
</script>
<script type="text/javascript">

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2013,2015 IBM Corp.
* Copyright 2013,2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@ module.exports = function(RED) {
"use strict";
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var fs = require('fs');
var fs = require('fs');
var gpioCommand = __dirname+'/nrgpio.py';
var gpioCommand = __dirname+'/nrgpio';
try {
fs.statSync("/dev/ttyAMA0"); // unlikely if not on a Pi
@@ -53,6 +53,7 @@ module.exports = function(RED) {
this.pin = n.pin;
this.intype = n.intype;
this.read = n.read || false;
this.debounce = Number(n.debounce || 25);
if (this.read) { this.buttonState = -2; }
var node = this;
if (!pinsInUse.hasOwnProperty(this.pin)) {
@@ -65,11 +66,7 @@ module.exports = function(RED) {
}
if (node.pin !== undefined) {
if (node.intype === "tri") {
node.child = spawn(gpioCommand, ["in",node.pin]);
} else {
node.child = spawn(gpioCommand, ["in",node.pin,node.intype]);
}
node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]);
node.running = true;
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
@@ -124,7 +121,6 @@ module.exports = function(RED) {
}
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
function GPIOOutNode(n) {
RED.nodes.createNode(this,n);
this.pin = n.pin;
@@ -164,7 +160,7 @@ module.exports = function(RED) {
if (node.pin !== undefined) {
if (node.set && (node.out === "out")) {
node.child = spawn(gpioCommand, [node.out,node.pin,node.level]);
} else {
} else {
node.child = spawn(gpioCommand, [node.out,node.pin]);
}
node.running = true;
@@ -214,20 +210,6 @@ module.exports = function(RED) {
});
}
var pitype = { type:"" };
exec(gpioCommand+" rev 0", function(err,stdout,stderr) {
if (err) {
RED.log.info(RED._("rpi-gpio.errors.version"));
}
else {
if (stdout.trim() == "0") { pitype = { type:"Compute" }; }
else if (stdout.trim() == "1") { pitype = { type:"A/B v1" }; }
else if (stdout.trim() == "2") { pitype = { type:"A/B v2" }; }
else if (stdout.trim() == "3") { pitype = { type:"Model B+" }; }
else { RED.log.info(RED._("rpi-gpio.errors.sawpitype"),stdout.trim()); }
}
});
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
function PiMouseNode(n) {
@@ -235,7 +217,7 @@ module.exports = function(RED) {
this.butt = n.butt || 7;
var node = this;
node.child = spawn(gpioCommand, ["mouse",node.butt]);
node.child = spawn(gpioCommand+".py", ["mouse",node.butt]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {
@@ -281,7 +263,7 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
var node = this;
node.child = spawn(gpioCommand, ["kbd","0"]);
node.child = spawn(gpioCommand+".py", ["kbd","0"]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {
@@ -325,6 +307,22 @@ module.exports = function(RED) {
}
RED.nodes.registerType("rpi-keyboard",PiKeyboardNode);
var pitype = { type:"" };
exec(gpioCommand+" info", function(err,stdout,stderr) {
if (err) {
RED.log.info(RED._("rpi-gpio.errors.version"));
}
else {
try {
var info = JSON.parse( stdout.trim().replace(/\'/g,"\"") );
pitype.type = info["TYPE"];
}
catch(e) {
RED.log.info(RED._("rpi-gpio.errors.sawpitype"),stdout.trim());
}
}
});
RED.httpAdmin.get('/rpi-gpio/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) {
res.json(pitype);
});

View File

@@ -1,6 +1,6 @@
#!/usr/bin/python
#
# Copyright 2014 IBM Corp.
# Copyright 2014,2016 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -19,8 +19,9 @@ import struct
import sys
import os
import subprocess
from time import sleep
bounce = 20 # bounce time in mS to apply
bounce = 25;
if sys.version_info >= (3,0):
print("Sorry - currently only configured to work with python 2.x")
@@ -95,18 +96,18 @@ if len(sys.argv) > 2:
elif cmd == "in":
#print "Initialised pin "+str(pin)+" to IN"
bounce = int(sys.argv[4])
def handle_callback(chan):
sleep(bounce/1000)
print GPIO.input(chan)
if len(sys.argv) == 4:
if sys.argv[3].lower() == "up":
GPIO.setup(pin,GPIO.IN,GPIO.PUD_UP)
elif sys.argv[3].lower() == "down":
GPIO.setup(pin,GPIO.IN,GPIO.PUD_DOWN)
else:
GPIO.setup(pin,GPIO.IN)
if sys.argv[3].lower() == "up":
GPIO.setup(pin,GPIO.IN,GPIO.PUD_UP)
elif sys.argv[3].lower() == "down":
GPIO.setup(pin,GPIO.IN,GPIO.PUD_DOWN)
else:
GPIO.setup(pin,GPIO.IN)
print GPIO.input(pin)
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=bounce)
@@ -169,12 +170,6 @@ if len(sys.argv) > 2:
except:
data = 0
elif cmd == "rev":
print GPIO.RPI_REVISION
elif cmd == "ver":
print GPIO.VERSION
elif cmd == "mouse": # catch mice button events
file = open( "/dev/input/mice", "rb" )
oldbutt = 0
@@ -225,9 +220,11 @@ elif len(sys.argv) > 1:
print GPIO.RPI_REVISION
elif cmd == "ver":
print GPIO.VERSION
elif cmd == "info":
print GPIO.RPI_INFO
else:
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver {pin} {value|up|down}"
print " only ver (gpio version) and rev (board revision) accept no pin parameter."
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}"
print " only ver (gpio version) and info (board information) accept no pin parameter."
else:
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver {pin} {value|up|down}"
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}"

View File

@@ -1,5 +1,5 @@
<!--
Copyright 2013,2015 IBM Corp.
Copyright 2013, 2016 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -27,9 +27,15 @@
</script>
<script type="text/x-red" data-help-name="mqtt in">
<p>MQTT input node. Connects to a broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p>
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>.</p>
<p><b>msg.payload</b> is usually a string, but can also be a binary buffer.</p>
<p>Connects to a broker and subscribes to the specified topic.</p>
<p>Outputs a message with the properties:</p>
<ul>
<li><code>msg.topic</code></li>
<li><code>msg.payload</code></li>
<li><code>msg.qos</code></li>
<li><code>msg.retain</code></li>
</ul>
<p><code>msg.payload</code> will be a String, unless it is detected as a binary buffer.</p>
</script>
<script type="text/javascript">
@@ -37,7 +43,7 @@
category: 'input',
defaults: {
name: {value:""},
topic: {value:"",required:true},
topic: {value:"",required:true,validate: RED.validators.regex(/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/)},
broker: {type:"mqtt-broker", required:true}
},
color:"#d8bfd8",
@@ -84,9 +90,14 @@
</script>
<script type="text/x-red" data-help-name="mqtt out">
<p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> or to the topic specified in the edit window. The value in the edit window has precedence.</p>
<p>Likewise QoS and/or retain values in the edit panel will overwrite any <b>msg.qos</b> and <b>msg.retain</b> properties. If nothing is set they default to <i>0</i> and <i>false</i> respectively.</p>
<p>If <b>msg.payload</b> contains an object it will be stringified before being sent.</p>
<p>Connects to a MQTT broker and publishes messages.</p>
<p>The topic used can be configured in the node or, if left blank, can be set
by <code>msg.topic</code>.</p>
<p>Likewise the QoS and retain values can be configured in the node or, if left
blank, set by <code>msg.qos</code> and <code>msg.retain</code> respectively.
By default, messages are published at QoS 0 with the retain flag set to false.</p>
<p>If <code>msg.payload</code> contains an object it will be converted to JSON
before being sent.</p>
</script>
<script type="text/javascript">
@@ -226,7 +237,7 @@
usetls: {value: false},
verifyservercert: { value: false},
compatmode: { value: true},
keepalive: {value:15,validate:RED.validators.number()},
keepalive: {value:60,validate:RED.validators.number()},
cleansession: {value: true},
willTopic: {value:""},
willQos: {value:"0"},

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2013,2015 IBM Corp.
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -76,7 +76,7 @@ module.exports = function(RED) {
this.verifyservercert = false;
}
if (typeof this.keepalive === 'undefined'){
this.keepalive = 15;
this.keepalive = 60;
} else if (typeof this.keepalive === 'string') {
this.keepalive = Number(this.keepalive);
}
@@ -184,15 +184,14 @@ module.exports = function(RED) {
if (node.birthMessage) {
node.publish(node.birthMessage);
}
// Send any queued messages
while(node.queue.length) {
var msg = node.queue.shift();
//console.log(msg);
node.publish(msg);
}
});
node.client.on("reconnect", function() {
for (var id in node.users) {
if (node.users.hasOwnProperty(id)) {
node.users[id].status({fill:"yellow",shape:"ring",text:"common.status.connecting"});
}
}
})
// Register disconnect handlers
node.client.on('close', function () {
if (node.connected) {
@@ -272,11 +271,6 @@ module.exports = function(RED) {
retain: msg.retain || false
};
node.client.publish(msg.topic, msg.payload, options, function (err){return});
} else {
if (!node.connecting) {
node.connect();
}
node.queue.push(msg);
}
};
@@ -306,6 +300,9 @@ module.exports = function(RED) {
this.topic = n.topic;
this.broker = n.broker;
this.brokerConn = RED.nodes.getNode(this.broker);
if (!/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(this.topic)) {
return this.warn(RED._("mqtt.errors.invalid-topic"));
}
var node = this;
if (this.brokerConn) {
this.status({fill:"red",shape:"ring",text:"common.status.disconnected"});

View File

@@ -28,6 +28,8 @@ module.exports = function(RED) {
var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
var nodeMethod = n.method || "GET";
this.ret = n.ret || "txt";
if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; }
else { this.reqTimeout = 120000; }
var node = this;
var prox, noprox;
@@ -162,6 +164,13 @@ module.exports = function(RED) {
node.status({});
});
});
req.setTimeout(node.reqTimeout, function() {
node.error(RED._("common.notification.errors.no-response"),msg);
setTimeout(function() {
node.status({fill:"red",shape:"ring",text:"common.notification.errors.no-response"});
},10);
req.abort();
});
req.on('error',function(err) {
msg.payload = err.toString() + " : " + url;
msg.statusCode = err.code;

View File

@@ -38,7 +38,7 @@
<script type="text/x-red" data-help-name="websocket in">
<p>WebSocket input node.</p>
<p>By default, the data received from the WebSocket will be in <b>msg.payload</b>.
<p>By default, the data received from the WebSocket will be in <code>msg.payload</code>.
The socket can be configured to expect a properly formed JSON string, in which
case it will parse the JSON and send on the resulting object as the entire message.</p>
</script>
@@ -48,7 +48,7 @@
(function() {
function ws_oneditprepare() {
$("#websocket-client-row").hide();
$("#websocket-client-row").hide();
$("#node-input-mode").change(function(){
if( $("#node-input-mode").val() === 'client') {
$("#websocket-server-row").hide();
@@ -59,7 +59,7 @@
$("#websocket-client-row").hide();
}
});
if(this.client) {
$("#node-input-mode").val('client').change();
}
@@ -67,24 +67,24 @@
$("#node-input-mode").val('server').change();
}
}
function ws_oneditsave() {
if($("#node-input-mode").val() === 'client') {
$("#node-input-server").append('<option value="">Dummy</option>');
$("#node-input-server").val('');
$("#node-input-server").val('');
}
else {
$("#node-input-client").append('<option value="">Dummy</option>');
$("#node-input-client").val('');
$("#node-input-client").val('');
}
}
function ws_label() {
var nodeid = (this.client)?this.client:this.server;
var wsNode = RED.nodes.node(nodeid);
return this.name||(wsNode?"[ws] "+wsNode.label():"websocket");
}
function ws_validateserver() {
if($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
return true;
@@ -93,7 +93,7 @@
return RED.nodes.node(this.server) != null;
}
}
function ws_validateclient() {
if($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
return RED.nodes.node(this.client) != null;
@@ -121,7 +121,7 @@
oneditsave: ws_oneditsave,
oneditprepare: ws_oneditprepare
});
RED.nodes.registerType('websocket out',{
category: 'output',
defaults: {
@@ -141,7 +141,7 @@
oneditsave: ws_oneditsave,
oneditprepare: ws_oneditprepare
});
RED.nodes.registerType('websocket-listener',{
category: 'config',
defaults: {
@@ -152,7 +152,7 @@
outputs:0,
label: function() {
var root = RED.settings.httpNodeRoot;
if (root.slice(-1) != "/") {
if (root.slice(-1) != "/") {
root = root+"/";
}
if (this.path.charAt(0) == "/") {
@@ -164,7 +164,7 @@
},
oneditprepare: function() {
var root = RED.settings.httpNodeRoot;
if (root.slice(-1) == "/") {
if (root.slice(-1) == "/") {
root = root.slice(0,-1);
}
if (root == "") {
@@ -188,7 +188,7 @@
return this.path;
}
});
})();
</script>
@@ -217,15 +217,15 @@
<script type="text/x-red" data-help-name="websocket out">
<p>WebSocket out node.</p>
<p>By default, <b>msg.payload</b> will be sent over the WebSocket. The socket
can be configured to encode the entire message object as a JSON string and send that
<p>By default, <code>msg.payload</code> will be sent over the WebSocket. The socket
can be configured to encode the entire <code>msg</code> object as a JSON string and send that
over the WebSocket.</p>
<p>If the message arriving at this node started at a WebSocket In node, the message
will be sent back to the client that triggered the flow. Otherwise, the message
will be broadcast to all connected clients.</p>
<p>If you want to broadcast a message that started at a WebSocket In node, you
should delete the <b>msg._session</b> property within the flow</p>.
should delete the <code>msg._session</code> property within the flow.</p>
</script>
<!-- WebSocket Server configuration node -->

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2013,2015 IBM Corp.
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -82,7 +82,15 @@ module.exports = function(RED) {
RED.server.addListener('newListener',storeListener);
// Create a WebSocket Server
node.server = new ws.Server({server:RED.server,path:path});
node.server = new ws.Server({
server:RED.server,
path:path,
// Disable the deflate option due to this issue
// https://github.com/websockets/ws/pull/632
// that is fixed in the 1.x release of the ws module
// that we cannot currently pickup as it drops node 0.10 support
perMessageDeflate: false
});
// Workaround https://github.com/einaros/ws/pull/253
// Stop listening for new listener events

View File

@@ -31,11 +31,11 @@
<p>You can enter a list of comma separated directories and/or files. You will
need to put quotes "..." around any that have spaces in.</p>
<p>On Windows you must use double back-slashes \\ in any directory names.</p>
<p>The full filename of the file that actually changed is put into <b>msg.payload</b>,
while a stringified version of the watch list is returned in <b>msg.topic</b>.</p>
<p><b>msg.file</b> contains just the short filename of the file that changed.
<b>msg.type</b> has the type of thing changed, usually <i>file</i> or <i>directory</i>,
while <b>msg.size</b> holds the file size in bytes.</p>
<p>The full filename of the file that actually changed is put into <code>msg.payload</code>,
while a stringified version of the watch list is returned in <code>msg.topic</code>.</p>
<p><code>msg.file</code> contains just the short filename of the file that changed.
<code>msg.type</code> has the type of thing changed, usually <i>file</i> or <i>directory</i>,
while <code>msg.size</code> holds the file size in bytes.</p>
<p>Of course in Linux, <i>everything</i> is a file and thus can be watched...</p>
<p><b>Note: </b>The directory or file must exist in order to be watched. If the file
or directory gets deleted it may no longer be monitored even if it gets re-created.</p>

View File

@@ -56,7 +56,7 @@
</script>
<script type="text/x-red" data-help-name="tcp in">
<p>Provides a choice of tcp inputs. Can either connect to a remote tcp port,
<p>Provides a choice of TCP inputs. Can either connect to a remote TCP port,
or accept incoming connections.</p>
</script>
@@ -147,12 +147,14 @@
</script>
<script type="text/x-red" data-help-name="tcp out">
<p>Provides a choice of tcp outputs. Can either connect to a remote tcp port,
<p>Provides a choice of TCP outputs. Can either connect to a remote TCP port,
accept incoming connections, or reply to messages received from a TCP In node.</p>
<p>Only <b>msg.payload</b> is sent.</p>
<p>If <b>msg.payload</b> is a string containing a Base64 encoding of binary
<p>Only <code>msg.payload</code> is sent.</p>
<p>If <code>msg.payload</code> is a string containing a Base64 encoding of binary
data, the Base64 decoding option will cause it to be converted back to binary
before being sent.</p>
<p>If <code>msg._session</code> is not present the payload is
sent to <b>all</b> connected clients.</p>
</script>
<script type="text/javascript">
@@ -247,12 +249,12 @@
</script>
<script type="text/x-red" data-help-name="tcp request">
<p>A simple TCP request node - sends the <b>msg.payload</b> to a server tcp port and expects a response.</p>
<p>Connects, sends the "request", reads the "response". It can either count a number of
<p>A simple TCP request node - sends the <code>msg.payload</code> to a server tcp port and expects a response.</p>
<p>Connects, sends the "request", and reads the "response". It can either count a number of
returned characters into a fixed buffer, match a specified character before returning,
wait a fixed timeout from first reply and then return, or just sit and wait for data.</p>
<p>The response will be output in <b>msg.payload</b> as a buffer, so you may want to .toString() it.</p>
<p>If you leave tcp host or port blank they must be set by using the <b>msg.host</b> and <b>msg.port</b> properties.</p>
<p>The response will be output in <code>msg.payload</code> as a buffer, so you may want to .toString() it.</p>
<p>If you leave tcp host or port blank they must be set by using the <code>msg.host</code> and <code>msg.port</code> properties.</p>
</script>
<script type="text/javascript">

View File

@@ -303,6 +303,17 @@ module.exports = function(RED) {
}
}
}
else {
for (var i in connectionPool) {
if (Buffer.isBuffer(msg.payload)) {
connectionPool[i].write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
connectionPool[i].write(new Buffer(msg.payload,'base64'));
} else {
connectionPool[i].write(new Buffer(""+msg.payload));
}
}
}
});
} else {
var connectedSockets = [];

View File

@@ -29,7 +29,7 @@
</div>
<div class="form-row node-input-iface">
<label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interface">
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interfaceprompt">
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.onport"></span></label>
@@ -56,9 +56,11 @@
</script>
<script type="text/x-red" data-help-name="udp in">
<p>A udp input node, that produces a <b>msg.payload</b> containing a <i>BUFFER</i>, string, or base64 encoded string. Supports multicast.</p>
<p>It also provides <b>msg.ip</b> and <b>msg.port</b> to the ip address and port from which the message was received.</p>
<p>On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
<p>A UDP input node, that produces a <code>msg.payload</code> containing a
Buffer, string, or base64 encoded string. Supports multicast.</p>
<p>It also provides <code>msg.ip</code> and <code>msg.port</code> set to the
ip address and port from which the message was received.</p>
<p><b>Note</b>: On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
</script>
<script type="text/javascript">
@@ -163,10 +165,10 @@
</script>
<script type="text/x-red" data-help-name="udp out">
<p>This node sends <b>msg.payload</b> to the designated udp host and port. Supports multicast.</p>
<p>You may also use <b>msg.ip</b> and <b>msg.port</b> to set the destination values.<br/><b>Note</b>: the statically configured values have precedence.</p>
<p>This node sends <code>msg.payload</code> to the designated UDP host and port. Supports multicast.</p>
<p>You may also use <code>msg.ip</code> and <code>msg.port</code> to set the destination values, but the statically configured values have precedence.</p>
<p>If you select broadcast either set the address to the local broadcast ip address, or maybe try 255.255.255.255, which is the global broadcast address.</p>
<p>On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
<p><b>Note</b>: On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
</script>
<script type="text/javascript">

View File

@@ -137,8 +137,11 @@
"template": {
"label": {
"template": "Template",
"property": "Property",
"format": "Syntax Highlight"
"property": "Set property",
"format": "Syntax Highlight",
"syntax": "Format",
"mustache": "Mustache template",
"plain": "Plain text"
},
"templatevalue": "This is the payload: {{payload}} !"
},
@@ -146,7 +149,7 @@
"action": "Action",
"for": "For",
"delaymsg": "Delay message",
"ramdomdelay": "Random delay",
"randomdelay": "Random delay",
"limitrate": "Limit rate to",
"fairqueue": "Topic based fair queue",
"milisecs": "Miliseconds",
@@ -165,7 +168,7 @@
"label": {
"delay": "delay",
"limit": "limit",
"random": "ramdom",
"random": "random",
"queue": "queue"
},
"error": {
@@ -177,8 +180,9 @@
"then": "then",
"then-send": "then send",
"output": {
"string": "the string payload",
"existing": "the existing message",
"string": "the string",
"number": "the number",
"existing": "the existing msg.payload",
"nothing": "nothing"
},
"wait-reset": "wait to be reset",
@@ -189,11 +193,13 @@
"m": "Minutes",
"h": "Hours"
},
"extend": "extend delay if new message arrives",
"tip": "The node can be reset by sending a message with the <b>msg.reset</b> property set",
"extend": " extend delay if new message arrives",
"tip": "The node can also be reset by sending a message with the <b>msg.reset</b> property set to any value.",
"label": {
"trigger": "trigger",
"trigger-block": "trigger & block"
"trigger-block": "trigger & block",
"reset": "and reset if msg.payload == ",
"resetprompt": "(optional reset value)"
}
},
"comment": {
@@ -371,7 +377,8 @@
"using": "using",
"output": "Output",
"group": "Group",
"interface": "Interface",
"interface": "Local IP",
"interfaceprompt": "(optional) local ip address to bind to",
"send": "Send a",
"toport": "to port",
"address": "Address",
@@ -569,6 +576,7 @@
"readinitial": "Read initial state of pin on deploy/restart?",
"type": "Type",
"initpin": "Initialise pin state?",
"debounce": "Debounce",
"button": "Button",
"pimouse": "Pi Mouse",
"pikeyboard": "Pi Keyboard",

View File

@@ -43,7 +43,7 @@
<p>When a message arrives, the selected property is evaluated against each
of the defined rules. The message is then sent to the output of <i>all</i>
rules that pass.</p>
<p>Note: the <i>otherwise</i> rule applies as a "not any of" the rules preceding it.</p>
<p><b>Note</b>: the <i>otherwise</i> rule applies as a "not any of" the rules preceding it.</p>
</script>
<script type="text/javascript">

View File

@@ -68,12 +68,12 @@
</script>
<script type="text/x-red" data-help-name="csv">
<p>A function that parses the <b>msg.payload</b> to convert CSV to/from a javascript object.
<p>A function that parses the <code>msg.payload</code> to convert CSV to/from a javascript object.
Places the result in the payload.</p>
<p>If the input is a string it tries to parse it as CSV and creates a javascript object.</p>
<p>If the input is a javascript object it tries to build a CSV string.</p>
<p>If the input is a simple array the output is just a CSV generated from that array.</p>
<p>If the input is an array of arrays or an array of objects a multiple-line CSV is created.</p>
<p>If the input is an array of arrays, or an array of objects, a multiple-line CSV is created.</p>
<p>The columns template should contain an ordered list of column headers. For CSV input these become the property names.
For CSV output these specify the properties to extract from the object and the order for the CSV.</p>
<p>If the input is an array then the columns template does not matter, but can be used to generate a row of column titles.</p>

View File

@@ -44,7 +44,7 @@
</script>
<script type="text/x-red" data-help-name="html">
<p>Extracts elements from an html document held in <b>msg.payload</b> using a selector.</p>
<p>Extracts elements from an html document held in <code>msg.payload</code> using a selector.</p>
<p>The selector uses <a href=="https://github.com/cheeriojs/cheerio/blob/master/Readme.md" target="_new">Cheerio</a>
which uses the <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_new">CSS selector</a> syntax.</p>
<p>The result can be either a single message with a payload containing an array of the matched elements, or multiple

View File

@@ -22,7 +22,7 @@
</script>
<script type="text/x-red" data-help-name="json">
<p>A function that parses the <b>msg.payload</b> to convert a JSON string to/from a javascript object. Places the result back into the payload.</p>
<p>A function that parses the <code>msg.payload</code> to convert a JSON string to/from a javascript object. Places the result back into the payload.</p>
<p>If the input is a JSON string it tries to parse it to a javascript object.</p>
<p>If the input is a javascript object it creates a JSON string.</p>
</script>

View File

@@ -33,11 +33,11 @@
</script>
<script type="text/x-red" data-help-name="xml">
<p>A function that parses the <b>msg.payload</b> to convert xml to/from a javascript object. Places the result in the payload.</p>
<p>A function that parses the <code>msg.payload</code> to convert xml to/from a javascript object. Places the result in the payload.</p>
<p>If the input is a string it tries to parse it as XML and creates a javascript object.</p>
<p>If the input is a javascript object it tries to build an XML string.</p>
<p>You can also pass in a <b>msg.options</b> object to overide all the multitude of parameters. See
<a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a>
<p>You can also pass in a <code>msg.options</code> object to overide all the multitude of parameters. See
<a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_new">the xml2js docs</a>
for more information.</p>
<p>If set, options in the edit dialogue override those passed in on the msg.options object.</p>
</script>

View File

@@ -33,7 +33,7 @@
<script type="text/x-red" data-help-name="tail">
<p>Tails (watches for things to be added) to the configured file. (Linux/Mac ONLY)</p>
<p>This won't work on Windows filesystems, as it relies on the tail -F command.</p>
<p>This won't work on Windows filesystems, as it relies on the <b>tail -F</b> command.</p>
</script>
<script type="text/javascript">

View File

@@ -44,11 +44,12 @@
</script>
<script type="text/x-red" data-help-name="file">
<p>Writes <b>msg.payload</b> to the file specified, e.g. to create a log.</p>
<p>The filename can be configured in the node, if left blank it should be set in an incoming message on <b>msg.filename</b>.</p>
<p>Writes <code>msg.payload</code> to the file specified, for example to create a log.</p>
<p>The filename can be configured in the node. If left blank it should be
set by <code>msg.filename</code> on the incoming message.</p>
<p>A newline is added to every message. But this can be turned off if required, for example, to allow binary files to be written.</p>
<p>The default behaviour is to append to the file. This can be changed to overwrite the file each time, for example if you want to output a "static" web page or report.</p>
<p>This node can also be configured to delete a file if required. <i>Note:</i> Using msg.delete is now deprecated.</p>
<p>This node can also be configured to delete a file if required.</p>
</script>
<script type="text/x-red" data-template-name="file in">
@@ -70,8 +71,10 @@
</script>
<script type="text/x-red" data-help-name="file in">
<p>Reads the specified file and sends the content as <b>msg.payload</b>, and the filename as <b>msg.filename</b>.</p>
<p>The filename can be configured in the node, if left blank it should be set in an incoming message on <b>msg.filename</b>.</p>
<p>Reads the specified file and sends the content as <code>msg.payload</code>,
and the filename as <code>msg.filename</code>.</p>
<p>The filename can be configured in the node. If left blank it should be
set by <code>msg.filename</code> on the incoming message.</p>
</script>
<script type="text/javascript">

View File

@@ -1,6 +1,6 @@
{
"name" : "node-red",
"version" : "0.13.1",
"version" : "0.13.2",
"description" : "A visual tool for wiring the Internet of Things",
"homepage" : "http://nodered.org",
"license" : "Apache-2.0",
@@ -27,29 +27,29 @@
"dependencies": {
"basic-auth": "1.0.3",
"bcryptjs": "2.3.0",
"body-parser": "1.14.2",
"cheerio":"0.19.0",
"body-parser": "1.15.0",
"cheerio":"0.20.0",
"clone": "1.0.2",
"cors":"2.7.1",
"cron":"1.1.0",
"express": "4.13.3",
"express": "4.13.4",
"follow-redirects":"0.0.7",
"fs-extra": "0.26.4",
"fs-extra": "0.26.5",
"fs.notify":"0.0.4",
"i18next":"1.10.6",
"is-utf8":"0.2.1",
"media-typer": "0.3.0",
"mqtt": "1.6.3",
"mqtt": "1.7.2",
"mustache": "2.2.1",
"nopt": "3.0.6",
"oauth2orize":"1.2.0",
"oauth2orize":"1.2.2",
"on-headers":"1.0.1",
"passport":"0.3.2",
"passport-http-bearer":"1.0.1",
"passport-oauth2-client-password":"0.1.2",
"raw-body":"2.1.5",
"semver": "5.1.0",
"sentiment":"1.0.5",
"sentiment":"1.0.6",
"uglify-js":"2.6.1",
"when": "3.7.7",
"ws": "0.8.1",
@@ -67,22 +67,22 @@
"grunt": "0.4.5",
"grunt-chmod": "1.1.1",
"grunt-cli": "0.1.13",
"grunt-concurrent":"2.1.0",
"grunt-concurrent":"2.2.1",
"grunt-contrib-clean":"0.7.0",
"grunt-contrib-compress": "0.14.0",
"grunt-contrib-concat":"0.5.1",
"grunt-contrib-copy": "0.8.2",
"grunt-contrib-jshint": "0.12.0",
"grunt-contrib-uglify": "0.11.0",
"grunt-contrib-uglify": "0.11.1",
"grunt-contrib-watch":"0.6.1",
"grunt-jsonlint":"1.0.7",
"grunt-nodemon":"0.4.1",
"grunt-sass":"1.1.0",
"grunt-simple-mocha": "0.4.1",
"mocha": "2.3.4",
"mocha": "2.4.5",
"should": "6.0.3",
"sinon": "1.17.2",
"supertest": "1.1.0"
"sinon": "1.17.3",
"supertest": "1.2.0"
},
"engines": {
"node": ">=0.10"

13
red.js
View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Copyright 2013, 2015 IBM Corp.
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,12 +35,14 @@ var flowFile;
var knownOpts = {
"settings":[path],
"userDir":[path],
"port": Number,
"v": Boolean,
"help": Boolean
};
var shortHands = {
"s":["--settings"],
"u":["--userDir"],
"p":["--port"],
"?":["--help"]
};
nopt.invalidHandler = function(k,v,t) {
@@ -51,11 +53,13 @@ var parsedArgs = nopt(knownOpts,shortHands,process.argv,2)
if (parsedArgs.help) {
console.log("Node-RED v"+RED.version());
console.log("Usage: node-red [-v] [-?] [--settings settings.js] [--userDir DIR] [flows.json]");
console.log("Usage: node-red [-v] [-?] [--settings settings.js] [--userDir DIR]");
console.log(" [--port PORT] [flows.json]");
console.log("");
console.log("Options:");
console.log(" -s, --settings FILE use specified settings file");
console.log(" -u, --userDir DIR use specified user directory");
console.log(" -p, --port PORT port to listen on");
console.log(" -v enable verbose output");
console.log(" -?, --help show usage");
console.log("");
@@ -153,7 +157,7 @@ if (settings.httpNodeRoot !== false) {
settings.httpNodeAuth = settings.httpNodeAuth || settings.httpAuth;
}
settings.uiPort = settings.uiPort||1880;
settings.uiPort = parsedArgs.port||settings.uiPort||1880;
settings.uiHost = settings.uiHost||"0.0.0.0";
if (flowFile) {
@@ -194,6 +198,9 @@ function basicAuthMiddleware(user,pass) {
}
return function(req,res,next) {
if (req.method === 'OPTIONS') {
return next();
}
var requestUser = basicAuth(req);
if (!requestUser || requestUser.name !== user || !checkPassword(requestUser.pass)) {
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2014, 2015 IBM Corp.
* Copyright 2014, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,15 @@ function start() {
var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
var path = settings.httpAdminRoot || "/";
path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
wsServer = new ws.Server({server:server,path:path});
wsServer = new ws.Server({
server:server,
path:path,
// Disable the deflate option due to this issue
// https://github.com/websockets/ws/pull/632
// that is fixed in the 1.x release of the ws module
// that we cannot currently pickup as it drops node 0.10 support
perMessageDeflate: false
});
wsServer.on('connection',function(ws) {
log.audit({event: "comms.open"});

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2014 IBM Corp.
* Copyright 2014, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,8 @@ var persistentSettings = {
userSettings = settings;
for (var i in settings) {
/* istanbul ignore else */
if (settings.hasOwnProperty(i) && typeof settings[i] !== "function") {
if (settings.hasOwnProperty(i) && i !== 'load' && i !== 'get' && i !== 'set' && i !== 'available' && i !== 'reset') {
// Don't allow any of the core functions get replaced via settings
(function() {
var j = i;
persistentSettings.__defineGetter__(j,function() { return userSettings[j]; });

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2014, 2015 IBM Corp.
* Copyright 2014, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -165,7 +165,7 @@ function evaluateNodeProperty(value, type, node, msg) {
} else if (type === 'global' && node) {
return node.context().global.get(value);
} else if (type === 'bool') {
return /^true$/i.test(value)
return /^true$/i.test(value);
}
return value;
}

View File

@@ -40,6 +40,10 @@ module.exports = {
// defaults to no timeout
//socketTimeout: 120000,
// Timeout in milliseconds for HTTP request connections
// defaults to 120 seconds
//httpRequestTimeout: 120000,
// The maximum length, in characters, of any message sent to the debug sidebar tab
debugMaxLength: 1000,

View File

@@ -23,7 +23,7 @@ describe('inject node', function() {
before(function(done) {
helper.startServer(done);
});
afterEach(function() {
helper.unload();
});
@@ -87,13 +87,13 @@ describe('inject node', function() {
it('should inject message', function(done) {
helper.load(injectNode,
[{id:"n1", type:"inject",
payloadType:"some type", topic: "t4",
payloadType:"str", topic: "t4",payload:"hello",
wires:[["n4"]] },
{ id:"n4", type:"helper"}], function() {
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property('topic', 't4');
msg.should.have.property('payload', '');
msg.should.have.property('payload', 'hello');
helper.clearFlows().then(function() {
done();
});

View File

@@ -35,8 +35,8 @@ describe('trigger Node', function() {
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'triggerNode');
n1.should.have.property('op1', 1);
n1.should.have.property('op2', 0);
n1.should.have.property('op1', '1');
n1.should.have.property('op2', '0');
n1.should.have.property('op1type', 'val');
n1.should.have.property('op2type', 'val');
n1.should.have.property('extend', "false");
@@ -82,11 +82,11 @@ describe('trigger Node', function() {
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("payload", '0');
done();
}
});
@@ -103,11 +103,11 @@ describe('trigger Node', function() {
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("payload", '0');
}
});
setTimeout( function() {