Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Nathanaël Lécaudé 2017-11-22 09:48:19 -05:00
commit 6f13776646
169 changed files with 6484 additions and 1339 deletions

20
.jscsrc
View File

@ -2,15 +2,23 @@
"fileExtensions": [ ".js", "jscs" ],
"excludeFiles": [ "node_modules/**" ],
"validateIndentation": 4,
"requireCurlyBraces": true,
"disallowKeywordsOnNewLine": [],
"disallowMixedSpacesAndTabs": true,
"disallowMultipleSpaces": {"allowEOLComments": true},
"disallowKeywordsOnNewLine": [],
"requireSpaceBeforeBlockStatements": 1,
//"requireSpaceBeforeObjectValues": false,
"disallowNewlineBeforeBlockStatements": true,
"disallowTabs": true,
"disallowTrailingWhitespace": true,
"requireCurlyBraces": true,
//"requireKeywordsOnNewLine": ["else", "catch"],
//"requireSemicolons": true,
//"requireSpaceAfterBinaryOperators": true,
//"requireSpaceAfterComma": {"allExcept": ["trailing"]},
"requireSpaceAfterKeywords": ["do","for","if","else","switch","case","try","while"],
"requireSpaceBeforeBlockStatements": 1,
"requireSpaceBeforeObjectValues": false,
"requireSpacesInForStatement": true,
"requireSpacesInFunction": { "beforeOpeningCurlyBrace": true },
//"validateParameterSeparator": ", ",
//"validateQuoteMarks": false,
"requireSpaceAfterKeywords": ["do","for","if","else","switch","case","try","while"],
"maximumLineLength": 255
"maximumLineLength": 280
}

View File

@ -2,6 +2,9 @@
"asi": true, // allow missing semicolons
"curly": true, // require braces
"eqnull": true, // ignore ==null
//"eqeqeq": true, // enforce ===
"freeze": true, // don't allow override
"indent": 4, // default indent of 4
"forin": true, // require property filtering in "for in" loops
"immed": true, // require immediate functions to be wrapped in ( )
"nonbsp": true, // warn on unexpected whitespace breaking chars
@ -9,6 +12,8 @@
//"unused": true, // Check for unused functions and variables
"loopfunc": true, // allow functions to be defined in loops
//"expr": true, // allow ternery operator syntax...
"shadow": true, // allow variable shadowing (re-use of names...)
"sub": true, // don't warn that foo['bar'] should be written as foo.bar
"proto": true // allow setting of __proto__ in node < v0.12
"proto": true, // allow setting of __proto__ in node < v0.12
"esversion": 6 // allow es6
}

View File

@ -11,14 +11,12 @@ addons:
- gcc-4.8
matrix:
allow_failures:
- node_js: "5"
before_install:
- npm install -g npm@latest-2
- node_js: "7"
node_js:
- "5"
- "8"
- "7"
- "6"
- "4"
- "0.12"
- "0.10"
before_script:
- npm install -g istanbul grunt-cli
- npm install coveralls

View File

