mirror of
https://github.com/node-red/node-red-nodes.git
synced 2025-03-01 10:37:43 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
6f13776646
20
.jscsrc
20
.jscsrc
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
10
.travis.yml
10
.travis.yml
@ -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
|
||||
|
@ -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.
|
||||
|
35
Gruntfile.js
35
Gruntfile.js
@ -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']);
|
||||
};
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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.*"
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -35,7 +35,7 @@
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
low: {value:"1"},
|
||||
high: {value:"6"},
|
||||
high: {value:"10"},
|
||||
inte: {value:"true"}
|
||||
},
|
||||
inputs:1,
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
},
|
||||
|
27
function/rbe/locales/ja/rbe.json
Normal file
27
function/rbe/locales/ja/rbe.json
Normal 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": "ペイロードに数値が含まれていません"
|
||||
}
|
||||
}
|
||||
}
|
@ -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" : {
|
||||
},
|
||||
|
@ -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"> </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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 α 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 α 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,
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -33,5 +33,7 @@ the more the smoothing. E.g. a value of 10 is similar to an α 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.
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
-----
|
||||
|
@ -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",
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -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"); }
|
||||
}
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
177
hardware/pigpiod/LICENSE
Normal 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
|
65
hardware/pigpiod/README.md
Normal file
65
hardware/pigpiod/README.md
Normal 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 ↑ or the pulldown resistor ↓.
|
||||
|
||||
### 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.
|
57
hardware/pigpiod/locales/en-US/pi-gpiod.json
Normal file
57
hardware/pigpiod/locales/en-US/pi-gpiod.json
Normal 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__"
|
||||
}
|
||||
}
|
||||
}
|
29
hardware/pigpiod/package.json
Normal file
29
hardware/pigpiod/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
527
hardware/pigpiod/pi-gpiod.html
Normal file
527
hardware/pigpiod/pi-gpiod.html
Normal 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>
|
||||
<span data-i18n="pi-gpiod.label.debounce"></span>
|
||||
<input type="text" id="node-input-debounce" style="width:47px; text-align:right"/> mS
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </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 ↑ or the pulldown resistor ↓.</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> <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> <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
|
||||
|
||||
<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> </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"> </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>
|
167
hardware/pigpiod/pi-gpiod.js
Normal file
167
hardware/pigpiod/pi-gpiod.js
Normal 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);
|
||||
}
|
@ -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",
|
||||
|
@ -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<axis></code></p>
|
||||
<p>Format: <code>F<axis></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>
|
||||
|
||||
|
@ -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(","));
|
||||
|
@ -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:
|
||||
|
@ -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(","));
|
||||
|
@ -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() {});
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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`)
|
||||
|
@ -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.*"
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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]) )) {
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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>',
|
||||
'<?xml version="1.0" encoding="UTF-8"?><DeviceStatus><IsGroupAction>%s</IsGroupAction><DeviceID available="YES">%s</DeviceID><CapabilityID>%s</CapabilityID><CapabilityValue>%s</CapabilityValue></DeviceStatus>',
|
||||
'</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>',
|
||||
'<?xml version="1.0" encoding="UTF-8"?><DeviceStatus><IsGroupAction>NO</IsGroupAction><DeviceID available="YES">%s</DeviceID><CapabilityID>%s</CapabilityID><CapabilityValue>%s</CapabilityValue></DeviceStatus>',
|
||||
'</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;
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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" : {
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 = "";
|
||||
|
8
io/ping/locales/en-US/88-ping.json
Normal file
8
io/ping/locales/en-US/88-ping.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"ping": {
|
||||
"label": {
|
||||
"target": "Target",
|
||||
"ping": "Ping (S)"
|
||||
}
|
||||
}
|
||||
}
|
8
io/ping/locales/ja/88-ping.json
Normal file
8
io/ping/locales/ja/88-ping.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"ping": {
|
||||
"label": {
|
||||
"target": "対象",
|
||||
"ping": "Ping (秒)"
|
||||
}
|
||||
}
|
||||
}
|
@ -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" : {
|
||||
},
|
||||
|
@ -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',
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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" }
|
||||
]
|
||||
}
|
||||
|
@ -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. [ { "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"}]"
|
||||
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>
|
241
io/snmp/snmp.js
241
io/snmp/snmp.js
@ -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);
|
||||
};
|
||||
|
@ -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() {
|
||||
|
@ -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
2729
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
71
package.json
71
package.json
@ -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
Loading…
x
Reference in New Issue
Block a user