@ -40,26 +40,18 @@ the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
### Contributor License Agreement
In order for us to accept pull-requests, the contributor must first complete
a Contributor License Agreement (CLA). This clarifies the intellectual
property license granted with any contribution. It is for your protection as a
Contributor as well as the protection of IBM and its customers; it does not
change your rights to use your own Contributions for any other purpose.
All contributors need to sign the JS Foundation's Contributor License Agreement.
It is an online process and quick to do. You can read the details of the agreement
here: https://cla.js.foundation/node-red/node-red.
You can download the CLAs here:
- [individual](http://nodered.org/cla/node-red-cla-individual.pdf)
- [corporate](http://nodered.org/cla/node-red-cla-corporate.pdf)
If you are an IBMer, please contact us directly as the contribution process is
slightly different.
If you raise a pull-request without having signed the CLA, you will be prompted
to do so automatically.
### Coding standards
Please ensure you follow the coding standards used through-out the existing
code base. Some basic rules include:
- all files must have the Apache license in the header.
- indent with 4-spaces, no tabs. No arguments.
- opening brace on same line as `if`/`for`/`function` and so on, closing brace
on its own line.

View File

@ -1,7 +1,10 @@
// Configuration for Node-RED-nodes project
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
simplemocha: {
options: {
globals: ['expect'],
@ -10,27 +13,24 @@ module.exports = function(grunt) {
ui: 'bdd',
reporter: 'spec'
},
all: { src: ['test/*/*/*_spec.js'] },
all: { src: ['test/*/*/*_spec.js'] }
},
jshint: {
options: {
jshintrc:".jshintrc", // Use external file - configured as below...
jshintrc:true // Use external jshinrc file configured as below
// http://www.jshint.com/docs/options/
//"asi": true, // allow missing semicolons
//"curly": true, // require braces
//"eqnull": true, // ignore ==null
//"forin": true, // require property filtering in "for in" loops
//"immed": true, // require immediate functions to be wrapped in ( )
//"nonbsp": true, // warn on unexpected whitespace breaking chars
////"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
//"loopfunc": true, // allow functions to be defined in loops
//"sub": true, // don't warn that foo['bar'] should be written as foo.bar
////"unused": true, // Check for unused functions
////"forin":false, // turn off check for "for (x in y...)"
//"reporter": require('jshint-stylish')
//"asi": true, // allow missing semicolons
//"curly": true, // require braces
//"eqnull": true, // ignore ==null
//"forin": true, // require property filtering in "for in" loops
//"immed": true, // require immediate functions to be wrapped in ( )
//"nonbsp": true, // warn on unexpected whitespace breaking chars
////"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
//"loopfunc": true, // allow functions to be defined in loops
//"sub": true // don't warn that foo['bar'] should be written as foo.bar
},
all: {
src: ['*/*.js','*/*/*.js'],
src: ['*/*/*.js'],
filter: function(filepath) { // on some developer machines the test coverage HTML report utilities cause further failures
if ((filepath.indexOf("coverage/") !== -1) || (filepath.indexOf("node_modules") !== -1)) {
console.log( "\033[30m filtered out \033[32m:\033[37m " + filepath + "\033[0m");
@ -42,7 +42,7 @@ module.exports = function(grunt) {
},
},
inlinelint: {
html: ['*/*/*.html'],
html: ['*/*/*.html', '!node_modules/*/*.html', '!*/node_modules/*.html'],
options: {
jshintrc: ".jshintrc"
//,reporter: require('jshint-stylish')
@ -53,6 +53,7 @@ module.exports = function(grunt) {
options: {
config: ".jscsrc",
reporter: "inline"
//,fix: true
}
}
});
@ -62,6 +63,6 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-lint-inline');
grunt.loadNpmTasks('grunt-jscs');
grunt.registerTask('default', ['jshint:all', 'inlinelint', 'simplemocha:all']);
grunt.registerTask('default', ['jshint:all', 'inlinelint:html', 'simplemocha:all']);
grunt.registerTask('style', ['jscs']);
};

View File

@ -71,6 +71,8 @@ Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
**node-red-node-intel-galileo** - *[mraa-spio](hardware/intel)* - A collection of analogue & digital input & output nodes for the Intel Galileo and Edison.
**node-red-node-pi-gpiod** - *[pigpiod](hardware/pigpiod)* - An alternative to the default PI GPIO nodes that allows remote access - so a host machine can access a remote Pi (via network) and is better for driving multiple servos.
**node-red-node-pi-mcp3008** - *[pimcp3008](hardware/mcp3008)* - Allows A Raspberry Pi to node to read from an MCP3008 Analogue to Digital Converter chip via the SPI bus.
**node-red-node-pi-neopixel** - *[neopixel](hardware/neopixel)* - Allows A Raspberry Pi to drive a strip of NeoPixels directly.
@ -109,7 +111,7 @@ Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
**node-red-node-ping** - *[88-ping](io/ping)* - Pings a machine and returns the trip time in mS. Returns false if no response received within 3 seconds, or if the host is unresolveable. Default ping is every 20 seconds but can be configured.
**node-red-node-mdns** - *[mdns](io/mdns)* - discovers other Avahi/Bonjour services on the network.
**node-red-node-discovery** - *[mdns](io/mdns)* - discovers other Avahi/Bonjour services on the network.
**node-red-node-mqlight** - *[mqlight](io/mqlight)* - Adds nodes to send and receive using MQlight.

View File

@ -79,13 +79,18 @@
this.editor.focus();
},
oneditsave: function() {
$("#node-input-template").val(this.editor.getValue())
$("#node-input-template").val(this.editor.getValue());
this.editor.destroy();
delete this.editor;
},
oneditcancel: function() {
this.editor.destroy();
delete this.editor;
},
oneditresize: function(size) {
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
var height = $("#dialog-form").height();
for (var i=0;i<rows.size();i++) {
for (var i=0; i<rows.size(); i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-text-editor-row");

View File

@ -20,14 +20,17 @@ module.exports = function(RED) {
}
if (node.fieldType === 'msg') {
RED.util.setMessageProperty(msg,node.field,value);
} else if (node.fieldType === 'flow') {
}
else if (node.fieldType === 'flow') {
node.context().flow.set(node.field,value);
} else if (node.fieldType === 'global') {
}
else if (node.fieldType === 'global') {
node.context().global.set(node.field,value);
}
node.send(msg);
} catch(err) {
node.error(err.message);
}
catch(e) {
node.error(e.message);
}
});
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-data-generator",
"version" : "0.0.3",
"version" : "0.0.4",
"description" : "A Node-RED node to create a string of dummy data values from a template. Useful for test-cases.",
"dependencies" : {
"dummy-json": "1.0.*"

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-random",
"version" : "0.0.7",
"version" : "0.0.8",
"description" : "A Node-RED node that when triggered generates a random number between two values.",
"dependencies" : {
},

View File

@ -35,7 +35,7 @@
defaults: {
name: {value:""},
low: {value:"1"},
high: {value:"6"},
high: {value:"10"},
inte: {value:"true"}
},
inputs:1,

View File

@ -10,7 +10,8 @@ module.exports = function(RED) {
this.on("input", function(msg) {
if (node.inte == "true" || node.inte === true) {
msg.payload = Math.round(Number(Math.random()) * (node.high - node.low + 1) + node.low - 0.5);
} else {
}
else {
msg.payload = Number(Math.random()) * (node.high - node.low) + node.low;
}
node.send(msg);

View File

@ -2,6 +2,7 @@
"rbe": {
"label": {
"func": "Mode",
"init": "Send initial value",
"start": "Start value",
"name": "Name"
},
@ -11,8 +12,11 @@
},
"opts": {
"rbe": "block unless value changes",
"deadband": "block unless value changes by more than",
"narrowband": "block if value changes by more than",
"rbei": "block unless value changes (ignore initial value)",
"deadband": "block unless value change is greater than",
"deadbandEq": "block unless value change is greater or equal to",
"narrowband": "block if value change is greater than",
"narrowbandEq": "block if value change is greater or equal to",
"in": "compared to last input value",
"out": "compared to last valid output value"
},

View File

@ -0,0 +1,27 @@
{
"rbe": {
"label": {
"func": "動作",
"init": "初期値を送付",
"start": "初期値",
"name": "名前"
},
"placeholder": {
"bandgap": "例:10、5%",
"start": "最初に受け取った値を用いる場合は空欄"
},
"opts": {
"rbe": "値が変化した時のみメッセージを中継",
"rbei": "値が変化した時のみメッセージを中継(初期値を無視)",
"deadband": "値が比較値を超える時のみメッセージを中継",
"deadbandEq": "値が比較値以上の時のみメッセージを中継",
"narrowband": "初期値、値が比較値を超える時のみメッセージを中継",
"narrowbandEq": "初期値、値が比較値以上の時のみメッセージを中継",
"in": "を最後の入力値と比較",
"out": "を最後の出力値と比較"
},
"warn": {
"nonumber": "ペイロードに数値が含まれていません"
}
}
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-rbe",
"version" : "0.1.6",
"version" : "0.1.13",
"description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capability.",
"dependencies" : {
},

View File

@ -4,14 +4,17 @@
<label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="rbe.label.func"></span></label>
<select type="text" id="node-input-func" style="width:74%;">
<option value="rbe" data-i18n="rbe.opts.rbe"></option>
<option value="rbei" data-i18n="rbe.opts.rbei"></option>
<option value="deadbandEq" data-i18n="rbe.opts.deadbandEq"></option>
<option value="deadband" data-i18n="rbe.opts.deadband"></option>
<option value="narrowbandEq" data-i18n="rbe.opts.narrowbandEq"></option>
<option value="narrowband" data-i18n="rbe.opts.narrowband"></option>
</select>
</div>
<div class="form-row" id="node-bandgap">
<label for="node-input-gap">&nbsp;</label>
<input type="text" id="node-input-gap" data-i18n="[placeholder]rbe.placeholder.bandgap" style="width:70px;">
<select type="text" id="node-input-inout" style="width:55%;">
<input type="text" id="node-input-gap" data-i18n="[placeholder]rbe.placeholder.bandgap" style="width:95px;">
<select type="text" id="node-input-inout" style="width:54%;">
<option value="out" data-i18n="rbe.opts.out"></option>
<option value="in" data-i18n="rbe.opts.in"></option>
</select>
@ -27,21 +30,38 @@
</script>
<script type="text/x-red" data-help-name="rbe">
<p>Report by Exception node - only passes on data if it has changed.</p>
<p>The node can either block until the <code>msg.payload</code> is
different to the previous one - <b>rbe</b> mode. This works on numbers, strings, and simple objects.</p>
<p>Or it can block until the value changes by a specified amount - <b>deadband</b> mode.</p>
<p>In deadband mode the incoming payload must contain a parseable <i>number</i> and is
output only if greater than + or - the <i>band gap</i> away from a previous value.</p>
<p>Report by Exception node - only passes on data if the payload has changed.</p>
<p>It can also block until the value changes by a specified amount - deadband modes.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">number | string | (object)</span>
</dt>
<dd>RBE mode will accept numbers, strings, and simple objects. Other modes must provide a parseable number.</dd>
<dt class="optional">topic <span class="property-type">string</span>
</dt>
<dd>if specified the function will work on a per topic basis.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">as per input</span>
</dt>
<dd>If triggered the output will be the same as the input.</dd>
</dl>
<h3>Details</h3>
<p>In RBE mode this node will block until the <code>msg.payload</code> is
different to the previous one. </p>
<p>In Deadband modes the incoming payload must contain a parseable number and is
output only if greater than + or - the band gap away from a previous value.</p>
<p>Deadband also supports % - only sends if the input differs by more than x% of the original value.</p>
<p>It can also ignore outlier values - <b>narrowband</b> mode.</p>
<p>In narrowband mode the incoming payload is blocked if it is more than + or - the band gap
<p>In Narrowband mode the incoming payload is blocked if it is more than + or - the band gap
away from the previous value. Useful for ignoring outliers from a faulty sensor for example.</p>
<p>Both Deadband and Narrowband allow comparison against either the <i>previous valid output value</i>, thus
ignoring any values out of range; or the <i>previous input value</i>, which resets the set point, thus allowing
<p>Both Deadband and Narrowband allow comparison against either the previous valid output value, thus
ignoring any values out of range; or the previous input value, which resets the set point, thus allowing
gradual drift (deadband), or a step change (narrowband).</p>
<p><b>Note:</b> This works on a per <code>msg.topic</code> basis. This means that a single rbe node can
handle multiple topics at the same time.</p>
handle multiple different topics at the same time.</p>
</script>
<script type="text/javascript">
@ -59,7 +79,8 @@
outputs:1,
icon: "rbe.png",
label: function() {
return this.name||this.func||"rbe";
var ll = (this.func||"").replace("Eq","").replace("rbei","rbe")||"rbe";
return this.name||ll||"rbe";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
@ -70,7 +91,7 @@
$("#node-input-inout").val("out");
}
$("#node-input-func").on("change",function() {
if ($("#node-input-func").val() === "rbe") {
if (($("#node-input-func").val() === "rbe")||($("#node-input-func").val() === "rbei")) {
$("#node-bandgap").hide();
} else {
$("#node-bandgap").show();

View File

@ -13,24 +13,26 @@ module.exports = function(RED) {
this.gap = parseFloat(this.gap);
}
this.g = this.gap;
var node = this;
node.previous = {};
this.on("input",function(msg) {
if (msg.hasOwnProperty("payload")) {
var t = msg.topic || "_no_topic";
if (this.func === "rbe") {
if ((this.func === "rbe") || (this.func === "rbei")) {
var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false;
if (typeof(msg.payload) === "object") {
if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; }
if (!RED.util.compareObjects(msg.payload, node.previous[t])) {
node.previous[t] = msg.payload;
node.send(msg);
node.previous[t] = RED.util.cloneMessage(msg.payload);
if (doSend) { node.send(msg); }
}
}
else {
if (msg.payload !== node.previous[t]) {
node.previous[t] = msg.payload;
node.send(msg);
node.previous[t] = RED.util.cloneMessage(msg.payload);
if (doSend) { node.send(msg); }
}
}
}
@ -42,15 +44,23 @@ module.exports = function(RED) {
else { node.previous[t] = node.start; }
}
if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
if (!node.previous.hasOwnProperty(t)) { node.previous[t] = n - node.gap; }
if (Math.abs(n - node.previous[t]) >= node.gap) {
if (this.func === "deadband") {
else { node.gap = Number(node.gap); }
if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; }
if (node.previous[t] === undefined) { node.previous[t] = n - node.gap; }
if (Math.abs(n - node.previous[t]) === node.gap) {
if (this.func === "deadbandEq") {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}
}
else {
if (this.func === "narrowband") {
else if (Math.abs(n - node.previous[t]) > node.gap) {
if (this.func === "deadband" || this.func === "deadbandEq") {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}
}
else if (Math.abs(n - node.previous[t]) < node.gap) {
if ((this.func === "narrowband")||(this.func === "narrowbandEq")) {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}

View File

@ -21,6 +21,13 @@
<label for="node-input-round">(optionally)</label>
round to <input type="text" id="node-input-round" placeholder="ignore" style="width:50px;"/> decimal places
</div>
<div class="form-row">
<label for="node-input-mult">Treat</label>
<select id="node-input-mult" style="width:60%; margin-right:5px;">
<option value="single">All msg as one stream.</option>
<option value="multi">Different msg.topic as individual streams.</option>
</select>
</div>
<br/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
@ -31,8 +38,12 @@
<script type="text/x-red" data-help-name="smooth">
<p>A simple node to provide various functions across several previous values, including max, min, mean, high and low pass filters.</p>
<p>Messages arriving with different <code>msg.topic</code> can be treated as separate streams if so configured.</p>
<p>Max, Min and Mean work over a specified number of previous values.</p>
<p>The High and Low pass filters use a smoothing factor. The higher the number the more the smoothing. E.g. a value of 10 is similar to an &alpha; of 0.1. It is analagous to an RC time constant - but there is no time component to this as the time is based on events arriving.</p>
<p>The High and Low pass filters use a smoothing factor. The higher the number the more the smoothing. E.g. a value of 10 is
similar to an &alpha; of 0.1. It is analagous to an RC time constant - but there is no time component to this as the
time is based on events arriving.</p>
<p>If <code>msg.reset</code> is received (with any value), all the counters and intermediate values are reset to an initial state.</p>
<p><b>Note:</b> This only operates on <b>numbers</b>. Anything else will try to be made into a number and rejected if that fails.</p>
</script>
@ -44,7 +55,8 @@
name: {value:""},
action: {value:"mean"},
count: {value:"10",required:true,validate:RED.validators.number()},
round: {value:""}
round: {value:""},
mult: {value:"single"}
},
inputs: 1,
outputs: 1,

View File

@ -8,41 +8,50 @@ module.exports = function(RED) {
this.round = n.round || false;
if (this.round == "true") { this.round = 0; }
this.count = Number(n.count);
this.mult = n.mult || "single";
var node = this;
var a = [];
var tot = 0;
var tot2 = 0;
var pop = 0;
var old = null;
var v = {};
this.on('input', function (msg) {
var top = msg.topic || "_my_default_topic";
if (this.mult === "single") { top = "a"; }
if ((v.hasOwnProperty(top) !== true) || msg.hasOwnProperty("reset")) {
v[top] = {};
v[top].a = [];
v[top].tot = 0;
v[top].tot2 = 0;
v[top].pop = 0;
v[top].old = null;
v[top].count = this.count;
}
if (msg.hasOwnProperty("payload")) {
var n = Number(msg.payload);
if (!isNaN(n)) {
if ((node.action === "low") || (node.action === "high")) {
if (old == null) { old = n; }
old = old + (n - old) / node.count;
if (node.action === "low") { msg.payload = old; }
else { msg.payload = n - old; }
if (v[top].old == null) { v[top].old = n; }
v[top].old = v[top].old + (n - v[top].old) / v[top].count;
if (node.action === "low") { msg.payload = v[top].old; }
else { msg.payload = n - v[top].old; }
}
else {
a.push(n);
if (a.length > node.count) { pop = a.shift(); }
v[top].a.push(n);
if (v[top].a.length > v[top].count) { v[top].pop = v[top].a.shift(); }
if (node.action === "max") {
msg.payload = Math.max.apply(Math, a);
msg.payload = Math.max.apply(Math, v[top].a);
}
if (node.action === "min") {
msg.payload = Math.min.apply(Math, a);
msg.payload = Math.min.apply(Math, v[top].a);
}
if (node.action === "mean") {
tot = tot + n - pop;
msg.payload = tot / a.length;
v[top].tot = v[top].tot + n - v[top].pop;
msg.payload = v[top].tot / v[top].a.length;
}
if (node.action === "sd") {
tot = tot + n - pop;
tot2 = tot2 + (n*n) - (pop * pop);
if (a.length > 1) {
msg.payload = Math.sqrt((a.length * tot2 - tot * tot)/(a.length * (a.length - 1)));
v[top].tot = v[top].tot + n - v[top].pop;
v[top].tot2 = v[top].tot2 + (n*n) - (v[top].pop * v[top].pop);
if (v[top].a.length > 1) {
msg.payload = Math.sqrt((v[top].a.length * v[top].tot2 - v[top].tot * v[top].tot)/(v[top].a.length * (v[top].a.length - 1)));
}
else { msg.payload = 0; }
}

View File

@ -33,5 +33,7 @@ the more the smoothing. E.g. a value of 10 is similar to an &alpha; of 0.1.
It is analogous to an RC time constant - but there is no time component to
this as the code is based on events arriving.
If `msg.reset` is received (with any value), all the counters and intermediate values are reset to an initial state.
**Note:** This node only operates on **numbers**. Anything else will try to be
made into a number and rejected if that fails.

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-smooth",
"version" : "0.0.9",
"version" : "0.0.11",
"description" : "A Node-RED node that provides several simple smoothing algorithms for incoming data values.",
"dependencies" : {
},

View File

@ -3,7 +3,7 @@ module.exports = function(RED) {
"use strict";
var Board = require('firmata');
var SP = require('firmata/node_modules/serialport');
var SP = require('serialport');
// The Board Definition - this opens (and closes) the connection
function ArduinoNode(n) {
@ -36,8 +36,10 @@ module.exports = function(RED) {
done();
if (RED.settings.verbose) { node.log(RED._("arduino.status.portclosed")); }
});
} catch(e) { done(); }
} else { done(); }
}
catch(e) { done(); }
}
else { done(); }
});
}
RED.nodes.registerType("arduino-board",ArduinoNode);

View File

@ -9,7 +9,7 @@ Install
Run the following command in your Node-RED user directory - typically `~/.node-red`
npm i node-red-node-arduino
npm i --unsafe-perm node-red-node-arduino
Usage
-----

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-node-arduino",
"version" : "0.0.11",
"version" : "0.0.14",
"description" : "A Node-RED node to talk to an Arduino running firmata",
"dependencies" : {
"firmata" : "~0.14.1"
"firmata" : "~0.17.0"
},
"repository" : {
"type":"git",

View File

@ -14,9 +14,11 @@ module.exports = function (RED) {
adjustName = function (pin) {
if (pin === "P8_7") {
pin = "P8_07";
} else if (pin === "P8_8") {
}
else if (pin === "P8_8") {
pin = "P8_08";
} else if (pin === "P8_9") {
}
else if (pin === "P8_9") {
pin = "P8_09";
}
return pin;
@ -38,7 +40,8 @@ module.exports = function (RED) {
this.averaging = n.averaging;
if (this.averaging) {
this.averages = 10;
} else {
}
else {
this.averages = 1;
}
@ -54,7 +57,8 @@ module.exports = function (RED) {
count = count - 1;
if (count > 0) {
bonescript.analogRead(node._pin, analogReadCallback);
} else {
}
else {
var msg = {};
msg.topic = node.topic;
sum = sum/node.averages;
@ -76,7 +80,8 @@ module.exports = function (RED) {
count = node.averages;
bonescript.analogRead(node._pin, analogReadCallback);
});
} else {
}
else {
node.error("Unconfigured input pin");
}
}
@ -92,18 +97,22 @@ module.exports = function (RED) {
this._pin = adjustName(this.pin); // Adjusted for Octal if necessary
if (n.activeLow) { // Set the 'active' state 0 or 1 as appropriate
this.activeState = 0;
} else {
}
else {
this.activeState = 1;
}
this.updateInterval = n.updateInterval*1000; // How often to send totalActiveTime messages
this.debounce = n.debounce || null; // Enable switch contact debouncing algorithm
if (n.outputOn === "rising") {
this.activeEdges = [false, true];
} else if (n.outputOn === "falling") {
}
else if (n.outputOn === "falling") {
this.activeEdges = [true, false];
} else if (n.outputOn === "both") {
}
else if (n.outputOn === "both") {
this.activeEdges = [true, true];
} else {
}
else {
node.error("Invalid edge type: " + n.outputOn);
}
@ -130,10 +139,12 @@ module.exports = function (RED) {
node.interruptAttached = true;
node.on("input", inputCallback);
node.intervalId = setInterval(timerCallback, node.updateInterval);
} else {
}
else {
node.error("Failed to attach interrupt");
}
} else if (node.currentState !== Number(x)) {
}
else if (node.currentState !== Number(x)) {
if (node.debounce) {
if (node.debouncing === false) {
node.debouncing = true;
@ -141,7 +152,8 @@ module.exports = function (RED) {
bonescript.digitalRead(node._pin, debounceCallback);
}, Number(node.debounce));
}
} else {
}
else {
sendStateMessage(x);
}
}
@ -166,7 +178,8 @@ module.exports = function (RED) {
var now = Date.now();
if (node.currentState === node.activeState) {
node.lastActiveTime = now;
} else if (!isNaN(node.lastActiveTime)) {
}
else if (!isNaN(node.lastActiveTime)) {
node.totalActiveTime += now - node.lastActiveTime;
}
if (node.activeEdges[node.currentState]) {
@ -203,7 +216,8 @@ module.exports = function (RED) {
var inputCallback = function (ipMsg) {
if (String(ipMsg.topic).search(/load/i) < 0 || isFinite(ipMsg.payload) === false) {
node.totalActiveTime = 0;
} else {
}
else {
node.totalActiveTime = Number(ipMsg.payload);
}
if (node.currentState === node.activeState) {
@ -217,7 +231,8 @@ module.exports = function (RED) {
if (node.activeEdges[0] && node.activeEdges[1]) {
msg = [{topic: node.topic}, {topic: node.topic}];
msg[0].payload = node.currentState;
} else {
}
else {
msg = [null, {topic: node.topic}];
}
msg[1].payload = node.totalActiveTime;
@ -246,12 +261,14 @@ module.exports = function (RED) {
node.emit("input", {});
}, 50);
});
} else {
}
else {
node.error("Unable to set " + pin + " as input: " + response);
}
});
});
} else {
}
else {
node.error("Unconfigured input pin");
}
}
@ -286,10 +303,12 @@ module.exports = function (RED) {
node.interruptAttached = true;
node.on("input", inputCallback);
node.intervalId = setInterval(timerCallback, node.updateInterval);
} else {
}
else {
node.error("Failed to attach interrupt");
}
} else {
}
else {
node.pulseTime = [node.pulseTime[1], process.hrtime()];
node.pulseCount = node.pulseCount + 1;
}
@ -301,7 +320,8 @@ module.exports = function (RED) {
var inputCallback = function (msg) {
if (String(msg.topic).search(/load/i) < 0 || isFinite(msg.payload) === false) {
node.pulseCount = 0;
} else {
}
else {
node.pulseCount = Number(msg.payload);
}
};
@ -337,19 +357,22 @@ module.exports = function (RED) {
if (node.countType === "pulse") {
// interruptType = bonescript.FALLING; <- doesn't work in v0.2.4
interruptType = bonescript.RISING;
} else {
}
else {
interruptType = bonescript.CHANGE;
}
// Attempt to attach the required interrupt handler to the pin. If we succeed,
// the input event and interval handlers will be installed by interruptCallback
bonescript.attachInterrupt(node._pin, interruptType, interruptCallback)
});
} else {
}
else {
node.error("Unable to set " + pin + " as input: " + response);
}
});
});
} else {
}
else {
node.error("Unconfigured input pin");
}
}
@ -376,12 +399,15 @@ module.exports = function (RED) {
var newState;
if (node.toggle) {
newState = node.currentState === 0 ? 1 : 0;
} else {
}
else {
if (isFinite(Number(msg.payload))) {
newState = Number(msg.payload) > 0.5;
} else if (msg.payload) {
}
else if (msg.payload) {
newState = true;
} else {
}
else {
newState = false;
}
if (node.inverting) {
@ -402,7 +428,8 @@ module.exports = function (RED) {
setPinMode(node._pin, bonescript.OUTPUT, function (response, pin) {
if (response) {
node.error("Unable to set " + pin + " as output: " + response.err);
} else {
}
else {
node.on("input", inputCallback);
setTimeout(function () {
bonescript.digitalWrite(node._pin, node.defaultState, function() {});
@ -410,7 +437,8 @@ module.exports = function (RED) {
}
});
});
} else {
}
else {
node.error("Unconfigured output pin");
}
}
@ -453,10 +481,12 @@ module.exports = function (RED) {
node.send({topic: node.topic, payload: node.pulseState});
});
}
} else {
}
else {
if (node.pulseTimer !== null) {
clearTimeout(node.pulseTimer);
} else {
}
else {
bonescript.digitalWrite(node._pin, node.pulseState, function() {
node.send({topic: node.topic, payload: node.pulseState});
});
@ -484,12 +514,14 @@ module.exports = function (RED) {
node.on("input", inputCallback);
// Set the pin to the default state once the dust settles
setTimeout(endPulseCallback, 50);
} else {
}
else {
node.error("Unable to set " + pin + " as output: " + response.err);
}
});
});
} else {
}
else {
node.error("Unconfigured output pin");
}
}

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-node-beaglebone",
"version" : "0.1.8",
"version" : "0.1.9",
"description" : "A set of Node-RED nodes to interface to the GPIO pins of a Beaglebone Black board",
"dependencies" : {
"octalbonescript":"^1.2.*"
"octalbonescript":"^1.2.2"
},
"repository" : {
"type":"git",

View File

@ -4,7 +4,7 @@
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Uses pins 11, 13 aand 15.<br/>
<div class="form-tips">Uses pins 11, 13 and 15.<br/>
See info panel for the various input options and limitations.</div>
</script>

View File

@ -12,7 +12,8 @@ module.exports = function(RED) {
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
} catch(err) {
}
catch(err) {
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-ledborg",
"version" : "0.0.17",
"version" : "0.0.18",
"description" : "A Node-RED node to control a PiBorg LedBorg board for a Raspberry Pi.",
"dependencies" : {
},

View File

@ -69,9 +69,8 @@ module.exports = function(RED) {
var l = Number(msg.payload.state);
if ((out >= 1) && (out <= 8)) {
out = (Math.pow(2, (out-1)));
if (l === 0) { out = ~ out; }
byte = byte & out & 255;
console.log("NOW",byte);
if (l === 0) { byte = (byte & (~out) & 255); }
else { byte = (byte | out) & 255; }
if (node.child !== null) { node.child.stdin.write(byte+"\n"); }
else { node.warn("Command not running"); }
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-piliter",
"version" : "0.0.10",
"version" : "0.0.11",
"description" : "A Node-RED node to drive a Raspberry Pi Pi-LITEr 8 LED board.",
"dependencies" : {
},

View File

@ -23,8 +23,10 @@ These can be any spare valid Pi GPIO pins. e.g.
7,11
You can also set the repeat frequency of measurements - default 0.5 seconds.
Outputs a `msg.payload` with a number representing the range in cm.
Produces one measure every 0.5s - but only if the distance is different from the previous reading.
Produces one measure every 0.5s (by default) - but only if the distance is different from the previous reading.
**Note:** we are using the actual physical pin numbers on connector P1 as they are easier to locate.

View File

@ -15,46 +15,62 @@ GPIO.setwarnings(False)
ECHO = 0
TRIGGER = 0
OLD = 0
SLEEP = 0.5
def Measure():
start = 0
realstart = 0
realstart = time.time()
GPIO.output(TRIGGER, True)
time.sleep(0.00001)
GPIO.output(TRIGGER, False)
channel = GPIO.wait_for_edge(ECHO, GPIO.BOTH, timeout=200)
if channel is None:
print("Ultrasonic sensor timed out (pre-echo).")
GPIO.remove_event_detect(ECHO)
restart()
# else:
# print("Echo start detected")
start = time.time()
while GPIO.input(ECHO)==0:
start = time.time()
Dif = time.time() - realstart
if Dif > 0.2:
print("Ultrasonic Sensor Timed out, Restarting.")
time.sleep(0.4)
Main()
while GPIO.input(ECHO)==1:
stop = time.time()
GPIO.wait_for_edge(ECHO, GPIO.BOTH, timeout=400)
if channel is None:
print("Ultrasonic sensor timed out (post-echo).")
GPIO.remove_event_detect(ECHO)
restart()
# else:
# print("Echo finish detected")
stop = time.time()
elapsed = stop-start
distance = (elapsed * 36000)/2
distance = (elapsed * 34300)/2 # Using speed of sound at 20C (68F)
return distance
def restart():
# print("Restarting...")
GPIO.setmode(GPIO.BOARD) # Use GPIO BOARD numbers
GPIO.setup(TRIGGER, GPIO.OUT) # Trigger
GPIO.output(TRIGGER, False) # Set low
GPIO.setup(ECHO, GPIO.OUT) # Echo
GPIO.output(ECHO, False)
time.sleep(0.1)
GPIO.setup(ECHO,GPIO.IN)
GPIO.add_event_detect(ECHO, GPIO.BOTH)
time.sleep(2.0)
# Main program loop
if len(sys.argv) > 1:
pins = sys.argv[1].lower().split(',')
if len(pins) != 2:
print "Bad number of pins supplied"
if len(pins) != 3:
print "Bad parameters supplied"
print pins
sys.exit(0)
TRIGGER = int(pins[0])
ECHO = int(pins[1])
SLEEP = float(pins[2])
GPIO.setmode(GPIO.BOARD) # Use GPIO BOARD numbers
GPIO.setup(TRIGGER, GPIO.OUT) # Trigger
GPIO.setup(ECHO, GPIO.OUT) # Echo
GPIO.output(ECHO, False)
GPIO.setup(ECHO,GPIO.IN)
restart()
# Flush stdin so we start clean
while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0])>0:
@ -63,17 +79,18 @@ if len(sys.argv) > 1:
while True:
try:
distance = int( Measure() + 0.5 )
if distance != OLD:
if distance != OLD and distance > 2 and distance < 400:
print(distance)
OLD = distance
time.sleep(0.5)
except: # try to clean up on exit
print("0.0");
time.sleep(SLEEP)
except Exception as e: # try to clean up on exit
print(e) # Print error message on exception
GPIO.remove_event_detect(ECHO)
GPIO.cleanup(TRIGGER)
GPIO.cleanup(ECHO)
sys.exit(0)
else:
print "Bad params"
print " sudo nrsrf.py trigger_pin,echo_pin"
print " sudo nrsrf.py trigger_pin,echo_pin,rate_in_seconds"
sys.exit(0)

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pisrf",
"version" : "0.0.3",
"version" : "0.1.0",
"description" : "A Node-RED node for a Raspberry Pi to use a SRF04 or SRF05 range finder",
"dependencies" : {
},

View File

@ -4,7 +4,14 @@
<label for="node-input-pins"><i class="fa fa-circle"></i> Pins</label>
<input type="text" id="node-input-pins" placeholder="Trigger,Echo">
</div>
<br/>
<div class="form-row">
<label for="node-input-pulse"><i class="fa fa-clock-o"></i> Repeat (S)</label>
<input type="text" id="node-input-pulse" placeholder="time between readings">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="fa fa-bars"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="optional topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
@ -16,7 +23,7 @@
<script type="text/x-red" data-help-name="rpi-srf">
<p>Raspberry Pi input from an SRF04 or SRF05 ultrasonic range finder.</p>
<p>Outputs a <code>msg.payload</code> with a number representing the range in cm.</p>
<p>Produces one measure every 0.5s - but only if the distance is different from the previous reading.</p>
<p>Produces one measurement every 0.5s (default) - but only if the distance is different from the previous reading.</p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
</script>
@ -26,6 +33,8 @@
color:"#c6dbef",
defaults: {
name: { value:"" },
topic: { value:"SRF" },
pulse: {value:"0.5" },
pins: { value:"", required:true, validate:RED.validators.regex(/^\d+,\d+$/) }
},
inputs:0,

View File

@ -24,19 +24,21 @@ module.exports = function(RED) {
function PiSrfNode(n) {
RED.nodes.createNode(this, n);
this.topic = n.topic;
this.pins = n.pins;
this.pins += ","+(n.pulse || 0.5);
var node = this;
if (node.pins !== undefined) {
node.child = spawn(gpioCommand, [node.pins]);
node.running = true;
if (RED.settings.verbose) { node.log("pin: " + node.pins + " :"); }
if (RED.settings.verbose) { node.log("parameters: " + node.pins + " :"); }
node.child.stdout.on('data', function(data) {
if (RED.settings.verbose) { node.log("out: " + data + " :"); }
data = data.toString().trim();
if (data.length > 0) {
node.send({topic:"SRF",payload:data});
node.send({topic:node.topic, payload:data});
}
});
@ -58,7 +60,7 @@ module.exports = function(RED) {
}
else {
node.error("Invalid GPIO pins: " + node.pin);
node.error("Invalid Parameters: " + node.pins);
}
var wfi = function(done) {

View File

@ -164,10 +164,12 @@ module.exports = function(RED) {
if (node.pin !== undefined) {
if (node.pin === "12") {
node.child = spawn(gpioCommand, ["buzz",node.pin]);
} else {
}
else {
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]);
}
}

View File

@ -32,7 +32,7 @@ module.exports = function(RED) {
return typeof (value) === "undefined" || value === null ? value = defaultValue : value;
}
function validateArray(value, defaultValue){
function validateArray(value, defaultValue) {
return typeof (value) === "undefined" || Array.isArray(value) ? value : defaultValue;
}
@ -66,7 +66,7 @@ module.exports = function(RED) {
this.name = n.name;
this.serial = n.serial;
this.mode = n.mode || "normal";
this.mode = n.mode || "normal";
this.task = n.task || "set_color";
this.delay = n.delay || 500;
this.repeats = n.repeats || 1;
@ -95,25 +95,28 @@ module.exports = function(RED) {
if (Object.size(node.led) === 0) {
node.status({fill:"red",shape:"ring",text:"not found"});
node.error("BlinkStick with serial number " + node.serial + " not found");
} else {
}
else {
node.status({fill:"green",shape:"dot",text:"connected"});
if(node.mode == "normal"){node.led.setMode(0);}
else if(node.mode == "inverted"){node.led.setMode(1);}
else if(node.mode == "neopixel"){node.led.setMode(2);}
if (node.mode == "normal") {node.led.setMode(0);}
else if (node.mode == "inverted") {node.led.setMode(1);}
else if (node.mode == "neopixel") {node.led.setMode(2);}
if (callback) { callback(); }
}
});
} else {
}
else {
node.led = blinkstick.findFirst();
if (Object.size(node.led) === 0) {
node.status({fill:"red",shape:"ring",text:"not found"});
node.error("No BlinkStick found");
} else {
}
else {
node.status({fill:"green",shape:"dot",text:"connected"});
if(node.mode == "normal"){node.led.setMode(0);}
else if(node.mode == "inverted"){node.led.setMode(1);}
else if(node.mode == "neopixel"){node.led.setMode(2);}
if (node.mode == "normal") {node.led.setMode(0);}
else if (node.mode == "inverted") {node.led.setMode(1);}
else if (node.mode == "neopixel") {node.led.setMode(2);}
if (callback) { callback(); }
}
}
@ -159,12 +162,15 @@ module.exports = function(RED) {
//Select animation to perform
if (node.task == "pulse") {
node.led.pulse(node.color, {'duration': node.duration, 'steps': node.steps, 'channel': node.channel, 'index': node.index }, blinkstickAnimationComplete);
} else if (node.task == "morph") {
}
else if (node.task == "morph") {
node.led.morph(node.color, {'duration': node.duration, 'steps': node.steps, 'channel': node.channel, 'index': node.index }, blinkstickAnimationComplete);
} else if (node.task == "blink") {
}
else if (node.task == "blink") {
node.led.blink(node.color,{'repeats': node.repeats, 'delay': node.delay, 'channel': node.channel, 'index': node.index }, blinkstickAnimationComplete);
} else {
if(node.row.length > 0){
}
else {
if (node.row.length > 0) {
var dat = [];
for (var i = 0; i < node.row.length; i++) {
if (typeof node.row[i] === "string") { // if string then assume must be colour names
@ -187,14 +193,15 @@ module.exports = function(RED) {
node.led.setColors(node.channel, dat, blinkstickAnimationComplete);
}
else {
node.warn("Colour array length not / 3");
node.warn("Colour array length not / 3");
}
}
else {
node.led.setColor(node.color, {'channel': node.channel, 'index': node.index}, blinkstickAnimationComplete);
}
}
} catch (err) {
}
catch (err) {
if (err.toString().indexOf("setColor") !== -1) {
node.led.setColour(node.color, blinkstickAnimationComplete);
node.warn("Old version - please upgrade Blinkstick npm");
@ -269,19 +276,22 @@ module.exports = function(RED) {
node.channel = typeof(data.channel) !== 'undefined' ? data.channel : node.channel;
node.index = data.index ? data.index : node.index;
node.row = data.row ? data.row : node.row;
} else {
}
else {
node.error(data);
return;
}
}
} else if (p1.test(msg.payload)) {
}
else if (p1.test(msg.payload)) {
//Color value is represented as "red,green,blue" string of bytes
var rgb = msg.payload.split(",");
//Convert color value back to HEX string for easier implementation
node.color = "#" + decimalToHex(parseInt(rgb[0])&255) +
decimalToHex(parseInt(rgb[1])&255) + decimalToHex(parseInt(rgb[2])&255);
} else {
}
else {
//Sanitize color value
node.color = msg.payload.toLowerCase().replace(/\s+/g,'');
if (node.color === "amber") { node.color = "#FFBF00"; }
@ -293,7 +303,8 @@ module.exports = function(RED) {
if (animationComplete) {
applyColor();
}
} else {
}
else {
//Attempt to find BlinkStick and start animation if it's found
findBlinkStick(function() {
if (animationComplete) {

View File

@ -16,7 +16,8 @@ module.exports = function(RED) {
try {
device = new HID.HID(devices[i].path);
break;
} catch (e) {
}
catch (e) {
node.log(e)
}
}
@ -32,16 +33,19 @@ module.exports = function(RED) {
var g = parseInt(msg.payload.slice(3,5),16);
var b = parseInt(msg.payload.slice(5),16);
device.sendFeatureReport([115,r,g,b]);
} else if (p2.test(msg.payload)) {
}
else if (p2.test(msg.payload)) {
var args = msg.payload.split(',');
if (args.length == 3) {
device.sendFeatureReport([115,parseInt(args[0]),parseInt(args[1]),parseInt(args[2])]);
}
} else {
}
else {
node.warn("incompatable input - " + msg.payload);
}
});
} else {
}
else {
node.warn("no digispark RGB found");
}

View File

@ -13,7 +13,7 @@ module.exports = function(RED) {
this.pollIntervalRef = undefined;
var hmoutnode = this;
this.hm = new Heatmiser(this.ip, this.pin);
this.hm = new Heatmiser.Wifi(this.ip, this.pin);
this.hm.on('success', function(data) {
if (DEBUG) {
@ -84,7 +84,7 @@ module.exports = function(RED) {
var hminnode = this;
this.pollIntervalRef = undefined;
this.hm = new Heatmiser(this.ip, this.pin);
this.hm = new Heatmiser.Wifi(this.ip, this.pin);
this.hm.on('success', function(data) {
if (DEBUG) {

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-contrib-heatmiser",
"version" : "0.0.4",
"version" : "0.0.5",
"description" : "A Node-RED node to control and poll a HeatMiser thermostat.",
"dependencies" : {
"heatmiser" : "2.0.0"
"heatmiser" : "~2.0.0"
},
"repository" : {
"type":"git",

View File

@ -16,31 +16,37 @@ module.exports = function(RED) {
var g = node.x.read();
var msg = { payload:g, topic:node.board+"/D"+node.pin };
switch (g) {
case 0:
case 0: {
node.status({fill:"green",shape:"ring",text:"low"});
if (node.interrupt=== "f" || node.interrupt === "b") {
node.send(msg);
}
break;
case 1:
}
case 1: {
node.status({fill:"green",shape:"dot",text:"high"});
if (node.interrupt=== "r" || node.interrupt === "b") {
node.send(msg);
}
break;
default:
}
default: {
node.status({fill:"grey",shape:"ring",text:"unknown"});
}
}
});
switch (node.x.read()) {
case 0:
case 0: {
node.status({fill:"green",shape:"ring",text:"low"});
break;
case 1:
}
case 1: {
node.status({fill:"green",shape:"dot",text:"high"});
break;
default:
}
default: {
node.status({});
}
}
this.on('close', function() {
node.x.isr(m.EDGE_BOTH, null);

View File

@ -10,7 +10,8 @@ module.exports = function(RED) {
var node = this;
if (node.pin === 14) {
node.p = new m.Gpio(3,false,true); // special for onboard LED v1
} else {
}
else {
node.p = new m.Gpio(node.pin);
}
node.p.mode(m.PIN_GPIO);
@ -21,7 +22,8 @@ module.exports = function(RED) {
node.on("input", function(msg) {
if (msg.payload == "1") {
node.p.write(1);
} else {
}
else {
node.p.write(0);
}
});

View File

@ -56,7 +56,8 @@ module.exports = function(RED) {
}
else { console.log(key); }
});
} catch(err) { node.warn("can't open MakeyMakey: Do you need root access ?"); }
}
catch(err) { node.warn("can't open MakeyMakey: Do you need root access ?"); }
}
else {
findmakey();

View File

@ -14,8 +14,8 @@ It will appear in the menu as ` A/D Converter `.
You must ensure that SPI is enabled. For recent (2016) versions of Raspbian you can do this
- Run `sudo raspi-config`
- Select `9 - Advanced Options`
- Select `A5 - SPI`
- Select `5 - Interfacing Options`
- Select `P4 - SPI`
- Select `yes` to enable SPI
- Select `OK` to confirm
- Select the `Finish` button

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pi-mcp3008",
"version" : "0.0.7",
"version" : "0.0.8",
"description" : "A Node-RED node to read from the MCP3008 Analogue to Digital Converter",
"dependencies" : {
"mcp-spi-adc": "^0.3.1"

View File

@ -6,7 +6,8 @@ module.exports = function(RED) {
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
} catch(err) {
}
catch(err) {
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}

View File

@ -3,7 +3,11 @@
# Import library functions we need
import sys
import time
from neopixel import *
try:
from rpi_ws281x import __version__, PixelStrip, Adafruit_NeoPixel, Color
except ImportError:
from neopixel import Adafruit_NeoPixel as PixelStrip, Color
__version__ = "legacy"
# LED strip configuration:
LED_COUNT = 8 # Number of LED pixels.
@ -12,10 +16,25 @@ LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_DMA = 5 # DMA channel to use for generating signal (try 5)
LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL = 0 # PWM channel
LED_GAMMA = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11,
11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28,
29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40,
40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89,
90, 91, 93, 94, 95, 96, 98, 99,100,102,103,104,106,107,109,110,
111,113,114,116,117,119,120,121,123,124,126,128,129,131,132,134,
135,137,138,140,142,143,145,146,148,150,151,153,155,157,158,160,
162,163,165,167,169,170,172,174,176,178,179,181,183,185,187,189,
191,193,194,196,198,200,202,204,206,208,210,212,214,216,218,220,
222,224,227,229,231,233,235,237,239,241,244,246,248,250,252,255]
if sys.version_info >= (3,0):
print("Sorry - currently only configured to work with python 2.x")
sys.exit(1)
LED_COUNT = int(sys.argv[1])
WAIT_MS = int(sys.argv[2])
@ -103,8 +122,12 @@ def rainbowCycle(strip, wait_ms=20, iterations=2):
# Main loop:
if __name__ == '__main__':
# Create NeoPixel object with appropriate configuration.
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS)
# Intialize the library (must be called once before other functions).
#strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS)
if __version__ == "legacy":
strip = PixelStrip(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
else:
strip = PixelStrip(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_GAMMA)# Intialize the library (must be called once before other functions).
strip.begin()
## Color wipe animations.

View File

@ -2,21 +2,22 @@
module.exports = function(RED) {
"use strict";
var spawn = require('child_process').spawn;
var execSync = require('child_process').execSync;
var fs = require('fs');
var colors = require('./colours.js');
var piCommand = __dirname+'/neopix';
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
} catch(err) {
}
catch(err) {
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/neopixel.py')) {
RED.log.warn("Can't find neopixel.py python library");
throw "Warning : Can't find neopixel.py python library";
if (execSync('python -c "import neopixel"').toString() !== "") {
RED.log.warn("Can't find neopixel python library");
throw "Warning : Can't find neopixel python library";
}
if ( !(1 & parseInt ((fs.statSync(piCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
@ -57,7 +58,8 @@ module.exports = function(RED) {
if (node.mode.indexOf("need") >= 0) {
needle = colors.getRGB(parts[0],node.rgb);
pay = "0,"+(l-1)+","+node.fgnd+"\n"+l+","+needle+"\n"+(l+1)+","+(node.pixels-1)+","+node.bgnd;
} else {
}
else {
node.fgnd = colors.getRGB(parts[0],node.rgb);
pay = "0,"+l+","+node.fgnd+"\n"+(l+1)+","+(node.pixels-1)+","+node.bgnd;
}
@ -78,7 +80,8 @@ module.exports = function(RED) {
ll = ll - 1;
if (node.mode.indexOf("need") >= 0) {
pay = "0,"+(ll-1)+","+node.fgnd+"\n"+ll+","+needle+"\n"+(ll+1)+","+(node.pixels-1)+","+node.bgnd;
} else {
}
else {
pay = "0,"+ll+","+node.fgnd+"\n"+(ll+1)+","+(node.pixels-1)+","+node.bgnd;
}
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pi-neopixel",
"version" : "0.0.14",
"version" : "0.0.16",
"description" : "A Node-RED node to output to a neopixel (ws2812) string of LEDS from a Raspberry Pi.",
"dependencies" : {
},

View File

@ -1,13 +1,13 @@
#!/usr/bin/env node
var fs = require('fs');
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/neopixel.py')) {
console.warn("WARNING : Can't find neopixel.py python library");
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/unicornhat.py')) {
console.warn("WARNING : Can't find required python library");
console.warn("WARNING : Please install using the following command");
console.warn("WARNING : Note: this uses root...");
console.warn("WARNING : curl -sS get.pimoroni.com/unicornhat | bash\n");
//process.exit(1);
}
else {
console.log("Neopixel Python library found OK.\n")
console.log("Python library found OK.\n")
}

View File

@ -7,42 +7,49 @@ module.exports = function(RED) {
var checkLength = function(text) {
var l = text.length;
switch(true) {
case /^http:\/\/www./.test(text):
l -= 10;
break;
case /^https:\/\/www./.test(text):
l -= 11;
break;
case /^http:\/\//.test(text):
l -= 6;
break;
case /^https:\/\//.test(text):
l -= 7;
break;
switch (true) {
case /^http:\/\/www./.test(text): {
l -= 10;
break;
}
case /^https:\/\/www./.test(text): {
l -= 11;
break;
}
case /^http:\/\//.test(text): {
l -= 6;
break;
}
case /^https:\/\//.test(text): {
l -= 7;
break;
}
}
switch(true) {
case /.*\.info\/.*/.test(text):
l -= 5;
break;
switch (true) {
case /.*\.info\/.*/.test(text): {
l -= 5;
break;
}
case /.*\.com\/.*/.test(text):
case /.*\.net\/.*/.test(text):
case /.*\.org\/.*/.test(text):
case /.*\.edu\/.*/.test(text):
case /.*\.biz\/.*/.test(text):
case /.*\.gov\/.*/.test(text):
case /.*\.info.*/.test(text):
l -= 4;
break;
case /.*\.info.*/.test(text): {
l -= 4;
break;
}
case /.*\.com.*/.test(text):
case /.*\.net.*/.test(text):
case /.*\.org.*/.test(text):
case /.*\.edu.*/.test(text):
case /.*\.biz.*/.test(text):
case /.*\.gov.*/.test(text):
l -= 3;
break;
case /.*\.gov.*/.test(text): {
l -= 3;
break;
}
}
return l;
}
@ -70,7 +77,8 @@ module.exports = function(RED) {
try {
eddystoneBeacon.advertiseUrl(node.url, node.options);
node.status({fill:"green",shape:"dot",text:node.url});
} catch(e) {
}
catch(e) {
node.error('Error setting beacon URL', e);
}
}
@ -83,7 +91,8 @@ module.exports = function(RED) {
try {
eddystoneBeacon.advertiseUid(node.namespace, node.instance, node.options);
node.status({fill:"green",shape:"dot",text:node.namespace});
} catch(e) {
}
catch(e) {
node.error('Error setting beacon information', e);
}
}
@ -96,7 +105,8 @@ module.exports = function(RED) {
try {
eddystoneBeacon.stop();
node.status({fill:"red",shape:"dot",text:"Stopped"});
} catch(e) {
}
catch(e) {
node.error('error shutting down beacon', e);
}
return;
@ -107,7 +117,8 @@ module.exports = function(RED) {
try {
eddystoneBeacon.advertiseUrl(node.url, node.options);
node.status({fill:"green",shape:"dot",text:node.url});
} catch(e) {
}
catch(e) {
node.error('Error setting beacon URL', e);
}
return;
@ -116,7 +127,8 @@ module.exports = function(RED) {
try {
eddystoneBeacon.advertiseUid(node.namespace, node.instance, node.options);
node.status({fill:"green",shape:"dot",text:node.namespace});
} catch(e) {
}
catch(e) {
node.error('Error setting beacon information', e);
}
return;
@ -124,30 +136,33 @@ module.exports = function(RED) {
}
// url mode
if (node.mode === "url") {
if (checkLength(msg.payload) <= 18) {
try {
node.url = msg.payload;
eddystoneBeacon.advertiseUrl(node.url, node.options);
node.status({fill:"green",shape:"dot",text:node.url});
} catch(e) {
node.status({fill:"red",shape:"dot",text:"Error setting URL"});
node.error('error updating beacon URL', e);
}
} else {
node.status({fill:"red",shape:"dot",text:"URL too long"});
}
if (checkLength(msg.payload) <= 18) {
try {
node.url = msg.payload;
eddystoneBeacon.advertiseUrl(node.url, node.options);
node.status({fill:"green",shape:"dot",text:node.url});
}
catch(e) {
node.status({fill:"red",shape:"dot",text:"Error setting URL"});
node.error('error updating beacon URL', e);
}
}
else {
node.status({fill:"red",shape:"dot",text:"URL too long"});
}
}
// uid mode
else {
try {
node.namespace = msg.payload;
node.instance = msg.topic;
eddystoneBeacon.advertiseUid(node.namespace, node.instance, node.options);
node.status({fill:"green",shape:"dot",text:msg.payload});
} catch(e) {
node.status({fill:"red",shape:"dot",text:"Error setting beacon information"});
node.error('Error setting beacon information', e);
}
try {
node.namespace = msg.payload;
node.instance = msg.topic;
eddystoneBeacon.advertiseUid(node.namespace, node.instance, node.options);
node.status({fill:"green",shape:"dot",text:msg.payload});
}
catch(e) {
node.status({fill:"red",shape:"dot",text:"Error setting beacon information"});
node.error('Error setting beacon information', e);
}
}
});
@ -157,7 +172,8 @@ module.exports = function(RED) {
node.status({});
eddystoneBeacon.stop();
done();
} catch(e) {
}
catch(e) {
node.error('error shutting down beacon', e);
}
});

177
hardware/pigpiod/LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,65 @@
node-red-node-pi-gpiod
======================
An alternative pair of <a href="http://nodered.org" target="_new">Node-RED</a> nodes to interact with Pi GPIO using
the <a href="http://abyz.co.uk/rpi/pigpio/pigpiod.html" target="_new">PiGPIOd</a> daemon that is now part of Raspbian.
The advantage is that it also talk to GPIO on a Pi that is remote as long as it is running the daemon, and also sharing pins works more cleanly as contention is handled by the multiple connections.
The disadvantage is that you must setup and run the PiGPIO daemon first.
## Requirements
PiGPIOd must be running on the pi. The easiest way to ensure this is to add the following line to your Pi `/etc/rc.local` file.
/usr/bin/pigpiod
**Note**: By default this will expose the daemon on TCP port 8888, which is obviously a **security vulnerability**. If you don't want or need remote access then you can start it in local mode only, (`-l` option), or restrict permissions to certain ip addresses, (`-n {ipaddr}` option). For example:
/usr/bin/pigpiod -l
/usr/bin/pigpiod -n 192.168.1.10
See the <a href="http://abyz.co.uk/rpi/pigpio/pigpiod.html" target="new">instructions</a> for more details.
## Install
Run the following command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-pi-gpiod
## Usage
**Note:** the pin numbers refer the physical pin numbers on connector P1 as they are easier to locate.</p>
### Input node
Generates a `msg.payload` with either a 0 or 1 depending on the state of the input pin.
##### Outputs
- `msg.payload` - *number* - the level of the pin (0 or 1)
- `msg.topic` - *string* - pi/{the pin number}
You may also enable the input pullup resistor &uarr; or the pulldown resistor &darr;.
### Output node
Can be used in Digital, PWM or Servo modes.
##### Input
- `msg.payload` - *number | string*
- Digital - 0, 1 - set pin low or high
- PWM - 0 to 100 - level from 0 to 100%
- Servo - 0 to 100, 50 is centred.
*Hint*: The `range` node can be used to scale inputs to the correct values.
Digital mode expects a `msg.payload` 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.
The initial value of the pin at deploy time can also be set to 0 or 1.
When using PWM and Servo modes, the input value should be a number 0 - 100, and can be floating point.
In servo mode you can also preset the minimum and maximum pulse times as required by your servo in order to reach its full range. Minimum of 5mS, maximum of 25 mS - defaults to 10 and 20 mS respectively.

View File

@ -0,0 +1,57 @@
{
"pi-gpiod": {
"label": {
"gpiopin": "GPIO",
"selectpin": "select pin",
"host": "Host",
"resistor": "Resistor?",
"readinitial": "Read initial state of pin on deploy/restart?",
"type": "Type",
"initpin": "Initialise pin state?",
"debounce": "Debounce",
"limits": "Limits",
"min": "min",
"max": "max"
},
"place": {
"host": "local or remote ip",
"port": "port"
},
"resistor": {
"none": "none",
"pullup": "pullup",
"pulldown": "pulldown"
},
"digout": "Digital output",
"pwmout": "PWM output",
"servo": "Servo output",
"initpin0": "initial level of pin - low (0)",
"initpin1": "initial level of pin - high (1)",
"pinname": "Pin",
"tip": {
"pin": "<b>Pins in Use</b>: ",
"in": "<b>Tip</b>: Only Digital Input is supported - input must be 0 or 1.",
"dig": "<b>Tip</b>: For digital output - input must be 0 or 1.",
"pwm": "<b>Tip</b>: For PWM output - input must be between 0 to 100.",
"ser": "<b>Tip</b>: For Servo output - input must be between 0 to 100. 50 is centre.<br/>Min must be 500uS or more, Max must be 2500uS or less."
},
"types": {
"digout": "digital output",
"input": "input",
"pullup": "input with pull up",
"pulldown": "input with pull down",
"pwmout": "PWM output",
"servo": "Servo output"
},
"status": {
"stopped": "stopped",
"closed": "closed",
"not-running": "not running"
},
"errors": {
"invalidpin": "Invalid GPIO pin",
"invalidinput": "Invalid input",
"error": "error: __error__"
}
}
}

View File

@ -0,0 +1,29 @@
{
"name": "node-red-node-pi-gpiod",
"version": "0.0.7",
"description": "A node-red node for PiGPIOd",
"dependencies" : {
"js-pigpio": "*"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red-nodes/tree/master/hardware/pigpiod"
},
"keywords": [
"node-red", "Pi", "GPIO", "PiGPIOd"
],
"author": {
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
},
"license": "Apache-2.0",
"node-red" : {
"nodes": {
"pi-gpiod": "pi-gpiod.js"
}
}
}

View File

@ -0,0 +1,527 @@
<script type="text/x-red" data-template-name="pi-gpiod in">
<style>
.pinTable {
width: 300px;
display: inline-table;
font-size: 13px;
height: 380px;
min-height: 380px;
max-height: 380px;
}
.pinTableBody {
width: 300px;
display: table-row-group;
line-height: 12px;
}
.pinTableRow {
width: 300;
display: table-row;
height: 14px;
}
.pinTableCellL {
width: 150px;
display: table-cell;
text-align: right;
padding-right: 4px;
vertical-align: top;
border: 1px solid #444;
}
.pinTableCellR {
width: 150px;
display: table-cell;
text-align: left;
padding-left: 4px;
vertical-align: top;
border: 1px solid #000;
}
.pinColorPower {
background-color:#FECBCE;
}
.pinColorGround {
background-color:#DDDDDD;
}
.pinColorGPIO {
background-color:#BFEBBF;
}
.pinColorDual {
background-color:#D0E6F4;
}
.pinColorSD {
background-color:#FFFDD0;
}
</style>
<div class="form-row">
<label><i class="fa fa-circle"></i> <span data-i18n="pi-gpiod.label.gpiopin"></span></label>
<input type="text" id="node-input-pin" style="display:none;">
<div class="pinTable">
<div class="pinTableBody"><form id="pinform" style="height:380px; max-height:380px; margin:initial;">
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 1 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 2 - 5V Power</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SDA1 - GPIO02 - 3 <input type="radio" name="pins" value="2" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 4 - 5V Power</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCL1 - GPIO03 - 5 <input type="radio" name="pins" value="3" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 6 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO04 - 7 <input type="radio" name="pins" value="4" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="14" style="width:auto;"> 8 - GPIO14 - TxD</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 9 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="15" style="width:auto;"> 10 - GPIO15 - RxD</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO17 - 11 <input type="radio" name="pins" value="17" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="18" style="width:auto;"> 12 - GPIO18</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO27 - 13 <input type="radio" name="pins" value="27" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 14 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO22 - 15 <input type="radio" name="pins" value="22" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="23" style="width:auto;"> 16 - GPIO23</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 17 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="24" style="width:auto;"> 18 - GPIO24</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MOSI - GPIO10 - 19 <input type="radio" name="pins" value="10" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 20 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MISO - GPIO09 - 21 <input type="radio" name="pins" value="9" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="25" style="width:auto;"> 22 - GPIO25</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCLK - GPIO11 - 23 <input type="radio" name="pins" value="11" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="8" style="width:auto;"> 24 - GPIO8 - CE0</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 25 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="7" style="width:auto;"> 26 - GPIO7 - CE1</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorSD">SD - 27 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorSD"><input disabled type="radio" name="pins" value="" style="width:auto;"> 28 - SC</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO05 - 29 <input type="radio" name="pins" value="5" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 30 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO06 - 31 <input type="radio" name="pins" value="6" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="12" style="width:auto;"> 32 - GPIO12</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO13 - 33 <input type="radio" name="pins" value="13" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 34 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO19 - 35 <input type="radio" name="pins" value="19" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="16" style="width:auto;"> 36 - GPIO16</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO26 - 37 <input type="radio" name="pins" value="26" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="20" style="width:auto;"> 38 - GPIO20</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 39 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="21" style="width:auto;"> 40 - GPIO21</div>
</div>
</form></div>
</div>
</div>
<div class="form-row">
<label for="node-input-intype"><i class="fa fa-level-up"></i> <span data-i18n="pi-gpiod.label.resistor"></span></label>
<select type="text" id="node-input-intype" style="width:100px;">
<option value="PUD_OFF" data-i18n="pi-gpiod.resistor.none"></option>
<option value="PUD_UP" data-i18n="pi-gpiod.resistor.pullup"></option>
<option value="PUD_DOWN" data-i18n="pi-gpiod.resistor.pulldown"></option>
</select>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="pi-gpiod.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="pi-gpiod.label.readinitial"></span></label>
</div>
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> <span data-i18n="pi-gpiod.label.host"></label>
<input type="text" id="node-input-host" data-i18n="[placeholder]pi-gpiod.place.host" style="width:250px;">
<input type="number" id="node-input-port" data-i18n="[placeholder]pi-gpiod.place.port" style="width:65px;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips" id="pin-tip">Pins marked in blue are dual use. Make sure they are not enabled for
their other use before using as GPIO.</div>
<div class="form-tips"><span data-i18n="[html]pi-gpiod.tip.in"></span></div>
</script>
<script type="text/x-red" data-help-name="pi-gpiod in">
<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.
Requires the <a href="http://abyz.co.uk/rpi/pigpio/pigpiod.html" target="_new">pi-gpiod</a>
daemon to be running on the host computer in order to work.</p>
<p><b>Outputs</b>
<ul>
<li><code>msg.payload</code> - <i>number</i> - the level of the pin (0 or 1).</li>
<li><code>msg.topic</code> - <i>string</i> - pi/{the pin number}</li>
</ul>
<p><b>Details</b></p>
<p>You may also enable the input pullup resistor &uarr; or the pulldown resistor &darr;.</p>
<p><b>Note:</b> the pin numbers refer the physical pin numbers on connector P1 as they are easier to locate.</p>
</script>
<script type="text/javascript">
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
"23":"16", "24":"18", "10":"19", "9":"21", "25":"22", "11":"23", "8":"24", "7":"26",
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
};
RED.nodes.registerType('pi-gpiod in',{
category: 'Raspberry Pi',
color:"#c6dbef",
defaults: {
name: { value:"" },
host: { value:"localhost" },
port: { value:8888 },
pin: { value:"", required:true, validate:RED.validators.number() },
intype: { value:"PUD_OFF" },
debounce: { value:"25" },
read: { value:false }
},
inputs:0,
outputs:1,
icon: "rpi.png",
label: function() {
var suf = "";
if (this.intype === "PUD_UP") { suf = "↑ "}
if (this.intype === "PUD_DOWN") { suf = "↓ "}
return this.name || "PIN: "+suf+bcm2pin[this.pin] ;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
outputLabels: function() { return "GPIO"+this.pin; },
palettelabel: "pi gpiod",
oneditprepare: function() {
var pinnow = this.pin;
var pintip = this._("pi-gpiod.tip.pin");
var pinname = this._("pi-gpiod.pinname");
$('#pinform input').on('change', function() {
this.pin = $("#pinform input[type='radio']:checked").val();
$("#node-input-pin").val(this.pin);
});
$("#node-input-pin").change(function () {
if ($("#node-input-pin").val()) {
$("#pinform input[value="+$("#node-input-pin").val()+"]").prop('checked', true);
}
});
}
});
</script>
<script type="text/x-red" data-template-name="pi-gpiod out">
<style>
.pinTable {
width: 300px;
display: inline-table;
font-size: 13px;
height: 380px;
min-height: 380px;
max-height: 380px;
}
.pinTableBody {
width: 300px;
display: table-row-group;
line-height: 12px;
}
.pinTableRow {
width: 300;
display: table-row;
height: 14px;
}
.pinTableCellL {
width: 150px;
display: table-cell;
text-align: right;
padding-right: 4px;
vertical-align: top;
border: 1px solid #444;
}
.pinTableCellR {
width: 150px;
display: table-cell;
text-align: left;
padding-left: 4px;
vertical-align: top;
border: 1px solid #000;
}
.pinColorPower {
background-color:#FECBCE;
}
.pinColorGround {
background-color:#DDDDDD;
}
.pinColorGPIO {
background-color:#BFEBBF;
}
.pinColorDual {
background-color:#D0E6F4;
}
.pinColorSD {
background-color:#FFFDD0;
}
</style>
<div class="form-row">
<label><i class="fa fa-circle"></i> <span data-i18n="pi-gpiod.label.gpiopin"></span></label>
<input type="text" id="node-input-pin" style="display:none;">
<div class="pinTable">
<div class="pinTableBody"><form id="pinform" style="height:380px; max-height:380px; margin:initial;">
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 1 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 2 - 5V Power</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SDA1 - GPIO02 - 3 <input type="radio" name="pins" value="2" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 4 - 5V Power</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCL1 - GPIO03 - 5 <input type="radio" name="pins" value="3" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 6 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO04 - 7 <input type="radio" name="pins" value="4" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="14" style="width:auto;"> 8 - GPIO14 - TxD</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 9 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="15" style="width:auto;"> 10 - GPIO15 - RxD</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO17 - 11 <input type="radio" name="pins" value="17" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="18" style="width:auto;"> 12 - GPIO18</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO27 - 13 <input type="radio" name="pins" value="27" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 14 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO22 - 15 <input type="radio" name="pins" value="22" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="23" style="width:auto;"> 16 - GPIO23</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 17 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="24" style="width:auto;"> 18 - GPIO24</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MOSI - GPIO10 - 19 <input type="radio" name="pins" value="10" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 20 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MISO - GPIO09 - 21 <input type="radio" name="pins" value="9" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="25" style="width:auto;"> 22 - GPIO25</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCLK - GPIO11 - 23 <input type="radio" name="pins" value="11" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="8" style="width:auto;"> 24 - GPIO8 - CE0</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 25 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="7" style="width:auto;"> 26 - GPIO7 - CE1</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorSD">SD - 27 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorSD"><input disabled type="radio" name="pins" value="" style="width:auto;"> 28 - SC</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO05 - 29 <input type="radio" name="pins" value="5" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 30 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO06 - 31 <input type="radio" name="pins" value="6" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="12" style="width:auto;"> 32 - GPIO12</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO13 - 33 <input type="radio" name="pins" value="13" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 34 - Ground</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO19 - 35 <input type="radio" name="pins" value="19" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="16" style="width:auto;"> 36 - GPIO16</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO26 - 37 <input type="radio" name="pins" value="26" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="20" style="width:auto;"> 38 - GPIO20</div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 39 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="21" style="width:auto;"> 40 - GPIO21</div>
</div>
</form></div>
</div>
</div>
<div class="form-row" id="node-set-pwm">
<label>&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="pi-gpiod.label.type"></span></label>
<select id="node-input-out" style="width: 250px;">
<option value="out" data-i18n="pi-gpiod.digout"></option>
<option value="pwm" data-i18n="pi-gpiod.pwmout"></option>
<option value="ser" data-i18n="pi-gpiod.servo"></option>
</select>
</div>
<div class="form-row" id="node-set-minimax">
<label>&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="pi-gpiod.label.limits"></label>
<span data-i18n="pi-gpiod.label.min"></span> <input type="text" id="node-input-sermin" style="width:70px;"> uS
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span data-i18n="pi-gpiod.label.max"></span> <input type="text" id="node-input-sermax" style="width:70px;"> uS
</div>
<div class="form-row" id="node-set-tick">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-set" style="display:inline-block; width:auto; vertical-align:top;">
<label for="node-input-set" style="width:70%;"><span data-i18n="pi-gpiod.label.initpin"></span></label>
</div>
<div class="form-row" id="node-set-state">
<label for="node-input-level">&nbsp;</label>
<select id="node-input-level" style="width:250px;">
<option value="0" data-i18n="pi-gpiod.initpin0"></option>
<option value="1" data-i18n="pi-gpiod.initpin1"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> <span data-i18n="pi-gpiod.label.host"></label>
<input type="text" id="node-input-host" placeholder="localhost" style="width:250px;">
<input type="number" id="node-input-port" placeholder="port" style="width:65px;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips" id="pin-tip">Pins marked in blue are dual use. Make sure they are not enabled for
their other use before using as GPIO.</div>
<div class="form-tips" id="dig-tip"><span data-i18n="[html]pi-gpiod.tip.dig"></span></div>
<div class="form-tips" id="pwm-tip"><span data-i18n="[html]pi-gpiod.tip.pwm"></span></div>
<div class="form-tips" id="ser-tip"><span data-i18n="[html]pi-gpiod.tip.ser"></span></div>
</script>
<script type="text/x-red" data-help-name="pi-gpiod out">
<p>Raspberry Pi output node. Can be used in Digital, PWM or Servo modes. Requires the
<a href="http://abyz.co.uk/rpi/pigpio/pigpiod.html" target="_new">pi-gpiod</a> daemon to be running in order to work.</p>
<p><b>Inputs</b>
<ul>
<li><code>msg.payload</code> - <i>number | string</i> - 0,1 (Digital), 0-100 (PWM, Servo)</li>
</ul>
<p><b>Details</b></p>
<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 and Servo modes, the input value should be a number 0 - 100, and can be floating point.</p>
<p><b>Note</b>: the pin numbers refer the physical pin numbers on connector P1 as they are easier to locate.</p>
</script>
<script type="text/javascript">
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
"23":"16", "24":"18", "10":"19", "9":"21", "25":"22", "11":"23", "8":"24", "7":"26",
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
};
RED.nodes.registerType('pi-gpiod out',{
category: 'Raspberry Pi',
color:"#c6dbef",
defaults: {
name: { value:"" },
host: { value:"localhost" },
port: { value:8888 },
pin: { value:"", required:true, validate:RED.validators.number() },
set: { value:"" },
level: { value:"0" },
out: { value:"out" },
sermin: { value:"1000" },
sermax: { value:"2000" }
},
inputs:1,
outputs:0,
icon: "rpi.png",
align: "right",
label: function() {
if (this.out === "pwm") { return this.name || "PWM: "+bcm2pin[this.pin]; }
else if (this.out === "ser") { return this.name || "Servo: "+bcm2pin[this.pin]; }
else {
var suf = "";
if (this.set === true) { suf = (this.level === "1") ? "¹ " : "₀ "; }
return this.name || "PIN: " + bcm2pin[this.pin] + suf ;
}
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
inputLabels: function() { return "GPIO"+this.pin; },
palettelabel: "pi gpiod",
oneditprepare: function() {
var pinnow = bcm2pin[this.pin];
var pintip = this._("pi-gpiod.tip.pin");
var pinname = this._("pi-gpiod.pinname");
if (!$("#node-input-out").val()) { $("#node-input-out").val("out"); }
var hidestate = function () {
if ($("#node-input-out").val() === "pwm") {
$('#node-set-tick').hide();
$('#node-set-state').hide();
$('#node-set-minimax').hide();
$('#node-input-set').prop('checked', false);
$("#dig-tip").hide();
$("#pwm-tip").show();
$("#ser-tip").hide();
}
if ($("#node-input-out").val() === "ser") {
$('#node-set-tick').hide();
$('#node-set-state').hide();
$('#node-set-minimax').show();
$('#node-input-set').prop('checked', false);
$("#dig-tip").hide();
$("#pwm-tip").hide();
$("#ser-tip").show();
}
else {
$('#node-set-tick').show();
$('#node-set-minimax').hide();
$("#dig-tip").show();
$("#pwm-tip").hide();
$("#ser-tip").hide();
}
};
$("#node-input-out").change(function () { hidestate(); });
hidestate();
var setstate = function () {
if ($('#node-input-set').is(":checked")) {
$("#node-set-state").show();
} else {
$("#node-set-state").hide();
}
};
$("#node-input-set").change(function () { setstate(); });
setstate();
$('#pinform input').on('change', function() {
this.pin = $("#pinform input[type='radio']:checked").val();
$("#node-input-pin").val(this.pin);
});
$("#node-input-pin").change(function () {
if ($("#node-input-pin").val()) {
$("#pinform input[value="+$("#node-input-pin").val()+"]").prop('checked', true);
}
});
}
});
</script>

View File

@ -0,0 +1,167 @@
module.exports = function(RED) {
"use strict";
var Pigpio = require('js-pigpio');
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
"23":"16", "24":"18", "10":"19", "9":"21", "25":"22", "11":"23", "8":"24", "7":"26",
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
};
var pinTypes = {
"PUD_OFF":RED._("pi-gpiod:types.input"),
"PUD_UP":RED._("pi-gpiod:types.pullup"),
"PUD_DOWN":RED._("pi-gpiod:types.pulldown"),
"out":RED._("pi-gpiod:types.digout"),
"pwm":RED._("pi-gpiod:types.pwmout"),
"ser":RED._("pi-gpiod:types.servo")
};
function GPioInNode(n) {
RED.nodes.createNode(this,n);
this.host = n.host || "127.0.0.1";
this.port = n.port || 8888;
this.pin = n.pin;
this.pio = bcm2pin[n.pin];
this.intype = n.intype;
this.read = n.read || false;
this.debounce = Number(n.debounce || 25);
var node = this;
var PiGPIO;
if (node.pin !== undefined) {
PiGPIO = new Pigpio();
var inerror = false;
var doit = function() {
PiGPIO.pi(node.host, node.port, function(err) {
if (err) {
node.status({fill:"red",shape:"ring",text:err.code+" "+node.host+":"+node.port});
if (!inerror) { node.error(err); inerror = true; }
node.retry = setTimeout(function() { doit(); }, 5000);
}
else {
inerror = false;
PiGPIO.set_mode(node.pin,PiGPIO.INPUT);
PiGPIO.set_pull_up_down(node.pin,PiGPIO[node.intype]);
PiGPIO.set_glitch_filter(node.pin,node.debounce);
node.status({fill:"green",shape:"dot",text:"node-red:common.status.ok"});
node.cb = PiGPIO.callback(node.pin, PiGPIO.EITHER_EDGE, function(gpio, level, tick) {
node.send({ topic:"pi/"+node.pio, payload:Number(level) });
node.status({fill:"green",shape:"dot",text:level});
});
if (node.read) {
setTimeout(function() {
PiGPIO.read(node.pin, function(err, level) {
node.send({ topic:"pi/"+node.pio, payload:Number(level) });
node.status({fill:"green",shape:"dot",text:level});
});
}, 20);
}
}
});
}
doit();
}
else {
node.warn(RED._("pi-gpiod:errors.invalidpin")+": "+node.pio);
}
node.on("close", function(done) {
if (node.retry) { clearTimeout(node.retry); }
node.status({fill:"grey",shape:"ring",text:"pi-gpiod.status.closed"});
node.cb.cancel();
PiGPIO.close();
done();
});
}
RED.nodes.registerType("pi-gpiod in",GPioInNode);
function GPioOutNode(n) {
RED.nodes.createNode(this,n);
this.host = n.host || "127.0.0.1";
this.port = n.port || 8888;
this.pin = n.pin;
this.pio = bcm2pin[n.pin];
this.set = n.set || false;
this.level = parseInt(n.level || 0);
this.out = n.out || "out";
this.sermin = Number(n.sermin)/100;
this.sermax = Number(n.sermax)/100;
if (this.sermin > this.sermax) {
var tmp = this.sermin;
this.sermin = this.sermax;
this.sermax = tmp;
}
if (this.sermin < 5) { this.sermin = 5; }
if (this.sermax > 25) { this.sermax = 25; }
var node = this;
var PiGPIO;
function inputlistener(msg) {
if (!inerror) {
if (msg.payload === "true") { msg.payload = true; }
if (msg.payload === "false") { msg.payload = false; }
var out = Number(msg.payload);
var limit = 1;
if (node.out !== "out") { limit = 100; }
if ((out >= 0) && (out <= limit)) {
if (RED.settings.verbose) { node.log("out: "+msg.payload); }
if (node.out === "out") {
PiGPIO.write(node.pin, msg.payload);
}
if (node.out === "pwm") {
PiGPIO.set_PWM_dutycycle(node.pin, parseInt(msg.payload * 2.55));
}
if (node.out === "ser") {
var r = (node.sermax - node.sermin) * 100;
PiGPIO.setServoPulsewidth(node.pin, parseInt(1500 - (r/2) + (msg.payload * r / 100)));
}
node.status({fill:"green",shape:"dot",text:msg.payload.toString()});
}
else { node.warn(RED._("pi-gpiod:errors.invalidinput")+": "+out); }
}
}
if (node.pin !== undefined) {
PiGPIO = new Pigpio();
var inerror = false;
var doit = function() {
PiGPIO.pi(node.host, node.port, function(err) {
if (err) {
node.status({fill:"red",shape:"ring",text:err.code+" "+node.host+":"+node.port});
if (!inerror) { node.error(err,err); inerror = true; }
node.retry = setTimeout(function() { doit(); }, 5000);
}
else {
inerror = false;
PiGPIO.set_mode(node.pin,PiGPIO.OUTPUT);
if (node.set) {
setTimeout(function() { PiGPIO.write(node.pin,node.level); }, 25 );
node.status({fill:"green",shape:"dot",text:node.level});
} else {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.ok"});
}
// if (node.out === "pwm") {
// PiGPIO.set_PWM_frequency(1000);
// PiGPIO.set_PWM_range(1000);
// }
}
});
}
doit();
node.on("input", inputlistener);
}
else {
node.warn(RED._("pi-gpiod:errors.invalidpin")+": "+node.pio);
}
node.on("close", function(done) {
if (node.retry) { clearTimeout(node.retry); }
node.status({fill:"grey",shape:"ring",text:"pi-gpiod.status.closed"});
PiGPIO.close();
done();
});
}
RED.nodes.registerType("pi-gpiod out",GPioOutNode);
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pi-sense-hat",
"version" : "0.0.13",
"version" : "0.0.15",
"description" : "A Node-RED node to interact with a Raspberry Pi Sense HAT",
"repository" : {
"type":"git",

View File

@ -102,7 +102,7 @@ be sent in a single message by separating them with newline (\n) characters.<p>
<p><code>angle</code> must be 0, 90, 180 or 270.</p>
<p><b>Flip the screen</b></p>
<p>Format: <code>R&lt;axis&gt;</code></p>
<p>Format: <code>F&lt;axis&gt;</code></p>
<p><code>axis</code> must be either <code>H</code> or <code>V</code> to flip on
the horizontal or vertical axis respectively.</p>

View File

@ -52,7 +52,7 @@ module.exports = function(RED) {
}
buffer = lines.pop();
var m,msg;
for (var i=0;i<lines.length;i++) {
for (var i=0; i<lines.length; i++) {
var line = lines[i];
msg = null;
if ((m = KEY_RE.exec(line)) !== null) {
@ -90,7 +90,7 @@ module.exports = function(RED) {
}
}
if (msg && !onclose) {
for (var j=0;j<users.length;j++) {
for (var j=0; j<users.length; j++) {
var node = users[j];
if (node.motion && msg.topic === "motion") {
node.send(RED.util.cloneMessage(msg));
@ -322,9 +322,9 @@ module.exports = function(RED) {
}
}
x = x0;
while(x<=x1) {
while (x<=x1) {
y = y0;
while(y<=y1) {
while (y<=y1) {
expanded.push([x,y,col]);
y++;
}
@ -334,7 +334,7 @@ module.exports = function(RED) {
if (expanded.length > 0) {
var pixels = {};
var rules = [];
for (i=expanded.length-1;i>=0;i--) {
for (i=expanded.length-1; i>=0; i--) {
var rule = expanded[i];
if (!pixels[rule[0]+","+rule[1]]) {
rules.unshift(rule.join(","));

View File

@ -151,7 +151,7 @@ def process_command(data):
speed = 0.1
s = data.split(':',1)
if len(s) == 2:
data = s[1]
data = s[1][0:-1]
if len(s[0]) > 0:
c = s[0].split(",")
if len(c) == 1:

View File

@ -42,9 +42,9 @@ module.exports = function(RED) {
currentFlipV = false;
currentRotation = "R0";
currentDisplay = [];
for (var y=0;y<8;y++) {
for (var y=0; y<8; y++) {
currentDisplay.push([]);
for (var x=0;x<8;x++) {
for (var x=0; x<8; x++) {
currentDisplay[y].push('0,0,0');
}
}
@ -80,8 +80,8 @@ module.exports = function(RED) {
socket.send("FH");
}
var cmd = "";
for (var y=0;y<8;y++) {
for (var x=0;x<8;x++) {
for (var y=0; y<8; y++) {
for (var x=0; x<8; x++) {
cmd += ","+x+","+y+","+currentDisplay[y][x];
}
}
@ -110,7 +110,7 @@ module.exports = function(RED) {
topic: "joystick",
payload: {key: KEY_MAP[m[1]], state: Number(m[2])}
}
for (var j=0;j<users.length;j++) {
for (var j=0; j<users.length; j++) {
var node = users[j];
if (node.stick) {
node.send(RED.util.cloneMessage(msg));
@ -168,7 +168,7 @@ module.exports = function(RED) {
topic: "environment",
payload: {temperature: currentEnvironment.temperature, humidity: currentEnvironment.humidity, pressure: currentEnvironment.pressure}
};
for (var j=0;j<users.length;j++) {
for (var j=0; j<users.length; j++) {
var node = users[j];
if (node.env) {
node.send(RED.util.cloneMessage(msg));
@ -332,7 +332,7 @@ module.exports = function(RED) {
if (expanded.length > 0) {
var pixels = {};
var rules = [];
for (i=expanded.length-1;i>=0;i--) {
for (i=expanded.length-1; i>=0; i--) {
var rule = expanded[i];
if (!pixels[rule[0]+","+rule[1]]) {
rules.unshift(rule.join(","));

View File

@ -100,7 +100,8 @@ module.exports = function(RED) {
},node.uuid);
}
},15000);
} else {
}
else {
console.log("reconfig",node.uuid);
enable(node);
}
@ -114,46 +115,54 @@ module.exports = function(RED) {
var enable = function(node) {
if (node.temperature) {
node.stag.notifyIrTemperature(function() {});
} else {
}
else {
node.stag.unnotifyIrTemperature(function() {});
}
if (node.pressure) {
node.stag.notifyBarometricPressure(function() {});
} else {
}
else {
node.stag.unnotifyBarometricPressure(function() {});
}
if (node.humidity) {
node.stag.notifyHumidity(function() {});
} else {
}
else {
node.stag.unnotifyHumidity(function() {});
}
if (node.accelerometer) {
node.stag.notifyAccelerometer(function() {});
} else {
}
else {
node.stag.unnotifyAccelerometer(function() {});
}
if (node.magnetometer) {
node.stag.notifyMagnetometer(function() {});
} else {
}
else {
node.stag.unnotifyMagnetometer(function() {});
}
if (node.gyroscope) {
node.stag.notifyGyroscope(function() {});
} else {
}
else {
node.stag.unnotifyGyroscope(function() {});
}
if (node.stag.type === "cc2650") {
if (node.luxometer) {
node.stag.enableLuxometer(function() {});
node.stag.notifyLuxometer(function() {});
} else {
}
else {
node.stag.unnotifyLuxometer(function() {});
node.stag.disableLuxometer(function() {});
}
}
if (node.keys) {
node.stag.notifySimpleKey(function() {});
} else {
}
else {
node.stag.unnotifySimpleKey(function() {});
}
}

View File

@ -1,7 +1,7 @@
{
"name": "node-red-node-sensortag",
"description": "A Node-RED node to read data from a TI SensorTag",
"version": "0.0.18",
"version": "0.0.19",
"keywords": [
"node-red",
"sensortag",
@ -9,7 +9,7 @@
"Ti CC2541"
],
"dependencies": {
"sensortag": "~1.2.0"
"sensortag": "~1.3.0"
},
"scripts" : {
"postinstall" : "node scripts/checkplatform.js pibt.sh"

View File

@ -1,17 +1,18 @@
var spawn = require('child_process').spawn;
if (process.argv.length === 3) {
var command = process.argv[2];
var command = process.argv[2];
if (process.platform === 'linux') {
var dir = __dirname;
var script = spawn(dir + "/" + command);
script.on('close',function(code){
process.exit(code);
});
} else {
process.exit(0);
}
if (process.platform === 'linux') {
var dir = __dirname;
var script = spawn(dir + "/" + command);
script.on('close',function(code) {
process.exit(code);
});
}
else {
process.exit(0);
}
} else {
process.exit(0);
}
process.exit(0);
}

View File

@ -1,4 +1,3 @@
#!/bin/bash
#sudo apt-get install libbluetooth-dev libudev-dev pi-bluetooth
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)
sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pi-unicorn-hat",
"version" : "0.0.14",
"version" : "0.0.16",
"description" : "A Node-RED node to output to a Raspberry Pi Unicorn HAT from Pimorini.",
"dependencies" : {
"pngjs": "2.2.*"

View File

@ -2,12 +2,12 @@
var fs = require('fs');
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/unicornhat.py')) {
console.warn("WARNING : Can't find unicorn.py python library");
console.warn("WARNING : Can't find required python library");
console.warn("WARNING : Please install using the following command");
console.warn("WARNING : Note: this uses root...");
console.warn("WARNING : curl -sS get.pimoroni.com/unicornhat | bash\n");
//process.exit(1);
}
else {
console.log("Unicorn Hat Python library found OK.\n")
console.log("Python library found OK.\n")
}

View File

@ -4,6 +4,7 @@ module.exports = function(RED) {
var fs = require('fs');
var PNG = require('pngjs').PNG;
var spawn = require('child_process').spawn;
var execSync = require('child_process').execSync;
var hatCommand = __dirname+'/unihat';
@ -12,9 +13,9 @@ module.exports = function(RED) {
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/unicornhat.py')) {
RED.log.warn("Can't find Unicorn HAT python libraries");
throw "Warning : Can't find Unicorn HAT python libraries";
if (execSync('python -c "import neopixel"').toString() !== "") {
RED.log.warn("Can't find neopixel python library");
throw "Warning : Can't find neopixel python library";
}
if ( !(1 & parseInt ((fs.statSync(hatCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {

View File

@ -24,7 +24,7 @@ The node accecpts the following inputs
* Strings on/off
* integers 1/0
* boolean true/false
* an Object like this (lights only, coming soon)
* an Object like this (lights only & color control is still work in the progress)
```
{
state: 1,

View File

@ -29,8 +29,9 @@
<li>Light Groups</li>
<li>Motion Detector</li>
</ul>
<p>Sockets will generate msg.payload with values of 0/1 for off or on, all other
types will return an object like this:</p>
<p>Sockets will generate msg.payload with values of 0/1/8 for off or on
(8 is on but at standby load for insight sockets), lightswill return an
object like this:</p>
<pre>
{
name: 'Bedroom light',
@ -44,6 +45,8 @@
<ul>
<li>10006 - on/off</li>
<li>10008 - brightness</li>
<li>10300 - color</li>
<li>30301 - color temperature</li>
</ul>
</script>
@ -53,7 +56,8 @@
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
topic: {value:"wemo", required: true},
device: {value:"", type: "wemo-dev"}
device: {value:"", type: "wemo-dev"},
label: {value:""}
},
color: "LawnGreen",
inputs:0, // set the number of inputs - only 0 or 1
@ -61,10 +65,17 @@
// set the icon (held in icons dir below where you save the node)
icon: "belkin.png", // saved in icons/myicon.png
label: function() { // sets the default label contents
return this.name||"wemo";
if (this.name){
return this.name;
} else {
return this.label||"wemo";
}
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
},
oneditsave: function(){
this.label = $('#node-input-device option:selected').text();
}
});
</script>
@ -106,6 +117,7 @@
temperature: 25000
}
</pre>
<p>color control is still a work in progress, but the rest should work</p>
</script>
<script type="text/javascript">
@ -113,17 +125,80 @@
category: 'output', // the palette category
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
device: {type: "wemo-dev", required: true}
device: {type: "wemo-dev", required: true},
label: {value: ""}
},
color: "LawnGreen",
inputs: 1, // set the number of inputs - only 0 or 1
// set the icon (held in icons dir below where you save the node)
icon: "belkin.png", // saved in icons/myicon.png
label: function() { // sets the default label contents
return this.name||"wemo";
if (this.name){
return this.name;
} else {
return this.label||"wemo";
}
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
},
oneditsave: function(){
this.label = $('#node-input-device option:selected').text();
}
});
</script>
<script type="text/x-red" data-template-name="wemo lookup">
<div class="form-row">
<label for="node-input-device"><i class="fa fa-tasks"></i> Device</label>
<input type="text" id="node-input-device" placeholder="Device">
</div>
<br/>
<!-- By convention, most nodes have a 'name' property. The following div -->
<!-- provides the necessary field. Should always be the last option -->
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="wemo lookup">
<p>This node queries the current state of a device</p>
<p>For lights it return a msg.payload that looks like this:</p>
<pre>{
available: true,
state: 0,
dim: 13
}</pre>
<p>Where <em>available</em> is if the blub is actually turned on
at the wall switch<p>
</script>
<script type="text/javascript">
RED.nodes.registerType('wemo lookup',{
category: 'function', // the palette category
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
device: {type: "wemo-dev", required: true},
label: {value:""}
},
color: "LawnGreen",
inputs: 1, // set the number of inputs - only 0 or 1
outputs: 1,
// set the icon (held in icons dir below where you save the node)
icon: "belkin.png", // saved in icons/myicon.png
label: function() { // sets the default label contents
if (this.name){
return this.name;
} else {
return this.label||"wemo";
}
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
},
oneditsave: function(){
this.label = $('#node-input-device option:selected').text();
}
});
</script>

View File

@ -45,10 +45,11 @@ module.exports = function(RED) {
console.log('problem with resubscription %s - %s', res.statusCode, res.statusMessage);
console.log('opts - %s', util.inspect(reSubOptions));
console.log('dev - %s', util.inspect(dev));
delete subscriptions[dev];
delete subscriptions[subs[s]];
delete sub2dev[sub.sid];
subscribe({dev: subs[s]});
} else {
}
else {
// console.log("resubscription good %s", res.statusCode);
// console.log("dev - %s", util.inspect(dev));
}
@ -57,7 +58,7 @@ module.exports = function(RED) {
resub_request.on('error', function() {
//console.log("failed to resubscribe to %s", dev.name );
//need to find a way to resubsribe
delete subscriptions[dev];
delete subscriptions[subs[s]];
delete sub2dev[sub.sid];
subscribe({dev: subs[s]});
});
@ -69,7 +70,7 @@ module.exports = function(RED) {
};
setInterval(resubscribe, 200000);
setInterval(resubscribe, 100000);
var subscribe = function(node) {
var dev = node.dev;
@ -78,7 +79,8 @@ module.exports = function(RED) {
if (subscriptions[dev]) {
//exists
subscriptions[dev].count++;
} else {
}
else {
//new
var ipAddr;
@ -97,7 +99,8 @@ module.exports = function(RED) {
break;
}
}
} else {
}
else {
//node 0.10 not great but best we can do
if (!addrs[add].internal && addrs[add].family == 'IPv4') {
ipAddr = addrs[add].address;
@ -143,7 +146,8 @@ module.exports = function(RED) {
if (res.statusCode == 200) {
subscriptions[dev] = {'count': 1, 'sid': res.headers.sid};
sub2dev[res.headers.sid] = dev;
} else {
}
else {
console.log('failed to subsrcibe');
}
});
@ -181,10 +185,12 @@ module.exports = function(RED) {
unSubreq.end();
} else {
}
else {
subscriptions[dev].count--;
}
} else {
}
else {
//shouldn't ever get here
}
}
@ -210,7 +216,8 @@ module.exports = function(RED) {
node.status({fill: 'green',shape: 'dot',text: 'found'});
}
});
} else {
}
else {
node.status({fill: 'green',shape: 'dot',text: 'found'});
}
@ -224,10 +231,12 @@ module.exports = function(RED) {
}
var on = 0;
var capability = '10006';
if (typeof msg.payload === 'string') {
if (msg.payload == 'on' || msg.payload == '1' || msg.payload == 'true') {
on = 1;
} else if (msg.payload === 'toggle') {
}
else if (msg.payload === 'toggle') {
on = 2;
}
} else if (typeof msg.payload === 'number') {
@ -236,14 +245,30 @@ module.exports = function(RED) {
}
} else if (typeof msg.payload === 'object') {
//object need to get complicated here
if (msg.payload.state && typeof msg.payload.state === 'number') {
if (msg.payload.hasOwnProperty('state') && typeof msg.payload.state === 'number') {
if (dev.type === 'socket') {
if (msg.payload >= 0 && msg.payload < 2) {
if (msg.payload.state >= 0 && msg.payload.state < 2) {
on = msg.payload.state;
}
} else if (dev.type === 'light' || dev.type === 'group') {
if (msg.payload >= 0 && msg.payload < 3) {
on = msg.payload.state;
}
else if (dev.type === 'light' || dev.type === 'group') {
// if (msg.payload.state >= 0 && msg.payload.state < 3) {
// on = msg.payload.state;
// }
var keys = Object.keys(msg.payload);
var caps = [];
var states = [];
for (var i=0; i<keys.length; i++) {
if (msg.payload.hasOwnProperty(keys[i])) {
if (wemo.reverseCapabilityMap.hasOwnProperty(keys[i])) {
caps.push(wemo.reverseCapabilityMap[keys[i]]);
states.push(msg.payload[keys[i]]);
}
}
}
if (caps.length > 0) {
capability = caps.join(',');
on = states.join(',');
}
}
}
@ -253,15 +278,14 @@ module.exports = function(RED) {
}
}
if (dev.type === 'socket') {
if (dev.type == 'socket') {
//console.log("socket");
wemo.toggleSocket(dev, on);
} else if (dev.type === 'light`') {
//console.log("light");
wemo.setStatus(dev,'10006', on);
} else if (dev.type === 'light') {
wemo.setStatus(dev,capability, on);
} else {
console.log('group');
wemo.setStatus(dev, '10006', on);
//console.log('group');
wemo.setStatus(dev, capability, on);
}
});
};
@ -295,17 +319,18 @@ module.exports = function(RED) {
switch (notification.type){
case 'light':
case 'group':
case 'group': {
if (dd.id === notification.id) {
node.send(msg);
}
break;
case 'socket':
}
case 'socket': {
node.send(msg);
break;
default:
}
default: {}
}
}
};
@ -316,7 +341,8 @@ module.exports = function(RED) {
if (wemo.get(node.dev)) {
node.status({fill: 'green',shape: 'dot',text: 'found'});
subscribe(node);
} else {
}
else {
wemo.on('discovered', function(d) {
if (node.dev === d) {
node.status({fill: 'green',shape: 'dot',text: 'found'});
@ -324,7 +350,8 @@ module.exports = function(RED) {
}
});
}
} else if (node.ipaddr) {
}
else if (node.ipaddr) {
//legacy
var devices = Object.keys(wemo.devices);
for (var d in devices) {
@ -349,6 +376,76 @@ module.exports = function(RED) {
};
RED.nodes.registerType('wemo in', wemoNGEvent);
var wemoNGLookup = function(n) {
RED.nodes.createNode(this,n);
var node = this;
node.device = n.device;
node.name = n.name;
node.dev = RED.nodes.getNode(node.device).device;
node.status({fill: 'red',shape: 'dot',text: 'searching'});
if (!wemo.get(node.dev)) {
wemo.on('discovered', function(d) {
if (node.dev === d) {
node.status({fill: 'green',shape: 'dot',text: 'found'});
}
});
}
else {
node.status({fill: 'green',shape: 'dot',text: 'found'});
}
node.on('input', function(msg) {
var dev = wemo.get(node.dev);
if (!dev) {
//need to show that dev not currently found
console.log('no device found');
return;
}
//console.log(dev.type);
if (dev.type === 'light' || dev.type === 'group') {
//console.log("light");
wemo.getLightStatus(dev)
.then(function(status) {
// if (status.available) {
var caps = status.capabilities.split(',');
var vals = status.state.split(',');
for (var i=0; i<caps.length; i++) {
if (wemo.capabilityMap.hasOwnProperty(caps[i])) {
if (vals[i] !== '' && vals[i].indexOf(':') == -1) {
status[wemo.capabilityMap[caps[i]]] = parseInt(vals[i]);
} else {
status[wemo.capabilityMap[caps[i]]] = parseInt(vals[i].substring(0,vals[i].indexOf(':')));
}
}
}
delete status.capabilities;
// }
msg.payload = status;
node.send(msg);
});
} else {
console.log("socket");
//socket
wemo.getSocketStatus(dev)
.then(function(status) {
msg.payload = {
state: status
};
node.send(msg);
});
}
});
node.on('close', function(done) {
done();
});
};
RED.nodes.registerType("wemo lookup", wemoNGLookup);
RED.httpAdmin.get('/wemoNG/devices', function(req, res) {
res.json(wemo.devices);
});

View File

@ -11,17 +11,16 @@ var url = require('url');
var Q = require('q');
var urn = 'urn:Belkin:service:basicevent:1';
var postbodyheader = [
'<?xml version="1.0" encoding="utf-8"?>',
'<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">',
'<s:Body>'].join('\n');
var postbodyfooter = ['</s:Body>',
'</s:Envelope>'
].join('\n');
var getenddevs = {};
getenddevs.path = '/upnp/control/bridge1';
getenddevs.action = '"urn:Belkin:service:bridge:1#GetEndDevices"';
@ -45,6 +44,58 @@ getcapabilities.body = [
postbodyfooter
].join('\n');
var getDevStatus = {
method: 'POST',
path: '/upnp/control/bridge1',
action: '"urn:Belkin:service:bridge:1#GetDeviceStatus"',
body: [
postbodyheader,
'<u:GetDeviceStatus xmlns:u="urn:Belkin:service:bridge:1">',
'<DeviceIDs>%s</DeviceIDs>',
'</u:GetDeviceStatus>',
postbodyfooter
].join('\n')
};
var getSocketState = {
method: 'POST',
path: '/upnp/control/basicevent1',
action: '"urn:Belkin:service:basicevent:1#GetBinaryState"',
body: [
postbodyheader,
'<u:GetBinaryState xmlns:u="urn:Belkin:service:basicevent:1">',
'</u:GetBinaryState>',
postbodyfooter
].join('\n')
}
var setdevstatus = {};
setdevstatus.path = '/upnp/control/bridge1';
setdevstatus.action = '"urn:Belkin:service:bridge:1#SetDeviceStatus"';
setdevstatus.body = [
postbodyheader,
'<u:SetDeviceStatus xmlns:u="urn:Belkin:service:bridge:1">',
'<DeviceStatusList>',
'&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;DeviceStatus&gt;&lt;IsGroupAction&gt;%s&lt;/IsGroupAction&gt;&lt;DeviceID available=&quot;YES&quot;&gt;%s&lt;/DeviceID&gt;&lt;CapabilityID&gt;%s&lt;/CapabilityID&gt;&lt;CapabilityValue&gt;%s&lt;/CapabilityValue&gt;&lt;/DeviceStatus&gt;',
'</DeviceStatusList>',
'</u:SetDeviceStatus>',
postbodyfooter
].join('\n');
var capabilityMap = {
'10006': 'state',
'10008': 'dim',
'10300': 'color',
'30301': 'temperature'
};
var reverseCapabilityMap = {
'state': '10006',
'dim': '10008',
'color': '10300',
'temperature': '30301'
};
var WeMoNG = function () {
this.devices = {};
@ -52,6 +103,10 @@ var WeMoNG = function () {
this._interval;
events.EventEmitter.call(this);
this.capabilityMap = capabilityMap;
this.reverseCapabilityMap = reverseCapabilityMap;
}
util.inherits(WeMoNG, events.EventEmitter);
@ -68,7 +123,7 @@ WeMoNG.prototype.start = function start() {
request.get(location.href, function(err, res, xml) {
if (!err) {
xml2js.parseString(xml, function(err, json) {
if (!err) {
if (!err && json && json.root) {
var device = { ip: location.hostname, port: location.port };
for (var key in json.root.device[0]) {
device[key] = json.root.device[0][key][0];
@ -248,20 +303,114 @@ WeMoNG.prototype.toggleSocket = function toggleSocket(socket, on) {
post_request.end();
}
WeMoNG.prototype.setStatus = function setStatus(light, capability, value) {
var setdevstatus = {};
setdevstatus.path = '/upnp/control/bridge1';
setdevstatus.action = '"urn:Belkin:service:bridge:1#SetDeviceStatus"';
setdevstatus.body = [
postbodyheader,
'<u:SetDeviceStatus xmlns:u="urn:Belkin:service:bridge:1">',
'<DeviceStatusList>',
'&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;DeviceStatus&gt;&lt;IsGroupAction&gt;NO&lt;/IsGroupAction&gt;&lt;DeviceID available=&quot;YES&quot;&gt;%s&lt;/DeviceID&gt;&lt;CapabilityID&gt;%s&lt;/CapabilityID&gt;&lt;CapabilityValue&gt;%s&lt;/CapabilityValue&gt;&lt;/DeviceStatus&gt;',
'</DeviceStatusList>',
'</u:SetDeviceStatus>',
postbodyfooter
].join('\n');
WeMoNG.prototype.getSocketStatus = function getSocketStatus(socket) {
var postoptions = {
host: socket.ip,
port: socket.port,
path: getSocketState.path,
method: getSocketState.method,
headers: {
'SOAPACTION': getSocketState.action,
'Content-Type': 'text/xml; charset="utf-8"',
'Accept': ''
}
}
var def = Q.defer();
var post_request = http.request(postoptions, function(res){
var data = "";
res.setEncoding('utf8');
res.on('data', function(chunk){
data += chunk;
});
res.on('end', function(){
xml2js.parseString(data, function(err, result){
if (!err) {
var status = result["s:Envelope"]["s:Body"][0]["u:GetBinaryStateResponse"][0]["BinaryState"][0];
status = parseInt(status);
def.resolve(status);
}
});
});
});
post_request.on('error', function (e) {
console.log(e);
console.log("%j", postoptions);
def.reject();
});
post_request.write(getSocketState.body);
post_request.end();
return def.promise;
};
WeMoNG.prototype.getLightStatus = function getLightStatus(light) {
var postoptions = {
host: light.ip,
port: light.port,
path: getDevStatus.path,
method: getDevStatus.method,
headers: {
'SOAPACTION': getDevStatus.action,
'Content-Type': 'text/xml; charset="utf-8"',
'Accept': ''
}
};
var def = Q.defer();
var post_request = http.request(postoptions, function(res){
var data = "";
res.setEncoding('utf8');
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
xml2js.parseString(data, function(err, result){
if(!err) {
if (result["s:Envelope"]) {
var status = result["s:Envelope"]["s:Body"][0]["u:GetDeviceStatusResponse"][0].DeviceStatusList[0];
xml2js.parseString(status, function(err,result2){
if (!err) {
var available = result2['DeviceStatusList']['DeviceStatus'][0]['DeviceID'][0]['$'].available === 'YES';
var state = result2['DeviceStatusList']['DeviceStatus'][0]['CapabilityValue'][0];
var capabilities = result2['DeviceStatusList']['DeviceStatus'][0]['CapabilityID'][0];
var obj = {
available: available,
state: state,
capabilities: capabilities
};
def.resolve(obj);
} else {
console.log("err");
}
});
}
} else {
console.log("err");
}
});
});
});
post_request.on('error', function (e) {
console.log(e);
console.log("%j", postoptions);
def.reject();
});
post_request.write(util.format(getDevStatus.body, light.id));
post_request.end();
return def.promise;
}
WeMoNG.prototype.setStatus = function setStatus(light, capability, value) {
var postoptions = {
host: light.ip,
port: light.port,
@ -269,7 +418,7 @@ WeMoNG.prototype.setStatus = function setStatus(light, capability, value) {
method: 'POST',
headers: {
'SOAPACTION': setdevstatus.action,
'Content-Type': 'text/xml; charset="utf-8"',
'Content-Type': 'text/xml; charset="utf-8"',
'Accept': ''
}
};
@ -293,7 +442,7 @@ WeMoNG.prototype.setStatus = function setStatus(light, capability, value) {
//console.log(util.format(setdevstatus.body, light.id, capability, value));
post_request.write(util.format(setdevstatus.body, light.id, capability, value));
post_request.write(util.format(setdevstatus.body, light.type === 'light'?'NO':'YES',light.id, capability, value));
post_request.end();
}
@ -312,6 +461,7 @@ WeMoNG.prototype.parseEvent = function parseEvent(evt) {
if (!err && res != null) {
msg.id = res['StateEvent']['DeviceID'][0]['_'];
msg.capability = res['StateEvent']['CapabilityId'][0];
msg.capabilityName = capabilityMap[msg.capability];
msg.value = res['StateEvent']['Value'][0];
def.resolve(msg);
}
@ -364,4 +514,27 @@ WeMoNG.prototype.rgb2xy = function rgb2xy(red, green, blue) {
];
};
//http://stackoverflow.com/questions/22894498/philips-hue-convert-xy-from-api-to-hex-or-rgb
WeMoNG.prototype.xy2rgb = function xy2rgb(x,y,bri) {
z = 1.0 - x - y;
Y = bri / 255.0; // Brightness of lamp
X = (Y / y) * x;
Z = (Y / y) * z;
r = X * 1.612 - Y * 0.203 - Z * 0.302;
g = -X * 0.509 + Y * 1.412 + Z * 0.066;
b = X * 0.026 - Y * 0.072 + Z * 0.962;
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
maxValue = Math.max(r,g,b);
r /= maxValue;
g /= maxValue;
b /= maxValue;
r = r * 255; if (r < 0) { r = 255 };
g = g * 255; if (g < 0) { g = 255 };
b = b * 255; if (b < 0) { b = 255 };
return [r,g,b];
};
module.exports = WeMoNG;

View File

@ -1,6 +1,6 @@
{
"name": "node-red-node-wemo",
"version": "0.1.9",
"version": "0.1.13",
"description": "Input and Output nodes for Belkin WeMo devices",
"repository": "https://github.com/node-red/node-red-nodes/tree/master/hardware",
"main": "WeMoNG.js",
@ -9,7 +9,8 @@
},
"keywords": [
"node-red",
"wemo"
"wemo",
"belkin"
],
"author": {
"email": "hardillb@gmail.com",
@ -18,7 +19,7 @@
},
"license": "APACHE-2.0",
"dependencies": {
"node-ssdp": "~2.6.3",
"node-ssdp": "~2.9.1",
"request": "~2.74.0",
"xml2js": "~0.4.13",
"util": "~0.10.3",

View File

@ -31,7 +31,7 @@
defaults: {
name: {value:"Emoncms"},
emonServer: {type:"emoncms-server", required:true},
nodegroup: {value:"", validate:function(v) {return ((v === undefined)||(/^\d+$/).test(v)); }}
nodegroup: {value:"", validate:function(v) {return ((v === "")||(/^[\d\w\s.-]*$/).test(v));} }
},
inputs:1,
outputs:0,

View File

@ -34,8 +34,9 @@ module.exports = function(RED) {
else {
if (msg.payload.indexOf(':') > -1) {
this.url += 'json={' + msg.payload + '}';
} else {
this.url += 'csv='+msg.payload;
}
else {
this.url += 'csv=' + msg.payload;
}
}
this.url += '&apikey='+this.apikey;
@ -96,7 +97,8 @@ module.exports = function(RED) {
if (msg.rc === 200) {
try {
msg.payload = JSON.parse(msg.payload);
} catch(err) {
}
catch(err) {
// Failed to parse, pass it on
}
node.send(msg);

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-emoncms",
"version" : "0.0.11",
"version" : "0.0.15",
"description" : "A Node-RED node to fetch/post data to/from emoncms",
"dependencies" : {
},

View File

@ -1,5 +1,5 @@
node-red-node-discover
======================
node-red-node-discovery
=======================
A <a href="http://nodered.org" target="_new">Node-RED</a> node that uses Bonjour
/ Avahi to discover local network services such as iTunes libraries, printers, etc.

View File

@ -10,13 +10,18 @@ module.exports = function(RED) {
RED.nodes.createNode(this, n);
this.topic = n.topic || "";
this.service = n.service;
var node = this;
// var sequence = [
// mdns.rst.DNSServiceResolve(),
// mdns.rst.getaddrinfo({families: [4] })
// ];
// var browser = mdns.createBrowser(this.service,{resolverSequence: sequence});
var browser = mdns.createBrowser(this.service);
var node = this;
var sequence = [
mdns.rst.DNSServiceResolve(),
'DNSServiceGetAddrInfo' in mdns.dns_sd ? mdns.rst.DNSServiceGetAddrInfo() : mdns.rst.getaddrinfo({families:[4]}),
mdns.rst.makeAddressesUnique()
];
var browser = mdns.createBrowser((this.service), {resolverSequence:sequence});
browser.on('serviceUp', function(service) {
if (RED.settings.verbose) { node.log("here : " + service.name); }
@ -30,6 +35,9 @@ module.exports = function(RED) {
var msg = {topic:node.topic, payload:service};
node.send(msg);
});
browser.on('error', function(exception) {
node.error(exception.toString());
});
browser.start();
node.on("close", function() {

View File

@ -1,16 +1,16 @@
{
"name" : "node-red-node-discovery",
"version" : "0.0.13",
"version" : "0.0.18",
"description" : "A Node-RED node that uses Bonjour / Avahi to discover nearby services.",
"dependencies" : {
"mdns" : "2.2.*"
"mdns" : "~2.3.3"
},
"repository" : {
"type":"git",
"url":"https://github.com/node-red/node-red-nodes/tree/master/io/mdns"
},
"license": "Apache-2.0",
"keywords": [ "node-red", "mdns", "avahi", "bonjour" ],
"keywords": [ "node-red", "mdns", "avahi", "bonjour", "discovery" ],
"node-red" : {
"nodes" : {
"discovery": "mdns.js"

View File

@ -22,7 +22,8 @@ module.exports = function(RED) {
this.client = mqlight.createClient(opts, function(err) {
if (err) {
util.log('[mqlight] ['+id+'] not connected to service '+n.service);
} else {
}
else {
util.log('[mqlight] ['+id+'] connected to service '+n.service);
}
});
@ -47,11 +48,10 @@ module.exports = function(RED) {
if (node.serviceConfig) {
if (node.serviceConfig.client) {
var recvClient;
var recvClient = node.serviceConfig.client;
recvClient.on("error", function(err) {
if (err) { node.error(err.toString()); }
});
recvClient = node.serviceConfig.client;
recvClient.on("started", function() {
recvClient.on("message", function(data, delivery) {
if (node.topic === delivery.destination.topicPattern) {
@ -72,13 +72,15 @@ module.exports = function(RED) {
var subscribeCallback = function(err) {
if (err) {
node.error("Failed to subscribe: " + err);
} else {
}
else {
node.log("Subscribed to "+node.topic+(node.share?" ["+node.share+"]":""));
}
};
if (node.share) {
recvClient.subscribe(node.topic, node.share, subscribeCallback);
} else {
}
else {
recvClient.subscribe(node.topic, subscribeCallback);
}
});
@ -102,18 +104,18 @@ module.exports = function(RED) {
if (node.serviceConfig) {
if (node.serviceConfig.client) {
var sendClient;
var sendClient = node.serviceConfig.client;
sendClient.on("error", function(err) {
if (err) { node.error(err.toString()); }
});
sendClient = node.serviceConfig.client;
sendClient.on("started", function () {
node.on("input", function(msg) {
var topic = node.topic;
if (topic === "") {
if (msg.topic) {
topic = msg.topic;
} else {
}
else {
node.warn("No topic set in MQ Light out node");
return;
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-mqlight",
"version" : "0.0.10",
"version" : "0.0.11",
"description" : "A Node-RED node to send and receive message from IBM MQ Light",
"dependencies" : {
"mqlight" : "1.0.x"

View File

@ -1,16 +1,16 @@
<script type="text/x-red" data-template-name="ping">
<div class="form-row">
<label for="node-input-host"><i class="fa fa-dot-circle-o"></i> Target</label>
<label for="node-input-host"><i class="fa fa-dot-circle-o"></i> <span data-i18n="ping.label.target"></span></label>
<input type="text" id="node-input-host" placeholder="www.google.com">
</div>
<div class="form-row">
<label for="node-input-timer"><i class="fa fa-repeat"></i> Ping (S)</label>
<label for="node-input-timer"><i class="fa fa-repeat"></i> <span data-i18n="ping.label.ping"></label>
<input type="text" id="node-input-timer" placeholder="20">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>

View File

@ -14,7 +14,7 @@ module.exports = function(RED) {
var ex;
if (plat == "linux") { ex = spawn('ping', ['-n', '-w', '5', '-c', '1', node.host]); }
else if (plat.match(/^win/)) { ex = spawn('ping', ['-n', '1', '-w', '5000', node.host]); }
else if (plat == "darwin") { ex = spawn('ping', ['-n', '-t', '5', '-c', '1', node.host]); }
else if (plat == "darwin" || plat == "freebsd") { ex = spawn('ping', ['-n', '-t', '5', '-c', '1', node.host]); }
else { node.error("Sorry - your platform - "+plat+" - is not recognised."); }
var res = false;
var line = "";

View File

@ -0,0 +1,8 @@
{
"ping": {
"label": {
"target": "Target",
"ping": "Ping (S)"
}
}
}

View File

@ -0,0 +1,8 @@
{
"ping": {
"label": {
"target": "対象",
"ping": "Ping (秒)"
}
}
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-ping",
"version" : "0.0.13",
"version" : "0.0.14",
"description" : "A Node-RED node to ping a remote server, for use as a keep-alive check.",
"dependencies" : {
},

View File

@ -12,7 +12,7 @@
<script type="text/x-red" data-help-name="serial in">
<p>Reads data from a local serial port.</p>
<p>Can either <ul><li>wait for a "split" character (default \n). Also accepts hex notation (0x0a).</li>
<p>Can either <ul><li>wait for a "split" character (default \n). Also accepts hex notation (0x0d).</li>
<li>Wait for a timeout in milliseconds for the first character received</li>
<li>Wait to fill a fixed sized buffer</li></ul></p>
<p>It then outputs <code>msg.payload</code> as either a UTF8 ascii string or a binary Buffer object.</p>
@ -154,6 +154,14 @@
<div class="form-tips" id="tip-bin" hidden><span data-i18n="serial.tip.timeout"></span></div>
</script>
<script type="text/x-red" data-help-name="serial-port">
<p>Provides configuration options for a serial port.</p>
<p>The search button should return a list of available serial ports to choose from, or you
can type in the location if known.</p>
<p>The input can be split on a fixed character, after a timeout, or after a fixed number of characters.</p>
<p>If using a character, it can be specified as either the character, the escaped shortcut (e.g. \n), or the hex code (e.g. 0x0d).</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial-port',{
category: 'config',

View File

@ -45,11 +45,13 @@ module.exports = function(RED) {
if (!Buffer.isBuffer(payload)) {
if (typeof payload === "object") {
payload = JSON.stringify(payload);
} else {
}
else {
payload = payload.toString();
}
payload += node.addCh;
} else if (node.addCh !== "") {
}
else if (node.addCh !== "") {
payload = Buffer.concat([payload,new Buffer(node.addCh)]);
}
node.port.write(payload,function(err,res) {
@ -66,14 +68,16 @@ module.exports = function(RED) {
node.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
});
} else {
}
else {
this.error(RED._("serial.errors.missing-conf"));
}
this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
} else {
}
else {
done();
}
});
@ -105,7 +109,8 @@ module.exports = function(RED) {
var splitc;
if (node.serialConfig.newline.substr(0,2) == "0x") {
splitc = new Buffer([parseInt(node.serialConfig.newline)]);
} else {
}
else {
splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0")); // jshint ignore:line
}
@ -169,14 +174,16 @@ module.exports = function(RED) {
this.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
});
} else {
}
else {
this.error(RED._("serial.errors.missing-conf"));
}
this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
} else {
}
else {
done();
}
});
@ -204,11 +211,11 @@ module.exports = function(RED) {
var olderr = "";
var setupSerial = function() {
obj.serial = new serialp(port,{
baudrate: baud,
databits: databits,
baudRate: baud,
dataBits: databits,
parity: parity,
stopbits: stopbits,
parser: serialp.parsers.raw,
stopBits: stopbits,
//parser: serialp.parsers.raw,
autoOpen: true
}, function(err, results) {
if (err) {
@ -249,9 +256,9 @@ module.exports = function(RED) {
obj._emitter.emit('data',d[z]);
}
});
obj.serial.on("disconnect",function() {
RED.log.error(RED._("serial.errors.disconnected",{port:port}));
});
// obj.serial.on("disconnect",function() {
// RED.log.error(RED._("serial.errors.disconnected",{port:port}));
// });
}
setupSerial();
return obj;
@ -273,7 +280,8 @@ module.exports = function(RED) {
}
catch(err) { }
delete connections[port];
} else {
}
else {
done();
}
}

View File

@ -4,16 +4,11 @@ node-red-node-serialport
<a href="http://nodered.org" target="_new">Node-RED</a> nodes to talk to
hardware Serial ports.
**Note** : The version 0.1.x of this package requires underlying serialport
v2.0.x
Earlier versions of node.js, as found on default Debian install on a Raspberry Pi, require an
updated version of npm. See below. Or you can install the older version of
this node - node-red-node-serialport@0.0.5
Install
-------
This node is usually installed by default in Node-RED so should not need to be installed manually.
Run the following command in your Node-RED user directory (typically `~/.node-red`):
npm i node-red-node-serialport

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-node-serialport",
"version" : "0.4.1",
"version" : "0.6.0",
"description" : "Node-RED nodes to talk to serial ports",
"dependencies" : {
"serialport" : "~4.0.3"
"serialport" : "^6.0.4"
},
"repository" : {
"type":"git",
@ -12,7 +12,7 @@
"license": "Apache-2.0",
"keywords": [ "node-red", "serial" ],
"node-red": {
"version": ">=0.13.0",
"version": ">=0.16.0",
"nodes": {
"serialport": "25-serial.js"
}

View File

@ -18,20 +18,108 @@ Usage
SNMP oids fetcher. Can fetch a single or comma separated list of oids. Triggered by any input.
`msg.host` may contain the host.
`msg.community` may contain the community.
`msg.oid` may contain a comma separated list of oids to search for. (no spaces)
The oids confgured in the edit config will override `msg.oid`. Leave blank if you
The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input.
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The oids configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input.
Outputs `msg.payload` containing a table of objects, and the requested `msg.oid`.
Values depends on the oids being requested.
### snmp-set
SNMP sets the value of one or more OIDs.
`msg.host` may contain the host.
`msg.community` may contain the community.
`msg.varbinds` may contain an array of varbind JSON objects e.g.:
```
msg.varbinds = [
{
"oid": "1.3.6.1.2.1.1.5.0",
"type": "OctetString",
"value": "host1"
}, {
"oid": "1.3.6.1.2.1.1.6.0",
"type": "OctetString",
"value": "somewhere"
}
];
```
Types can be:
* `Boolean`
* `Integer`
* `OctetString`
* `Null`
* `OID`
* `IpAddress`
* `Counter`
* `Gauge`
* `TimeTicks`
* `Opaque`
* `Integer32`
* `Counter32`
* `Gauge32`
* `Unsigned32`
* `Counter64`
* `NoSuchObject`
* `NoSuchInstance`
* `EndOfMibView`
The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input.
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The varbinds configured in the edit config will override `msg.varbinds`. Leave blank if you want to use `msg.varbinds` to provide input.
### snmp-table
Simple SNMP table oid fetcher. Triggered by any input.
`msg.host` may contain the host.
`msg.community` may contain the community.
`msg.oid` may contain the oid of a single table to search for.
The oid confgured in the edit config will override `msg.oid`. Leave blank if you
The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input.
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The oid configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input.
Outputs `msg.payload` containing the table of objects, and the requested `msg.oid`.
Values depends on the oids being requested.
### snmp-subtree
Simple SNMP oid subtree fetcher. Triggered by any input.
`msg.host` may contain the host.
`msg.community` may contain the community.
`msg.oid` may contain the oid of a single table to search for.
The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input.
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The oid configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input.
Outputs `msg.payload` containing the table of objects, and the requested `msg.oid`.
@ -39,4 +127,20 @@ Values depends on the oids being requested.
### snmp-walker
### snmp-subtree
Simple SNMP oid walker fetcher. Triggered by any input.
`msg.host` may contain the host.
`msg.community` may contain the community.
`msg.oid` may contain the oid of a single table to search for.
The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input.
The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input.
The oid configured in the edit config will override `msg.oid`. Leave blank if you
want to use `msg.oid` to provide input.
Outputs `msg.payload` containing the table of objects, and the requested `msg.oid`.
Values depends on the oids being requested.

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-snmp",
"version" : "0.0.9",
"version" : "0.0.15",
"description" : "A Node-RED node that looks for SNMP oids.",
"dependencies" : {
"net-snmp" : "^1.1.19"
@ -22,6 +22,7 @@
"url": "http://nodered.org"
},
"contributors": [
{ "name": "Mika Karalia" }
{ "name": "Mika Karaila" },
{ "name": "Bryan Malyn" }
]
}

View File

@ -1,8 +1,7 @@
<script type="text/x-red" data-template-name="snmp">
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
</div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
@ -28,38 +27,108 @@
<script type="text/x-red" data-help-name="snmp">
<p>Simple SNMP oid or oid list fetcher. Triggered by any input.</p>
<p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p>
<p><code>msg.oid</code> may contain a comma separated list of oids to request. (no spaces)</p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('snmp',{
RED.nodes.registerType('snmp', {
category: 'network-input',
color:"YellowGreen",
color: "YellowGreen",
defaults: {
host: {value:"",required:true},
community: {value:"public",required:true},
version: {value:"1",required:true},
oids: {value:""},
name: {value:""}
host: { value: "127.0.0.1" },
community: { value: "public" },
version: { value: "1", required: true },
oids: { value: "" },
name: { value: "" }
},
inputs:1,
outputs:1,
inputs: 1,
outputs: 1,
icon: "snmp.png",
label: function() {
return this.name||"snmp "+this.host;
label: function () {
return this.name || "snmp " + this.host;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
labelStyle: function () {
return this.name ? "node_label_italic" : "";
}
});
</script>
<script type="text/x-red" data-template-name="snmp set">
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
</div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
<input type="text" id="node-input-community" placeholder="public">
</div>
<div class="form-row">
<label for="node-input-version"><i class="fa fa-bookmark"></i> Version</label>
<select type="text" id="node-input-version" style="width: 150px;">
<option value="1">v1</option>
<option value="2c">v2c</option>
</select>
</div>
<div class="form-row">
<label for="node-input-varbinds"><i class="fa fa-tags"></i> Varbinds</label>
<textarea rows="10" cols="60" id="node-input-varbinds" placeholder="e.g. [ { &quot;oid&quot;: &quot;1.3.6.1.2.1.1.5.0&quot;,&quot;type&quot;: &quot;OctetString&quot;,&quot;value&quot;: &quot;host1&quot;},{&quot;oid&quot;: &quot;1.3.6.1.2.1.1.6.0&quot;,&quot;type&quot;: &quot;OctetString&quot;,value: &quot;somewhere&quot;}]"
style="width:70%;"></textarea>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="snmp set">
<p>Simple snmp Set node. Trigger by any input</p>
<p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p>
<p><code>msg.varbinds</code> may contain varbinds as an array of json objects containing multiple oids, types and values.
<pre>[
{
"oid": "1.3.6.1.2.1.1.5.0",
"type": "OctetString",
"value": "host1"
},
{ "oid": ... }
]</pre>
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('snmp set', {
category: 'network-input',
color: "YellowGreen",
defaults: {
host: { value: "127.0.0.1" },
community: { value: "public" },
version: { value: "1", required: true },
varbinds: { value: "" },
name: { value: "" }
},
inputs: 1,
outputs: 0,
icon: "snmp.png",
label: function () {
return this.name || "snmp set " + this.host;
},
labelStyle: function () {
return this.name ? "node_label_italic" : "";
}
});
</script>
<script type="text/x-red" data-template-name="snmp table">
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
</div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
@ -85,37 +154,40 @@
<script type="text/x-red" data-help-name="snmp table">
<p>Simple SNMP oid table fetcher. Triggered by any input.</p>
<p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p>
<p><code>msg.oid</code> may contain the oid of a table to request.</p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('snmp table',{
RED.nodes.registerType('snmp table', {
category: 'network-input',
color:"YellowGreen",
color: "YellowGreen",
defaults: {
host: {value:"",required:true},
community: {value:"public",required:true},
version: {value:"1",required:true},
oids: {value:""},
name: {value:""}
host: { value: "127.0.0.1" },
community: { value: "public" },
version: { value: "1", required: true },
oids: { value: "" },
name: { value: "" }
},
inputs:1,
outputs:1,
inputs: 1,
outputs: 1,
icon: "snmp.png",
label: function() {
return this.name||"snmp table "+this.host;
label: function () {
return this.name || "snmp table " + this.host;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
labelStyle: function () {
return this.name ? "node_label_italic" : "";
}
});
</script>
<script type="text/x-red" data-template-name="snmp subtree">
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
</div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
@ -141,39 +213,42 @@
<script type="text/x-red" data-help-name="snmp subtree">
<p>Simple SNMP oid subtree fetcher. Triggered by any input.</p>
<p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p>
<p><code>msg.oid</code> may contain the oid of a table to request.</p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('snmp subtree',{
RED.nodes.registerType('snmp subtree', {
category: 'network-input',
color:"YellowGreen",
color: "YellowGreen",
defaults: {
host: {value:"",required:true},
community: {value:"public",required:true},
version: {value:"1",required:true},
oids: {value:""},
name: {value:""}
host: { value: "127.0.0.1" },
community: { value: "public" },
version: { value: "1", required: true },
oids: { value: "" },
name: { value: "" }
},
inputs:1,
outputs:1,
inputs: 1,
outputs: 1,
icon: "snmp.png",
label: function() {
return this.name||"snmp subtree "+this.host;
label: function () {
return this.name || "snmp subtree " + this.host;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
labelStyle: function () {
return this.name ? "node_label_italic" : "";
}
});
</script>
<script type="text/x-red" data-template-name="snmp walker">
<div class="form-row">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
<label for="node-input-host"><i class="fa fa-globe"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost">
</div>
<div class="form-row">
<label for="node-input-community"><i class="fa fa-user"></i> Community</label>
@ -199,29 +274,32 @@
<script type="text/x-red" data-help-name="snmp walker">
<p>Simple SNMP oid walker fetcher. Triggered by any input.</p>
<p><code>msg.host</code> may contain the host.</p>
<p><code>msg.community</code> may contain the community.</p>
<p><code>msg.oid</code> may contain the oid of a table to request.</p>
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('snmp walker',{
RED.nodes.registerType('snmp walker', {
category: 'network-input',
color:"YellowGreen",
color: "YellowGreen",
defaults: {
host: {value:"",required:true},
community: {value:"public",required:true},
version: {value:"1",required:true},
oids: {value:""},
name: {value:""}
host: { value: "127.0.0.1" },
community: { value: "public" },
version: { value: "1", required: true },
oids: { value: "" },
name: { value: "" }
},
inputs:1,
outputs:1,
inputs: 1,
outputs: 1,
icon: "snmp.png",
label: function() {
return this.name||"snmp walker "+this.host;
label: function () {
return this.name || "snmp walker " + this.host;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
labelStyle: function () {
return this.name ? "node_label_italic" : "";
}
});
</script>
</script>

View File

@ -1,27 +1,39 @@
module.exports = function(RED) {
module.exports = function (RED) {
"use strict";
var snmp = require("net-snmp");
var sessions = {};
function getSession(host, community, version) {
var sessionKey = host + ":" + community + ":" + version;
if (!(sessionKey in sessions)) {
sessions[sessionKey] = snmp.createSession(host, community, { version: version });
}
return sessions[sessionKey];
}
function SnmpNode(n) {
RED.nodes.createNode(this,n);
this.community = n.community || "public";
this.host = n.host || "127.0.0.1";
RED.nodes.createNode(this, n);
this.community = n.community;
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.oids = n.oids.replace(/\s/g,"");
this.session = snmp.createSession(this.host, this.community, {version: this.version});
this.oids = n.oids.replace(/\s/g, "");
var node = this;
this.on("input",function(msg) {
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var oids = node.oids || msg.oid;
if (oids) {
node.session.get(oids.split(","), function(error, varbinds) {
getSession(host, community, node.version).get(oids.split(","), function (error, varbinds) {
if (error) {
node.error(error.toString(),msg);
} else {
node.error(error.toString(), msg);
}
else {
for (var i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]),msg);
node.error(snmp.varbindError(varbinds[i]), msg);
}
else {
if (varbinds[i].type == 4) { varbinds[i].value = varbinds[i].value.toString(); }
@ -40,17 +52,56 @@ module.exports = function(RED) {
}
});
}
RED.nodes.registerType("snmp",SnmpNode);
RED.nodes.registerType("snmp", SnmpNode);
function SnmpSNode(n) {
RED.nodes.createNode(this, n);
this.community = n.community;
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.varbinds = n.varbinds;
var node = this;
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var varbinds = (node.varbinds) ? JSON.parse(node.varbinds) : msg.varbinds;
if (varbinds) {
for (var i = 0; i < varbinds.length; i++) {
varbinds[i].type = snmp.ObjectType[varbinds[i].type];
}
getSession(host, community, node.version).set(varbinds, function (error, varbinds) {
if (error) {
node.error(error.toString(), msg);
}
else {
for (var i = 0; i < varbinds.length; i++) {
// for version 2c we must check each OID for an error condition
if (snmp.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]), msg);
}
}
}
});
}
else {
node.warn("No varbinds to set");
}
});
}
RED.nodes.registerType("snmp set", SnmpSNode);
function SnmpTNode(n) {
RED.nodes.createNode(this,n);
this.community = n.community || "public";
this.host = n.host || "127.0.0.1";
RED.nodes.createNode(this, n);
this.community = n.community;
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.oids = n.oids.replace(/\s/g,"");
this.session = snmp.createSession(this.host, this.community, {version: this.version});
this.oids = n.oids.replace(/\s/g, "");
var node = this;
var msg;
var maxRepetitions = 20;
function sortInt(a, b) {
@ -59,143 +110,141 @@ module.exports = function(RED) {
else { return 0; }
}
function responseCb(error, table) {
if (error) {
le.error(error.toString());
} else {
var indexes = [];
for (var index in table) {
if (table.hasOwnProperty(index)) {
indexes.push(parseInt(index));
}
}
indexes.sort(sortInt);
for (var i = 0; i < indexes.length; i++) {
var columns = [];
for (var column in table[indexes[i]]) {
if (table[indexes[i]].hasOwnProperty(column)) {
columns.push(parseInt(column));
}
}
columns.sort(sortInt);
// console.log("row index = " + indexes[i]);
// for (var j = 0; j < columns.length; j++) {
// console.log(" column " + columns[j] + " = " + table[indexes[i]][columns[j]]);
// }
}
msg.payload = table;
node.send(msg);
}
}
this.on("input",function(m) {
msg = m;
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var oids = node.oids || msg.oid;
if (oids) {
msg.oid = oids;
node.session.table(oids, maxRepetitions, responseCb);
getSession(host, community, node.version).table(oids, maxRepetitions, function (error, table) {
if (error) {
node.error(error.toString(), msg);
}
else {
var indexes = [];
for (var index in table) {
if (table.hasOwnProperty(index)) {
indexes.push(parseInt(index));
}
}
indexes.sort(sortInt);
for (var i = 0; i < indexes.length; i++) {
var columns = [];
for (var column in table[indexes[i]]) {
if (table[indexes[i]].hasOwnProperty(column)) {
columns.push(parseInt(column));
}
}
columns.sort(sortInt);
// console.log("row index = " + indexes[i]);
// for (var j = 0; j < columns.length; j++) {
// console.log(" column " + columns[j] + " = " + table[indexes[i]][columns[j]]);
// }
}
msg.payload = table;
node.send(msg);
}
});
}
else {
node.warn("No oid to search for");
}
});
}
RED.nodes.registerType("snmp table",SnmpTNode);
RED.nodes.registerType("snmp table", SnmpTNode);
function SnmpSubtreeNode(n) {
RED.nodes.createNode(this,n);
this.community = n.community || "public";
this.host = n.host || "127.0.0.1";
RED.nodes.createNode(this, n);
this.community = n.community;
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.oids = n.oids.replace(/\s/g,"");
this.session = snmp.createSession(this.host, this.community, {version: this.version});
this.oids = n.oids.replace(/\s/g, "");
var node = this;
var maxRepetitions = 20;
var response = [];
function doneCb(error) {
if (error) {
console.error(error.toString());
}
else {
var msg = {};
msg.payload = response;
node.send(msg);
response.clear();
}
}
function feedCb(varbinds) {
for (var i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]));
node.error(snmp.varbindError(varbinds[i]), msg);
}
else {
//console.log(varbinds[i].oid + "|" + varbinds[i].value);
response.add({oid: varbinds[i].oid, value: varbinds[i].value});
response.push({ oid: varbinds[i].oid, value: varbinds[i].value });
}
}
}
this.on("input",function(msg) {
this.on("input", function (msg) {
var host = node.host || msg.host;
var community = node.community || msg.community;
var oids = node.oids || msg.oid;
if (oids) {
msg.oid = oids;
node.session.subtree(msg.oid, maxRepetitions, feedCb, doneCb);
//node.session.subtree(oids, maxRepetitions, responseCb);
getSession(host, community, node.version).subtree(msg.oid, maxRepetitions, feedCb, function (error) {
if (error) {
node.error(error.toString(), msg);
}
else {
msg.payload = response;
node.send(msg);
//Clears response
response.length = 0;
}
});
}
else {
node.warn("No oid to search for");
}
});
}
RED.nodes.registerType("snmp subtree",SnmpSubtreeNode);
RED.nodes.registerType("snmp subtree", SnmpSubtreeNode);
function SnmpWalkerNode(n) {
RED.nodes.createNode(this,n);
this.community = n.community || "public";
this.host = n.host || "127.0.0.1";
RED.nodes.createNode(this, n);
this.community = n.community;
this.host = n.host;
this.version = (n.version === "2c") ? snmp.Version2c : snmp.Version1;
this.oids = n.oids.replace(/\s/g,"");
this.session = snmp.createSession(this.host, this.community, {version: this.version});
this.oids = n.oids.replace(/\s/g, "");
var node = this;
var maxRepetitions = 20;
var response = [];
function doneCb(error) {
if (error) {
node.error(error.toString());
}
else {
var msg = {};
msg.payload = response;
node.send(msg);
response.clear();
}
}
function feedCb(varbinds) {
for (var i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) {
node.error(snmp.varbindError(varbinds[i]));
node.error(snmp.varbindError(varbinds[i]), msg);
}
else {
//console.log(varbinds[i].oid + "|" + varbinds[i].value);
response.add({oid: varbinds[i].oid, value: varbinds[i].value});
response.push({ oid: varbinds[i].oid, value: varbinds[i].value });
}
}
}
this.on("input",function(msg) {
this.on("input", function (msg) {
node.msg = msg;
var oids = node.oids || msg.oid;
var host = node.host || msg.host;
var community = node.community || msg.community;
if (oids) {
msg.oid = oids;
node.session.walk(msg.oid, maxRepetitions, feedCb, doneCb);
getSession(host, community, node.version).walk(msg.oid, maxRepetitions, feedCb, function (error) {
if (error) {
node.error(error.toString(), msg);
}
else {
msg.payload = response;
node.send(msg);
//Clears response
response.length = 0;
}
});
}
else {
node.warn("No oid to search for");
}
});
}
RED.nodes.registerType("snmp walker",SnmpWalkerNode);
RED.nodes.registerType("snmp walker", SnmpWalkerNode);
};

View File

@ -41,7 +41,7 @@ module.exports = function(RED) {
}
};
if (this.serverConfig.vhost) {
this.stompClientOpts.vhost = this.serverConfig.vhost;
this.stompClientOpts.vhost = this.serverConfig.vhost;
}
var node = this;
@ -49,7 +49,7 @@ module.exports = function(RED) {
node.client = new StompClient(node.stompClientOpts);
node.client.on("connect", function() {
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({fill:"green",shape:"dot",text:"connected"});
});
node.client.on("reconnecting", function() {
@ -110,14 +110,14 @@ module.exports = function(RED) {
}
};
if (this.serverConfig.vhost) {
this.stompClientOpts.vhost = this.serverConfig.vhost;
this.stompClientOpts.vhost = this.serverConfig.vhost;
}
var node = this;
node.client = new StompClient(node.stompClientOpts);
node.client.on("connect", function() {
node.status({fill:"green",shape:"dot",text:"connected"});
node.status({fill:"green",shape:"dot",text:"connected"});
});
node.client.on("reconnecting", function() {

View File

@ -22,7 +22,8 @@ module.exports = function(RED) {
node.log("sent WOL magic packet");
}
});
} catch(e) {
}
catch(e) {
if (RED.settings.verbose) { node.log("WOL: socket error"); }
}
}

2729
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +1,50 @@
{
"name" : "node-red-nodes",
"version" : "0.0.8",
"description" : "Node-RED-nodes package to hold the test framework ONLY - use npm to install individual nodes",
"homepage" : "http://nodered.org",
"license" : "Apache-2.0",
"repository" : {
"type":"git",
"url":"https://github.com/node-red/node-red-nodes.git"
"name": "node-red-nodes",
"version": "0.0.10",
"description": "Node-RED-nodes package to hold the test framework ONLY - use npm to install individual nodes",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red-nodes.git"
},
"contributors": [
{"name": "Dave Conway-Jones"},
{"name": "Nick O'Leary"},
{"name": "Ben Hardill"}
{
"name": "Dave Conway-Jones"
},
{
"name": "Nick O'Leary"
},
{
"name": "Ben Hardill"
}
],
"keywords": [
"Node-RED", "nodes", "iot", "ibm", "flow"
"node-red",
"nodes",
"iot",
"ibm",
"flow"
],
"devDependencies": {
"grunt": "^0.4.5",
"grunt-simple-mocha": "^0.4.1",
"grunt-contrib-jshint": "^1.0.0",
"grunt-lint-inline": "^0.4.4",
"grunt-jscs": "^2.8.0",
"mocha": "^2.4.5",
"should": "^8.3.1",
"sinon": "^1.17.3",
"supertest": "^1.2.0",
"proxyquire": "^1.7.10",
"pushbullet": "^1.4.3",
"when": "^3.7.7",
"exif": "^0.6.0",
"grunt": "^1.0.1",
"grunt-contrib-jshint": "^1.1.0",
"grunt-jscs": "^3.0.1",
"grunt-lint-inline": "^1.0.0",
"grunt-simple-mocha": "^0.4.1",
"imap": "^0.8.19",
"mailparser": "^0.6.1",
"mocha": "~3.5.3",
"msgpack-lite": "^0.1.26",
"ngeohash": "^0.6.0",
"nodemailer" : "^1.11.0",
"poplib" : "^0.1.7",
"mailparser" : "^0.6.1",
"imap" : "^0.8.18",
"msgpack-js": "^0.3.0"
},
"engines": {
"node": ">=0.10"
"nodemailer": "^1.11.0",
"poplib": "^0.1.7",
"proxyquire": "^1.7.11",
"pushbullet": "~2.1.0",
"should": "^11.2.1",
"sinon": "^1.17.7",
"supertest": "^3.0.0",
"when": "^3.7.8"
}
}

Some files were not shown because too many files have changed in this diff Show More