mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
334 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b2f4bc915e | ||
|
13deef189d | ||
|
b5a8a7288b | ||
|
b6fd103b37 | ||
|
6a17a7d4c2 | ||
|
c39f4f9738 | ||
|
c20128b80f | ||
|
0b7fa1ab5c | ||
|
775297d625 | ||
|
d00624f9e3 | ||
|
d702caa5be | ||
|
729036ec0b | ||
|
eee8f89146 | ||
|
4ae5f34d2e | ||
|
18ae7108f5 | ||
|
b8bcd57cda | ||
|
0a2dab67c7 | ||
|
fa275646a1 | ||
|
4219681cfa | ||
|
a386c028b0 | ||
|
9ad4d50442 | ||
|
653c02bb15 | ||
|
d5b36fcadc | ||
|
e9c6501771 | ||
|
7290512794 | ||
|
be5f6762f7 | ||
|
1ea023e8ef | ||
|
896b52ed9e | ||
|
92d10384ba | ||
|
de9ee37b42 | ||
|
ae02cf8d71 | ||
|
df0ecbaf3a | ||
|
96ed3055bf | ||
|
10d9dee4aa | ||
|
8a646f73b3 | ||
|
a08789a086 | ||
|
e38b321c33 | ||
|
d784889b75 | ||
|
43073de10b | ||
|
069f42f0c5 | ||
|
0b8e8de260 | ||
|
61285a0ee8 | ||
|
789d64f7ed | ||
|
572a6156d6 | ||
|
214338eb62 | ||
|
bcefa6c9ef | ||
|
c52db897b3 | ||
|
fa2d2771a7 | ||
|
6cc0df75a6 | ||
|
c1502663b4 | ||
|
ea2c0da163 | ||
|
1bdfd920cd | ||
|
22db06046b | ||
|
9a7042b8dc | ||
|
e7dcdb075a | ||
|
1d23cdad9f | ||
|
1f6155f118 | ||
|
a3fa6dada5 | ||
|
e2bac40b17 | ||
|
99ab6eaafd | ||
|
8b138ff2c8 | ||
|
c24cf9c1c2 | ||
|
cefa0ae5b6 | ||
|
79f8d057a1 | ||
|
e5e457a410 | ||
|
0409a14a84 | ||
|
4e41db15e9 | ||
|
4e00ab3b2d | ||
|
2a0491542d | ||
|
0b516a83db | ||
|
bc8683f40a | ||
|
4dce130acb | ||
|
f666b0e6f9 | ||
|
015adb3dfd | ||
|
15d642c55d | ||
|
d87cc471a0 | ||
|
d5ad113d1b | ||
|
15002f6872 | ||
|
5e58cc9fc1 | ||
|
77f1ee9f64 | ||
|
f7792c66b4 | ||
|
68d5ebf388 | ||
|
1c655b5945 | ||
|
ebcf539795 | ||
|
e403924d2b | ||
|
325600ea61 | ||
|
fb5b45c655 | ||
|
6d4a7c73b5 | ||
|
3ac0ea75f4 | ||
|
a0aec3f8f1 | ||
|
f81ebf0e64 | ||
|
a44104a7e4 | ||
|
715fb6e7f4 | ||
|
f7a72a48ea | ||
|
58774c366d | ||
|
0bc4a3bbb1 | ||
|
46765d5737 | ||
|
309e5f4921 | ||
|
ed9ce1bb3c | ||
|
d97e23947d | ||
|
b4ef1d354d | ||
|
d8f2f24b44 | ||
|
ec0b5da29c | ||
|
19d5709e2a | ||
|
af50fe876f | ||
|
da167c8607 | ||
|
560d106ba2 | ||
|
7af88f63f5 | ||
|
b8953abb28 | ||
|
6b278fdceb | ||
|
09f162d933 | ||
|
b7e3e2d739 | ||
|
7c24d4d760 | ||
|
e6cf783d52 | ||
|
eb90d96d65 | ||
|
35fb4bb47a | ||
|
62389b487f | ||
|
c9374532a9 | ||
|
75fc46c05d | ||
|
79d4b32e3f | ||
|
6756e964fd | ||
|
7c24c7465a | ||
|
20de0c7c89 | ||
|
cef652eef7 | ||
|
ae03562f86 | ||
|
f3f52fa586 | ||
|
40232f95ed | ||
|
b1de42b297 | ||
|
bdd9d901ec | ||
|
45cb1016cc | ||
|
ad1f967a8d | ||
|
2afe474ec8 | ||
|
e19f2956a8 | ||
|
b882846516 | ||
|
c47c72cf48 | ||
|
d52cd1ce00 | ||
|
f79fdc66e0 | ||
|
3dee0f1e20 | ||
|
3a2ed39b51 | ||
|
cfd8d137cf | ||
|
1bdbd6a5b0 | ||
|
8178ab3415 | ||
|
a6d5d6ca82 | ||
|
288b129ec3 | ||
|
3c41b2624a | ||
|
aa044970c9 | ||
|
bdef2a5b96 | ||
|
ad675c00d8 | ||
|
82f58393c7 | ||
|
08559838cc | ||
|
79aeeea640 | ||
|
6b3010f95b | ||
|
10b7f402c3 | ||
|
cbad188be8 | ||
|
b652d26b6b | ||
|
e5536b848a | ||
|
5219d08cb8 | ||
|
9839e87580 | ||
|
982ad91581 | ||
|
1c010c568d | ||
|
0046164689 | ||
|
4e3594d617 | ||
|
24c373ecc2 | ||
|
74f43f4059 | ||
|
44e920fde2 | ||
|
1ebc5979aa | ||
|
b411d59d43 | ||
|
231f8b6a4d | ||
|
b81f251023 | ||
|
00202a3930 | ||
|
e0921f84c4 | ||
|
dafb2f1d38 | ||
|
6b2e666600 | ||
|
b7531bae4d | ||
|
2ba5e0fe3e | ||
|
a9668a1999 | ||
|
c6264e8040 | ||
|
a03b4e4dd4 | ||
|
655e777a3e | ||
|
5d43334b1c | ||
|
15669b7f1f | ||
|
95b8600da7 | ||
|
48d37df199 | ||
|
73f3ea52a5 | ||
|
02df584af6 | ||
|
751ac7b9ee | ||
|
344660dfee | ||
|
4fff3ce448 | ||
|
ac884bfdf3 | ||
|
3984b6b702 | ||
|
cce5f33a97 | ||
|
f22cd381ee | ||
|
fae34f8244 | ||
|
67e16adfd0 | ||
|
d2ce6af486 | ||
|
4475e74187 | ||
|
ce7bf78349 | ||
|
5767478871 | ||
|
7eae669a34 | ||
|
f44272877e | ||
|
4b3f26bed5 | ||
|
a4a3322048 | ||
|
0507578c98 | ||
|
399617dc58 | ||
|
0bc0dc3a2b | ||
|
9690ebe9c1 | ||
|
ab04fcf7c0 | ||
|
7040aaa179 | ||
|
796080471d | ||
|
83072dcda4 | ||
|
3982dcdaf1 | ||
|
0a78838c71 | ||
|
873974478a | ||
|
c1d495b62a | ||
|
cb8a3f064e | ||
|
9104b4200a | ||
|
f051fbd1e1 | ||
|
f2ed2365cd | ||
|
8176506d72 | ||
|
e88dcd4aba | ||
|
88be896f1c | ||
|
7463ef92cb | ||
|
0aa17662f5 | ||
|
ff8db09fd9 | ||
|
3054b04378 | ||
|
1967046cc8 | ||
|
e1dbb95396 | ||
|
6a4aa1ff21 | ||
|
a0aed93c69 | ||
|
48c4786d66 | ||
|
2028880b48 | ||
|
620af84088 | ||
|
72f72e8a50 | ||
|
f5284f5e1f | ||
|
1fc4a65307 | ||
|
cbe57aa96c | ||
|
3797ace89b | ||
|
7d2195d95c | ||
|
e703fa1b6b | ||
|
f7fc0760ca | ||
|
3c32186a9d | ||
|
f2b7fada9d | ||
|
c17687e5db | ||
|
e700a11647 | ||
|
48dabffefc | ||
|
affcc8ae65 | ||
|
82b863805d | ||
|
d2208fae83 | ||
|
2a5f4abd49 | ||
|
11523a6ced | ||
|
510fab7b8f | ||
|
3a52397744 | ||
|
851c2ab089 | ||
|
e4353a22bc | ||
|
5b69dfb2f2 | ||
|
ca72e187f9 | ||
|
e2a532434e | ||
|
8426c9802b | ||
|
848a69dc26 | ||
|
1536dcdf1e | ||
|
07a5d3626e | ||
|
29734dd994 | ||
|
f3a84eacf3 | ||
|
e9a64f7bdf | ||
|
89dc15567d | ||
|
95bef6b6ca | ||
|
22f46a4317 | ||
|
a2e77471a9 | ||
|
cb6fbf29a8 | ||
|
3b49c85a8e | ||
|
8f71ee4631 | ||
|
25596b06b1 | ||
|
d9ed5b46c4 | ||
|
2e92b9a120 | ||
|
09348eb353 | ||
|
ff5f2da7e7 | ||
|
b12b02ebb9 | ||
|
8891548909 | ||
|
bbc1b70a5a | ||
|
80e96d9a01 | ||
|
8befd44195 | ||
|
bc878b40d6 | ||
|
76def0a320 | ||
|
cbc12d783c | ||
|
426e866113 | ||
|
b71645f8ea | ||
|
60652d2095 | ||
|
e02189d092 | ||
|
a16c0835fd | ||
|
37c89a7796 | ||
|
12d8d45f83 | ||
|
c3258e91d6 | ||
|
a8f5d6b9ee | ||
|
419b044a12 | ||
|
9c72b65611 | ||
|
2e15944b20 | ||
|
8d14ae888f | ||
|
948cbc537e | ||
|
87fdc74ed0 | ||
|
deef63334f | ||
|
c193779f67 | ||
|
c9344cd5f1 | ||
|
386520e65b | ||
|
488a039781 | ||
|
c810edc10e | ||
|
adc6b44840 | ||
|
3604286793 | ||
|
6fb8506722 | ||
|
a4160a6bea | ||
|
f1f00da1a8 | ||
|
f3e33f4c29 | ||
|
fa3c219685 | ||
|
9e072cf182 | ||
|
30f3a46d46 | ||
|
ec09dc524a | ||
|
dc0d62cb28 | ||
|
dce09f318f | ||
|
ae13db90a9 | ||
|
3b60e1a0e3 | ||
|
fda72d2b53 | ||
|
ce4b5ee237 | ||
|
612dd4dc7f | ||
|
56e14a81b4 | ||
|
31a3d1e91b | ||
|
fa8dcdc87f | ||
|
f80cbf729a | ||
|
c4f30a6111 | ||
|
e775346946 | ||
|
778e0d2086 | ||
|
f8e8bf22c0 | ||
|
547ae0cd72 | ||
|
b1c9e95209 | ||
|
294224de9b | ||
|
426444b042 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
node_modules
|
||||
credentials.json
|
||||
flows*.json
|
||||
nodes/node-red-nodes/
|
||||
.npm
|
||||
|
5
.travis.yml
Normal file
5
.travis.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
|
82
CONTRIBUTING.md
Normal file
82
CONTRIBUTING.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Contributing to Node-RED
|
||||
|
||||
We welcome contributions, but request you follow these guidelines.
|
||||
|
||||
## Raising issues
|
||||
|
||||
Please raise any bug reports on the project's
|
||||
[issue tracker](https://github.com/node-red/node-red/issues?state=open). Be sure to
|
||||
search the list to see if your issue has already been raised.
|
||||
|
||||
A good bug report is one that make it easy for us to understand what you were
|
||||
trying to do and what went wrong.
|
||||
|
||||
Provide as much context as possible so we can try to recreate the issue.
|
||||
If possible, include the relevant part of your flow. To do this, select the
|
||||
relevant nodes, press Ctrl-E and copy the flow data from the Export dialog.
|
||||
|
||||
At a minimum, please include:
|
||||
|
||||
- Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly.
|
||||
- Version of node.js - what does `node -v` say?
|
||||
|
||||
|
||||
## New features
|
||||
|
||||
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
|
||||
|
||||
## Pull-Requests
|
||||
|
||||
### Changes to existing code
|
||||
if you want to raise a pull-request with a new feature, or a refactoring
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
|
||||
|
||||
### New nodes
|
||||
|
||||
The plugin nature of Node-RED means anyone can create a new node to extend
|
||||
its capabilities.
|
||||
|
||||
We want to avoid duplication as that can lead to confusion. Many of our existing
|
||||
nodes offer a starting point of functionality. If they are missing features,
|
||||
we would rather extend them than add separate 'advanced' versions. But the key
|
||||
to that approach is getting the UX right to not lose the simplicity.
|
||||
|
||||
To contribute a new node, please raise a pull-request against the
|
||||
`node-red-nodes` repository.
|
||||
|
||||
Eventually, the nodes will be npm-installable, but we're not there yet. We'll
|
||||
also have some sort of registry of nodes to help with discoverability.
|
||||
|
||||
### 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`/etc, closing brace on its
|
||||
own line.
|
||||
|
||||
### Contributor License Aggreement
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
24
Gruntfile.js
Normal file
24
Gruntfile.js
Normal file
@@ -0,0 +1,24 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
simplemocha: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reporter: 'tap'
|
||||
},
|
||||
all: { src: ['test/*.js'] }
|
||||
}
|
||||
});
|
||||
|
||||
// Load the plugin that provides the "uglify" task.
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
|
||||
// Default task(s).
|
||||
grunt.registerTask('default', ['simplemocha']);
|
||||
|
||||
};
|
49
README.md
49
README.md
@@ -1,5 +1,7 @@
|
||||
# Node-RED
|
||||
|
||||
http://nodered.org
|
||||
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
||||

|
||||
@@ -30,54 +32,17 @@ list.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Reporting issues
|
||||
|
||||
Please raise any bug reports on the project's [issue tracker](https://github.com/node-red/node-red/issues?state=open).
|
||||
Be sure to search the list to see if your issue has already been raised.
|
||||
|
||||
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red)
|
||||
first.
|
||||
|
||||
### Creating new nodes
|
||||
|
||||
The plugin nature of Node-RED means anyone can create a new node to extend
|
||||
its capabilities.
|
||||
|
||||
We want to avoid duplication as that can lead to confusion. Many of our existing
|
||||
nodes offer a starting point of functionality. If they are missing features,
|
||||
we would rather extend them than add separate 'advanced' versions. But the key
|
||||
to that approach is getting the UX right to not lose the simplicity.
|
||||
|
||||
We are also going to be quite selective over what nodes are included in the main
|
||||
repository - enough to be useful, but not so many that new user is overwhelmed.
|
||||
|
||||
To contribute a new node, please raise a pull-request against the
|
||||
`node-red-nodes` repository.
|
||||
|
||||
Eventually, the nodes will be npm-installable, but we're not there yet. We'll
|
||||
also have some sort of registry of nodes to help with discoverability.
|
||||
|
||||
### Pull-Requests
|
||||
|
||||
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.
|
||||
|
||||
Once you have created a pull-request, we'll provide a link to the appropriate
|
||||
CLA document.
|
||||
|
||||
If you are an IBMer, please contact us directly as the contribution process is
|
||||
slightly different.
|
||||
Please see our [contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Authors
|
||||
|
||||
Node-RED is a creation of the IBM Emerging Technology Services team.
|
||||
Node-RED is a creation of [IBM Emerging Technology](http://ibm.com/blogs/et).
|
||||
|
||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||
|
||||
For more open-source projects from IBM, head over [here](http://ibm.github.io).
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright 2013 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
Copyright 2013, 2014 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
Copyright 2014 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -28,14 +28,15 @@
|
||||
<!-- Generally, there should be an input for each property of the node. -->
|
||||
<!-- The for and id attributes identify the corresponding property -->
|
||||
<!-- (with the 'node-input-' prefix). -->
|
||||
<!-- The available icon classes are defined in Twitter Bootstrap -->
|
||||
<!-- The available icon classes are defined Twitter Bootstrap glyphicons -->
|
||||
<div class="form-row">
|
||||
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
|
||||
<input type="text" id="node-input-topic" placeholder="Topic">
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<!-- By convention, most nodes have a 'name' property. The following div -->
|
||||
<!-- provides the necessary field. -->
|
||||
<!-- provides the necessary field. Should always be the last option -->
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
@@ -64,10 +65,11 @@
|
||||
name: {value:""}, // along with default values.
|
||||
topic: {value:"", required:true}
|
||||
},
|
||||
inputs:0, // set the number of inputs - only 0 or 1
|
||||
outputs:1, // set the number of outputs - 0 to n
|
||||
icon: "arrow-in.png", // set the icon (held in public/icons)
|
||||
label: function() { // sets the default label contents
|
||||
inputs:0, // set the number of inputs - only 0 or 1
|
||||
outputs:1, // set the number of outputs - 0 to n
|
||||
// set the icon (held in icons dir below where you save the node)
|
||||
icon: "myicon.png", // saved in icons/myicon.png
|
||||
label: function() { // sets the default label contents
|
||||
return this.name||this.topic||"sample";
|
||||
},
|
||||
labelStyle: function() { // sets the class to apply to the label
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2014 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@
|
||||
// Sample Node-RED node file
|
||||
|
||||
// Require main module
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
// The main node definition - most things happen in here
|
||||
function SampleNode(n) {
|
||||
@@ -41,10 +41,10 @@ function SampleNode(n) {
|
||||
this.send(msg);
|
||||
|
||||
this.on("close", function() {
|
||||
// Called when the node is shutdown - eg on redeploy.
|
||||
// Allows ports to be closed, connections dropped etc.
|
||||
// eg: this.client.disconnect();
|
||||
});
|
||||
// Called when the node is shutdown - eg on redeploy.
|
||||
// Allows ports to be closed, connections dropped etc.
|
||||
// eg: this.client.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
// Register the node by name. This must be called before overriding any of the
|
||||
|
@@ -1,53 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="xml2js">
|
||||
<div class="form-row">
|
||||
<label>Use Console</label>
|
||||
<input type="checkbox" id="node-input-useEyes" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-useEyes" style="width: 70%;">Debug output in console ?</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Uses xml2js to process xml into javascript object.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="xml2js">
|
||||
<p>A function that parses the <b>msg.payload</b> using the xml2js library. Places the result in the payload.</p>
|
||||
<p>See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a> for more information.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('xml2js',{
|
||||
category: 'advanced-function',
|
||||
color:"#E6E0F8",
|
||||
defaults: {
|
||||
useEyes: {value:false},
|
||||
name: {value:""},
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "arrow-in.png",
|
||||
label: function() {
|
||||
return this.name||"xml2js";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var util = require("util");
|
||||
var parseString = require('xml2js').parseString;
|
||||
var gotEyes = false;
|
||||
try {
|
||||
var eyes = require("eyes");
|
||||
gotEyes = true;
|
||||
} catch(e) {
|
||||
util.log("[73-parsexml.js] Warning: Module 'eyes' not installed");
|
||||
}
|
||||
|
||||
function Xml2jsNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.useEyes = n.useEyes;
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
parseString(msg.payload, function (err, result) {
|
||||
msg.payload = result;
|
||||
node.send(msg);
|
||||
if (node.useEyes == true) {
|
||||
if (gotEyes == true) { eyes.inspect(msg); }
|
||||
else { node.log(JSON.stringify(msg)); }
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("xml2js",Xml2jsNode);
|
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
try {
|
||||
var cron = require("cron");
|
||||
} catch(err) {
|
||||
require("util").log("[inject] Warning: cannot find module 'cron'");
|
||||
}
|
||||
|
||||
function InjectNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.topic = n.topic;
|
||||
this.payload = n.payload;
|
||||
this.repeat = n.repeat;
|
||||
this.crontab = n.crontab;
|
||||
this.once = n.once;
|
||||
var node = this;
|
||||
this.interval_id = null;
|
||||
this.cronjob = null;
|
||||
|
||||
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
|
||||
this.repeat = this.repeat * 1000;
|
||||
this.log("repeat = "+this.repeat);
|
||||
this.interval_id = setInterval( function() {
|
||||
node.emit("input",{});
|
||||
}, this.repeat );
|
||||
} else if (this.crontab) {
|
||||
if (cron) {
|
||||
this.log("crontab = "+this.crontab);
|
||||
this.cronjob = new cron.CronJob(this.crontab,
|
||||
function() {
|
||||
node.emit("input",{});
|
||||
},
|
||||
null,true);
|
||||
} else {
|
||||
this.error("'cron' module not found");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.once) {
|
||||
setTimeout( function(){ node.emit("input",{}); }, 50);
|
||||
}
|
||||
|
||||
this.on("input",function(msg) {
|
||||
var msg = {topic:this.topic,payload:this.payload};
|
||||
if (msg.payload == "") { msg.payload = Date.now(); }
|
||||
this.send(msg);
|
||||
msg = null;
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("inject",InjectNode);
|
||||
|
||||
InjectNode.prototype.close = function() {
|
||||
if (this.interval_id != null) {
|
||||
clearInterval(this.interval_id);
|
||||
this.log("inject: repeat stopped");
|
||||
} else if (this.cronjob != null) {
|
||||
this.cronjob.stop();
|
||||
this.log("inject: cronjob stopped");
|
||||
delete this.cronjob;
|
||||
}
|
||||
}
|
||||
|
||||
RED.app.post("/inject/:id", function(req,res) {
|
||||
var node = RED.nodes.getNode(req.params.id);
|
||||
if (node != null) {
|
||||
try {
|
||||
node.receive();
|
||||
res.send(200);
|
||||
} catch(err) {
|
||||
res.send(500);
|
||||
node.error("Inject failed:"+err);
|
||||
console.log(err.stack);
|
||||
}
|
||||
} else {
|
||||
res.send(404);
|
||||
}
|
||||
});
|
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
|
||||
var util = require("util");
|
||||
var ws = require('ws');
|
||||
var events = require("events");
|
||||
var debuglength = RED.settings.debugMaxLength||1000;
|
||||
|
||||
function DebugNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.name = n.name;
|
||||
this.complete = n.complete;
|
||||
this.active = (n.active == null)||n.active;
|
||||
|
||||
this.on("input",function(msg) {
|
||||
if (this.active) {
|
||||
if (msg.payload instanceof Buffer) {
|
||||
msg.payload = "(Buffer) "+msg.payload.toString();
|
||||
}
|
||||
if (this.complete == "true") {
|
||||
DebugNode.send({id:this.id,name:this.name,topic:msg.topic,msg:msg,_path:msg._path});
|
||||
} else {
|
||||
if (typeof msg.payload !== "undefined") {
|
||||
DebugNode.send({id:this.id,name:this.name,topic:msg.topic,msg:msg.payload,_path:msg._path});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("debug",DebugNode);
|
||||
|
||||
DebugNode.send = function(msg) {
|
||||
if (msg.msg instanceof Error) {
|
||||
msg.msg = msg.msg.toString();
|
||||
}
|
||||
else if (typeof msg.msg === 'object') {
|
||||
var seen = [];
|
||||
msg.msg = "(Object) " + JSON.stringify(msg.msg, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.indexOf(value) !== -1) { return "[circular]"; }
|
||||
seen.push(value);
|
||||
}
|
||||
return value;
|
||||
}," ");
|
||||
seen = null;
|
||||
}
|
||||
else if (typeof msg.msg === "boolean") msg.msg = "(boolean) "+msg.msg.toString();
|
||||
else if (msg.msg === 0) msg.msg = "0";
|
||||
|
||||
if (msg.msg.length > debuglength) {
|
||||
msg.msg = msg.msg.substr(0,debuglength) +" ....";
|
||||
}
|
||||
|
||||
for (var i in DebugNode.activeConnections) {
|
||||
var ws = DebugNode.activeConnections[i];
|
||||
try {
|
||||
var p = JSON.stringify(msg);
|
||||
ws.send(p);
|
||||
} catch(err) {
|
||||
util.log("[debug] ws error : "+err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugNode.activeConnections = [];
|
||||
DebugNode.wsServer = new ws.Server({server:RED.server});
|
||||
DebugNode.wsServer.on('connection',function(ws) {
|
||||
DebugNode.activeConnections.push(ws);
|
||||
ws.on('close',function() {
|
||||
for (var i in DebugNode.activeConnections) {
|
||||
if (DebugNode.activeConnections[i] === ws) {
|
||||
DebugNode.activeConnections.splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
DebugNode.logHandler = new events.EventEmitter();
|
||||
DebugNode.logHandler.on("log",function(msg) {
|
||||
if (msg.level == "warn" || msg.level == "error") {
|
||||
DebugNode.send(msg);
|
||||
}
|
||||
});
|
||||
RED.nodes.addLogHandler(DebugNode.logHandler);
|
||||
|
||||
RED.app.post("/debug/:id", function(req,res) {
|
||||
var node = RED.nodes.getNode(req.params.id);
|
||||
if (node != null) {
|
||||
if (node.active) {
|
||||
node.active = false;
|
||||
res.send(201);
|
||||
} else {
|
||||
node.active = true;
|
||||
res.send(200);
|
||||
}
|
||||
} else {
|
||||
res.send(404);
|
||||
}
|
||||
});
|
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
function ExecNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.cmd = n.command;
|
||||
this.append = n.append || "";
|
||||
this.useSpawn = n.useSpawn;
|
||||
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
if (msg != null) {
|
||||
|
||||
if (this.useSpawn == true) {
|
||||
// make the extra args into an array
|
||||
// then prepend with the msg.payload
|
||||
var arg = node.append.split(",");
|
||||
if (msg.payload != " ") { arg.unshift(msg.payload); }
|
||||
//console.log(arg);
|
||||
var ex = spawn(node.cmd,arg);
|
||||
ex.stdout.on('data', function (data) {
|
||||
//console.log('[exec] stdout: ' + data);
|
||||
msg.payload = data;
|
||||
node.send([msg,null,null]);
|
||||
});
|
||||
ex.stderr.on('data', function (data) {
|
||||
//console.log('[exec] stderr: ' + data);
|
||||
msg.payload = data;
|
||||
node.send([null,msg,null]);
|
||||
});
|
||||
ex.on('close', function (code) {
|
||||
//console.log('[exec] result: ' + code);
|
||||
msg.payload = code;
|
||||
node.send([null,null,msg]);
|
||||
});
|
||||
}
|
||||
|
||||
else {
|
||||
var cl = node.cmd+" "+msg.payload+" "+node.append;
|
||||
node.log(cl);
|
||||
var child = exec(cl, function (error, stdout, stderr) {
|
||||
msg.payload = stdout;
|
||||
var msg2 = {payload:stderr};
|
||||
//console.log('[exec] stdout: ' + stdout);
|
||||
//console.log('[exec] stderr: ' + stderr);
|
||||
if (error !== null) {
|
||||
var msg3 = {payload:error};
|
||||
//console.log('[exec] error: ' + error);
|
||||
}
|
||||
node.send([msg,msg2,msg3]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("exec",ExecNode);
|
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
// Simple node to introduce a pause into a flow
|
||||
|
||||
// Require main module
|
||||
var RED = require("../../red/red");
|
||||
|
||||
// main node definition
|
||||
function DelayNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.pauseType = n.pauseType;
|
||||
this.timeoutUnits = n.timeoutUnits;
|
||||
this.rateUnits = n.rateUnits;
|
||||
|
||||
if (n.timeoutUnits == "milliseconds") {
|
||||
this.timeout = n.timout;
|
||||
} else if (n.timeoutUnits == "seconds") {
|
||||
this.timeout = n.timeout * 1000;
|
||||
} else if (n.timeoutUnits == "minutes") {
|
||||
this.timeout = n.timeout * (60 * 1000);
|
||||
} else if (n.timeoutUnits == "hours") {
|
||||
this.timeout = n.timeout * (60 * 60 * 1000);
|
||||
} else if (n.timeoutUnits == "days") {
|
||||
this.timeout = n.timeout * (24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
if (n.rateUnits == "second") {
|
||||
this.rate = 1000/n.rate;
|
||||
} else if (n.rateUnits == "minute") {
|
||||
this.rate = (60 * 1000)/n.rate;
|
||||
} else if (n.rateUnits == "hour") {
|
||||
this.rate = (60 * 60 * 1000)/n.rate;
|
||||
} else if (n.rateUnits == "day") {
|
||||
this.rate = (24 * 60 * 60 * 1000)/n.rate;
|
||||
}
|
||||
|
||||
this.name = n.name;
|
||||
this.idList = [];
|
||||
this.buffer = [];
|
||||
this.intervalID = -1;
|
||||
var node= this;
|
||||
|
||||
if (this.pauseType == "delay") {
|
||||
this.on("input", function(msg) {
|
||||
var node= this;
|
||||
var id;
|
||||
id = setTimeout(function(){
|
||||
node.idList.splice(node.idList.indexOf(id),1);
|
||||
node.send(msg);
|
||||
}, node.timeout);
|
||||
this.idList.push(id);
|
||||
});
|
||||
|
||||
this.on("close", function() {
|
||||
for (var i=0; i<this.idList.length; i++ ) {
|
||||
clearTimeout(this.idList[i]);
|
||||
}
|
||||
this.idList = [];
|
||||
});
|
||||
} else if (this.pauseType == "rate") {
|
||||
|
||||
this.on("input", function(msg) {
|
||||
if ( node.intervalID != -1) {
|
||||
node.buffer.push(msg);
|
||||
if (node.buffer.length > 1000) {
|
||||
node.warn(this.name + " buffer exceeded 1000 messages");
|
||||
}
|
||||
} else {
|
||||
node.send(msg);
|
||||
node.intervalID = setInterval(function() {
|
||||
if (node.buffer.length == 0) {
|
||||
clearInterval(node.intervalID);
|
||||
node.intervalID = -1;
|
||||
}
|
||||
|
||||
if (node.buffer.length > 0) {
|
||||
node.send(node.buffer.shift());
|
||||
}
|
||||
},node.rate);
|
||||
}
|
||||
});
|
||||
|
||||
this.on("close", function() {
|
||||
clearInterval(this.intervalID);
|
||||
this.buffer = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// register node
|
||||
RED.nodes.registerType("delay",DelayNode);
|
||||
|
@@ -24,10 +24,11 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="sentiment">
|
||||
<p>Analyses the <b>msg.payload</b> and adds a <b>msg.sentiment</b> object that contains the resulting AFINN-111 sentiment score as <b>msg.sentiment.score</b>.</p>
|
||||
<p>A score greater than zero is positive and less than zero is negative.</p>
|
||||
<p>Score can range from -5 to +5.</p>
|
||||
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_new">the Sentiment docs here</a>.</p>
|
||||
<p>Analyses the <b>msg.payload</b> and adds a <b>msg.sentiment</b> object that contains the resulting AFINN-111 sentiment score as <b>msg.sentiment.score</b>.</p>
|
||||
<p>A score greater than zero is positive and less than zero is negative.</p>
|
||||
<p>The score typically ranges from -5 to +5, but can go higher and lower.</p>
|
||||
<p>An object of word score overrides can be supplied as <b>msg.overrides</b>.</p>
|
||||
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_new">the Sentiment docs here</a>.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -47,5 +48,4 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@@ -14,19 +14,18 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var sentiment = require('sentiment');
|
||||
|
||||
function SentimentNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
var node = this;
|
||||
sentiment(msg.payload, function (err, result) {
|
||||
msg.sentiment = result;
|
||||
node.send(msg);
|
||||
});
|
||||
sentiment(msg.payload, msg.overrides || null, function (err, result) {
|
||||
msg.sentiment = result;
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("sentiment",SentimentNode);
|
53
nodes/core/analysis/73-parsexml.html
Normal file
53
nodes/core/analysis/73-parsexml.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="xml2js">
|
||||
<!-- <div class="form-row">
|
||||
<label>Use Console</label>
|
||||
<input type="checkbox" id="node-input-useEyes" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-useEyes" style="width: 70%;">Debug output in console ?</label>
|
||||
</div> -->
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<!-- <div class="form-tips">Uses xml2js to process xml into javascript object.</div> -->
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="xml2js">
|
||||
<p>A function that parses the <b>msg.payload</b> using the xml2js library. Places the result in the payload.</p>
|
||||
<p>See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a> for more information.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('xml2js',{
|
||||
category: 'advanced-function',
|
||||
color:"#E6E0F8",
|
||||
defaults: {
|
||||
//useEyes: {value:false},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "arrow-in.png",
|
||||
label: function() {
|
||||
return this.name||"xml2json";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
44
nodes/core/analysis/73-parsexml.js
Normal file
44
nodes/core/analysis/73-parsexml.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var util = require("util");
|
||||
var parseString = require('xml2js').parseString;
|
||||
var useColors = true;
|
||||
//util.inspect.styles.boolean = "red";
|
||||
|
||||
function Xml2jsNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.useEyes = n.useEyes||false;
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
try {
|
||||
parseString(msg.payload, {strict:true,async:true}, function (err, result) {
|
||||
//parseString(msg.payload, {strict:false,async:true}, function (err, result) {
|
||||
if (err) { node.error(err); }
|
||||
else {
|
||||
msg.payload = result;
|
||||
node.send(msg);
|
||||
if (node.useEyes == true) {
|
||||
node.log("\n"+util.inspect(msg, {colors:useColors, depth:10}));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(e) { util.log("[73-parsexml.js] "+e); }
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("xml2js",Xml2jsNode);
|
@@ -1,57 +1,51 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="wordpos">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Adds <b>msg.pos</b> as the anaylsis result.
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="wordpos">
|
||||
<p>Analyses <b>msg.payload</b> and classifies the part-of-speech of each word.</p>
|
||||
<p>The resulting message has <b>msg.pos</b> added with the results:</p>
|
||||
<pre>{
|
||||
nouns:[],
|
||||
verbs:[],
|
||||
adjectives:[],
|
||||
adverbs:[],
|
||||
rest:[]
|
||||
}</pre>
|
||||
<p>Note: a word may appear in multiple POS (eg, 'great' is both a noun and an adjective)</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('wordpos',{
|
||||
category: 'analysis-function',
|
||||
color:"#E6E0F8",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "arrow-in.png",
|
||||
label: function() {
|
||||
return this.name||"wordpos";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="json2xml">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-list"></i> XML Root</label>
|
||||
<input type="text" id="node-input-root" placeholder="object"></input>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name"></input>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="json2xml">
|
||||
<p>A function that parses the <b>msg.payload</b> using the js2xmlparser library. Places the result back in <b>msg.payload</b>.</p>
|
||||
<p>See the <a href="https://github.com/michaelkourlas/node-js2xmlparser" target="_new">js2xmlparser docs</a> for more information.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('json2xml',{
|
||||
category: 'advanced-function',
|
||||
color:"#E6E0F8",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
root: {value:"object"},
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "arrow-in.png",
|
||||
label: function() {
|
||||
return this.name||"json2xml";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
36
nodes/core/analysis/74-js2xml.js
Normal file
36
nodes/core/analysis/74-js2xml.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var js2xmlparser = require("js2xmlparser");
|
||||
|
||||
function Js2XmlNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.root = n.root;
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
try {
|
||||
var root = node.root || typeof msg.payload;
|
||||
if (typeof msg.payload !== "object") { msg.payload = '"'+msg.payload+'"'; }
|
||||
console.log(root, typeof msg.payload,msg.payload);
|
||||
msg.payload = js2xmlparser(root, msg.payload);
|
||||
node.send(msg);
|
||||
}
|
||||
catch(e) { console.log(e); }
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("json2xml",Js2XmlNode);
|
@@ -16,7 +16,16 @@
|
||||
|
||||
<script type="text/x-red" data-template-name="inject">
|
||||
<div class="form-row node-input-payload">
|
||||
<label for="node-input-payload"><i class="icon-envelope"></i> Payload</label>
|
||||
<label for="node-input-payloadType"><i class="icon-envelope"></i> Payload</label>
|
||||
<select id="node-input-payloadType" style="width:125px !important">
|
||||
<option value="date">timestamp</option>
|
||||
<option value="none">blank</option>
|
||||
<option value="string">string</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="node-input-row-payload">
|
||||
<label for="node-input-payload"></label>
|
||||
<input type="text" id="node-input-payload" placeholder="Payload">
|
||||
</div>
|
||||
|
||||
@@ -25,12 +34,6 @@
|
||||
<input type="text" id="node-input-topic" placeholder="Topic">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-once" placeholder="once" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-once" style="width: 70%;">Fire once at start ?</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for=""><i class="icon-repeat"></i> Repeat</label>
|
||||
<select id="inject-time-type-select"><option value="none">None</option><option value="interval">interval</option><option value="interval-time">interval between times</option><option value="time">at a specific time</option></select>
|
||||
@@ -56,19 +59,31 @@
|
||||
on <select id="inject-time-time-days" class="inject-time-days"></select>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="node-once">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-once" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-once" style="width: 70%;">Fire once at start ?</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-tips">Tip: Injects Date.now() if no payload set</div>
|
||||
<script>
|
||||
{
|
||||
|
||||
$("#inject-time-type-select").change(function() {
|
||||
$("#node-input-crontab").val('');
|
||||
var id = $("#inject-time-type-select option:selected").val();
|
||||
$(".inject-time-row").hide();
|
||||
$("#inject-time-row-"+id).show();
|
||||
if ((id == "none") || (id == "interval")) {
|
||||
$("#node-once").show();
|
||||
}
|
||||
else {
|
||||
$("#node-once").hide();
|
||||
$("#node-input-once").prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
var days = [
|
||||
@@ -83,6 +98,7 @@
|
||||
{v:"6",t:"Saturdays"},
|
||||
{v:"7",t:"Sundays"}
|
||||
];
|
||||
|
||||
$(".inject-time-days").each(function() {
|
||||
for (var d in days) {
|
||||
$(this).append($("<option></option>").val(days[d].v).text(days[d].t));
|
||||
@@ -95,6 +111,7 @@
|
||||
$(this).append($("<option></option>").val(i).text(l));
|
||||
}
|
||||
});
|
||||
|
||||
$(".inject-time-count").spinner({
|
||||
min:1,
|
||||
max:60
|
||||
@@ -104,10 +121,8 @@
|
||||
var units = $("#inject-time-interval-units option:selected").val();
|
||||
$("#inject-time-interval-days").prop("disabled",(units == "s")?"disabled":false);
|
||||
$(".inject-time-count").spinner("option","max",(units == "h")?24:60);
|
||||
|
||||
});
|
||||
|
||||
|
||||
$.widget( "ui.injecttimespinner", $.ui.spinner, {
|
||||
options: {
|
||||
// seconds
|
||||
@@ -132,16 +147,13 @@
|
||||
var d = new Date(value);
|
||||
var h = d.getHours();
|
||||
var m = d.getMinutes();
|
||||
|
||||
return ((h<10)?"0":"")+h+":"+((m<10)?"0":"")+m;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$("#inject-time-time").injecttimespinner();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
</script>
|
||||
<style>
|
||||
@@ -169,14 +181,12 @@
|
||||
.inject-time-count {
|
||||
width: 30px !important;
|
||||
}
|
||||
.
|
||||
|
||||
</style>
|
||||
<script type="text/x-red" data-help-name="inject">
|
||||
<p>Pressing the button on the left side of the node allows a message on a topic to be injected into the flow. This is mainly for test purposes.</p>
|
||||
<p>If no payload is specified the payload is set to the current time in millisecs since 1970. This allows subsequent functions to perform time based actions.</p>
|
||||
<p>The repeat function does what it says on the tin and continuously sends the payload every x seconds.</p>
|
||||
<p>The Fire once at start option actually waits 50mS before firing to give other nodes a chance to instantiate properly.</p>
|
||||
<p>Pressing the button on the left side of the node allows a message on a topic to be injected into the flow. This is mainly for test purposes.</p>
|
||||
<p>If no payload is specified the payload is set to the current time in millisecs since 1970. This allows subsequent functions to perform time based actions.</p>
|
||||
<p>The repeat function does what it says on the tin and continuously sends the payload every x seconds.</p>
|
||||
<p>The Fire once at start option actually waits 50mS before firing to give other nodes a chance to instantiate properly.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -187,6 +197,7 @@
|
||||
name: {value:""},
|
||||
topic: {value:""},
|
||||
payload: {value:""},
|
||||
payloadType: {value:"date"},
|
||||
repeat: {value:""},
|
||||
crontab: {value:""},
|
||||
once: {value:false}
|
||||
@@ -195,14 +206,17 @@
|
||||
outputs:1,
|
||||
icon: "inject.png",
|
||||
label: function() {
|
||||
return this.name||this.topic||this.payload||"inject";
|
||||
if (this.payloadType == "string") {
|
||||
return this.name||this.topic||this.payload||"inject";
|
||||
}
|
||||
else { return this.name||"inject"; }
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var repeattype = "none";
|
||||
if (this.repeat != "") {
|
||||
if (this.repeat != "" && this.repeat != 0) {
|
||||
repeattype = "interval";
|
||||
$("#inject-time-interval-units option").filter(function() {return $(this).val() == "s";}).attr('selected',true);
|
||||
$("#inject-time-interval-count").val(this.repeat);
|
||||
@@ -278,6 +292,25 @@
|
||||
$("#inject-time-type-select option").filter(function() {return $(this).val() == repeattype;}).attr('selected',true);
|
||||
$("#inject-time-row-"+repeattype).show();
|
||||
|
||||
if (this.payloadType == null) {
|
||||
if (this.payload == "") {
|
||||
this.payloadType = "date";
|
||||
} else {
|
||||
this.payloadType = "string";
|
||||
}
|
||||
}
|
||||
|
||||
$("#node-input-payloadType").change(function() {
|
||||
var id = $("#node-input-payloadType option:selected").val();
|
||||
if (id == "string") {
|
||||
$("#node-input-row-payload").show();
|
||||
} else {
|
||||
$("#node-input-row-payload").hide();
|
||||
}
|
||||
});
|
||||
$("#node-input-payloadType").val(this.payloadType);
|
||||
$("#node-input-payloadType").change();
|
||||
$("#inject-time-type-select").change();
|
||||
|
||||
},
|
||||
oneditsave: function() {
|
||||
@@ -307,7 +340,7 @@
|
||||
var timerange = "";
|
||||
if (startTime == endTime) {
|
||||
//TODO: invalid
|
||||
repeat = 0;
|
||||
repeat = "";
|
||||
crontab = "";
|
||||
} else if (endTime == 0) {
|
||||
timerange = startTime+"-23";
|
||||
@@ -330,13 +363,13 @@
|
||||
}
|
||||
timerange = startpart+","+endpart;
|
||||
}
|
||||
repeat = 0;
|
||||
repeat = "";
|
||||
crontab = "*/"+count+" "+timerange+" * * "+days;
|
||||
} else if (type == "time") {
|
||||
var time = $("#inject-time-time").val();
|
||||
var days = $("#inject-time-time-days option:selected").val();
|
||||
var parts = time.split(":");
|
||||
repeat = 0;
|
||||
repeat = "";
|
||||
crontab = parts[1]+" "+parts[0]+" * * "+days;
|
||||
}
|
||||
|
||||
@@ -346,7 +379,7 @@
|
||||
},
|
||||
button: {
|
||||
onclick: function() {
|
||||
var label = this.name||this.payload;
|
||||
var label = (this.name||this.payload).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
d3.xhr("inject/"+this.id).post(function(err,resp) {
|
||||
if (err) {
|
||||
if (err.status == 404) {
|
100
nodes/core/core/20-inject.js
Normal file
100
nodes/core/core/20-inject.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
try {
|
||||
var cron = require("cron");
|
||||
} catch(err) {
|
||||
require("util").log("[inject] Warning: cannot find module 'cron'");
|
||||
}
|
||||
|
||||
function InjectNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.topic = n.topic;
|
||||
this.payload = n.payload;
|
||||
this.payloadType = n.payloadType;
|
||||
this.repeat = n.repeat;
|
||||
this.crontab = n.crontab;
|
||||
this.once = n.once;
|
||||
var node = this;
|
||||
this.interval_id = null;
|
||||
this.cronjob = null;
|
||||
|
||||
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
|
||||
this.repeat = this.repeat * 1000;
|
||||
this.log("repeat = "+this.repeat);
|
||||
this.interval_id = setInterval( function() {
|
||||
node.emit("input",{});
|
||||
}, this.repeat );
|
||||
} else if (this.crontab) {
|
||||
if (cron) {
|
||||
this.log("crontab = "+this.crontab);
|
||||
this.cronjob = new cron.CronJob(this.crontab,
|
||||
function() {
|
||||
node.emit("input",{});
|
||||
},
|
||||
null,true);
|
||||
} else {
|
||||
this.error("'cron' module not found");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.once) {
|
||||
setTimeout( function(){ node.emit("input",{}); }, 50);
|
||||
}
|
||||
|
||||
this.on("input",function(msg) {
|
||||
var msg = {topic:this.topic};
|
||||
if ( (this.payloadType == null && this.payload == "") || this.payloadType == "date") {
|
||||
msg.payload = Date.now();
|
||||
} else if (this.payloadType == null || this.payloadType == "string") {
|
||||
msg.payload = this.payload;
|
||||
} else {
|
||||
msg.payload = "";
|
||||
}
|
||||
this.send(msg);
|
||||
msg = null;
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("inject",InjectNode);
|
||||
|
||||
InjectNode.prototype.close = function() {
|
||||
if (this.interval_id != null) {
|
||||
clearInterval(this.interval_id);
|
||||
this.log("inject: repeat stopped");
|
||||
} else if (this.cronjob != null) {
|
||||
this.cronjob.stop();
|
||||
this.log("inject: cronjob stopped");
|
||||
delete this.cronjob;
|
||||
}
|
||||
}
|
||||
|
||||
RED.httpAdmin.post("/inject/:id", function(req,res) {
|
||||
var node = RED.nodes.getNode(req.params.id);
|
||||
if (node != null) {
|
||||
try {
|
||||
node.receive();
|
||||
res.send(200);
|
||||
} catch(err) {
|
||||
res.send(500);
|
||||
node.error("Inject failed:"+err);
|
||||
console.log(err.stack);
|
||||
}
|
||||
} else {
|
||||
res.send(404);
|
||||
}
|
||||
});
|
@@ -17,9 +17,16 @@
|
||||
<script type="text/x-red" data-template-name="debug">
|
||||
<div class="form-row">
|
||||
<label for="node-input-complete"><i class="icon-list"></i> Output</label>
|
||||
<select type="text" id="node-input-complete" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<option value=false>Payload only</option>
|
||||
<option value=true>Complete msg object</option>
|
||||
<select type="text" id="node-input-complete" style="display: inline-block; width: 250px; vertical-align: top;">
|
||||
<option value=false>payload only</option>
|
||||
<option value=true>complete msg object</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-console"><i class="icon-random"></i> to</label>
|
||||
<select type="text" id="node-input-console" style="display: inline-block; width: 250px; vertical-align: top;">
|
||||
<option value=false>debug tab</option>
|
||||
<option value=true>debug tab and console</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -29,14 +36,14 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="debug">
|
||||
<p>The Debug node can be connected to the output of any node. It will display the timestamp, <b>msg.topic</b> and <b>msg.payload</b> fields of any messages it receives in the debug tab of the sidebar.
|
||||
<br/>The sidebar can be accessed under the options drop-down in the top right corner.</p>
|
||||
<p>The button to the right of the node will toggle it's output on and off so you can de-clutter the debug window.</p>
|
||||
<p>If the payload is an object it will be stringified first for display and indicate that by saying "(Object) ".</p>
|
||||
<p>If the payload is a buffer it will be stringified first for display and indicate that by saying "(Buffer) ".</p>
|
||||
<p>Selecting any particular message will highlight (in red) the debug node that reported it. This is useful if you wire up multiple debug nodes.</p>
|
||||
<p>Optionally can show the complete msg object - but the screen can get messy.</p>
|
||||
<p>In addition any calls to node.warn or node.error will appear here.</p>
|
||||
<p>The Debug node can be connected to the output of any node. It will display the timestamp, <b>msg.topic</b> and <b>msg.payload</b> fields of any messages it receives in the debug tab of the sidebar.
|
||||
<br/>The sidebar can be accessed under the options drop-down in the top right corner.</p>
|
||||
<p>The button to the right of the node will toggle it's output on and off so you can de-clutter the debug window.</p>
|
||||
<p>If the payload is an object it will be stringified first for display and indicate that by saying "(Object) ".</p>
|
||||
<p>If the payload is a buffer it will be stringified first for display and indicate that by saying "(Buffer) ".</p>
|
||||
<p>Selecting any particular message will highlight (in red) the debug node that reported it. This is useful if you wire up multiple debug nodes.</p>
|
||||
<p>Optionally can show the complete msg object - but the screen can get messy.</p>
|
||||
<p>In addition any calls to node.warn or node.error will appear here.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -45,6 +52,7 @@
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
active: {value:true},
|
||||
console: {value:false},
|
||||
complete: {value:false}
|
||||
},
|
||||
label: function() {
|
||||
@@ -59,34 +67,25 @@
|
||||
icon: "debug.png",
|
||||
align: "right",
|
||||
button: {
|
||||
color: function() {
|
||||
return (typeof this.active === 'undefined') ? ("#87a980" ) : (this.active ? "#87a980" : "#b9b9b9");
|
||||
},
|
||||
toggle: "active",
|
||||
onclick: function() {
|
||||
var label = this.name||"debug";
|
||||
var node = this;
|
||||
d3.xhr("debug/"+this.id).post(function(err,resp) {
|
||||
if (err) {
|
||||
if (err.status == 404) {
|
||||
RED.notify("<strong>Error</strong>: debug node not deployed","error");
|
||||
} else if (err.status == 0) {
|
||||
RED.notify("<strong>Error</strong>: no response from server","error");
|
||||
} else {
|
||||
RED.notify("<strong>Error</strong>: unexpected error: ("+err.status+")"+err.response,"error");
|
||||
}
|
||||
} else if (resp.status == 200) {
|
||||
RED.notify("Successfully activated: "+label,"success");
|
||||
node.active = true;
|
||||
node.dirty = true;
|
||||
RED.view.redraw();
|
||||
} else if (resp.status == 201) {
|
||||
RED.notify("Successfully deactivated: "+label,"success");
|
||||
node.active = false;
|
||||
node.dirty = true;
|
||||
RED.view.redraw();
|
||||
d3.xhr("debug/"+this.id+"/"+(this.active?"enable":"disable")).post(function(err,resp) {
|
||||
if (err) {
|
||||
if (err.status == 404) {
|
||||
RED.notify("<strong>Error</strong>: debug node not deployed","error");
|
||||
} else if (err.status == 0) {
|
||||
RED.notify("<strong>Error</strong>: no response from server","error");
|
||||
} else {
|
||||
RED.notify("<strong>Error</strong>: unexpected response: ("+resp.status+") "+resp.response,"error");
|
||||
RED.notify("<strong>Error</strong>: unexpected error: ("+err.status+")"+err.response,"error");
|
||||
}
|
||||
} else if (resp.status == 200) {
|
||||
RED.notify("Successfully activated: "+label,"success");
|
||||
} else if (resp.status == 201) {
|
||||
RED.notify("Successfully deactivated: "+label,"success");
|
||||
} else {
|
||||
RED.notify("<strong>Error</strong>: unexpected response: ("+resp.status+") "+resp.response,"error");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -116,13 +115,16 @@
|
||||
var sbc = document.getElementById("debug-content");
|
||||
|
||||
var errornotification = null;
|
||||
|
||||
|
||||
var messageCount = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
function debugConnect() {
|
||||
//console.log("debug ws connecting");
|
||||
var ws = new WebSocket("ws://"+location.hostname+":"+location.port+document.location.pathname+"/debug");
|
||||
var path = location.hostname+":"+location.port+document.location.pathname;
|
||||
path = path+(path.slice(-1) == "/"?"":"/")+"debug/ws";
|
||||
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
|
||||
var ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
if (errornotification) {
|
||||
errornotification.close();
|
||||
@@ -132,31 +134,39 @@
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var o = JSON.parse(event.data);
|
||||
if (o.heartbeat) {
|
||||
return;
|
||||
}
|
||||
//console.log(msg);
|
||||
var msg = document.createElement("div");
|
||||
msg.onmouseover = function() {
|
||||
msg.style.borderRightColor = "#999";
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if( node.id == o.id) {
|
||||
node.highlighted = true;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
var n = RED.nodes.node(o.id);
|
||||
if (n) {
|
||||
n.highlighted = true;
|
||||
n.dirty = true;
|
||||
}
|
||||
RED.view.redraw();
|
||||
};
|
||||
msg.onmouseout = function() {
|
||||
msg.style.borderRightColor = "";
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if( node.id == o.id) {
|
||||
node.highlighted = false;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
var n = RED.nodes.node(o.id);
|
||||
if (n) {
|
||||
n.highlighted = false;
|
||||
n.dirty = true;
|
||||
}
|
||||
RED.view.redraw();
|
||||
};
|
||||
var name = (o.name?o.name:o.id).toString().replace(/</g,"<").replace(/>/g,">");
|
||||
var topic = (o.topic||"").toString().replace(/</g,"<").replace(/>/g,">");
|
||||
var payload = (o.msg||"").toString().replace(/</g,"<").replace(/>/g,">");
|
||||
msg.onclick = function() {
|
||||
var node = RED.nodes.node(o.id);
|
||||
if (node) {
|
||||
RED.view.showWorkspace(node.z);
|
||||
}
|
||||
|
||||
};
|
||||
var name = (o.name?o.name:o.id).toString().replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
var topic = (o.topic||"").toString().replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
var payload = (o.msg||"").toString().replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'')
|
||||
msg.innerHTML = '<span class="debug-message-date">'+getTimestamp()+'</span>'+
|
||||
'<span class="debug-message-name">['+name+']</span>'+
|
||||
@@ -165,7 +175,7 @@
|
||||
var atBottom = (sbc.scrollHeight-messages.offsetHeight-sbc.scrollTop) < 5;
|
||||
messageCount++;
|
||||
$(messages).append(msg);
|
||||
|
||||
|
||||
if (messageCount > 200) {
|
||||
$("#debug-content .debug-message:first").remove();
|
||||
messageCount--;
|
||||
@@ -228,7 +238,7 @@
|
||||
background: #fff;
|
||||
padding: 1px 5px;
|
||||
font-size: 9px;
|
||||
color: #caa;
|
||||
color: #a66;
|
||||
}
|
||||
.debug-message-name {
|
||||
background: #fff;
|
170
nodes/core/core/58-debug.js
Normal file
170
nodes/core/core/58-debug.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var util = require("util");
|
||||
var ws = require("ws");
|
||||
var events = require("events");
|
||||
var debuglength = RED.settings.debugMaxLength||1000;
|
||||
var useColors = false;
|
||||
// util.inspect.styles.boolean = "red";
|
||||
|
||||
function DebugNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.name = n.name;
|
||||
this.complete = n.complete;
|
||||
this.console = n.console;
|
||||
this.active = (n.active == null)||n.active;
|
||||
var node = this;
|
||||
|
||||
this.on("input",function(msg) {
|
||||
if (this.complete == "true") { // debug complete msg object
|
||||
if (this.console == "true") {
|
||||
node.log("\n"+util.inspect(msg, {colors:useColors, depth:10}));
|
||||
}
|
||||
if (msg.payload instanceof Buffer) { msg.payload = "(Buffer) "+msg.payload.toString('hex'); }
|
||||
if (this.active) {
|
||||
DebugNode.send({id:this.id,name:this.name,topic:msg.topic,msg:msg,_path:msg._path});
|
||||
}
|
||||
} else { // debug just the msg.payload
|
||||
if (this.console == "true") {
|
||||
if (typeof msg.payload === "string") {
|
||||
if (msg.payload.indexOf("\n") != -1) { msg.payload = "\n"+msg.payload; }
|
||||
node.log(msg.payload);
|
||||
}
|
||||
else if (typeof msg.payload === "object") { node.log("\n"+util.inspect(msg.payload, {colors:useColors, depth:10})); }
|
||||
else { node.log(util.inspect(msg.payload, {colors:useColors})); }
|
||||
}
|
||||
if (typeof msg.payload == "undefined") { msg.payload = "(undefined)"; }
|
||||
if (msg.payload instanceof Buffer) { msg.payload = "(Buffer) "+msg.payload.toString('hex'); }
|
||||
if (this.active) {
|
||||
DebugNode.send({id:this.id,name:this.name,topic:msg.topic,msg:msg.payload,_path:msg._path});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var lastSentTime = (new Date()).getTime();
|
||||
|
||||
setInterval(function() {
|
||||
var now = (new Date()).getTime();
|
||||
if (now-lastSentTime > 15000) {
|
||||
lastSentTime = now;
|
||||
for (var i in DebugNode.activeConnections) {
|
||||
var ws = DebugNode.activeConnections[i];
|
||||
try {
|
||||
var p = JSON.stringify({heartbeat:lastSentTime});
|
||||
ws.send(p);
|
||||
} catch(err) {
|
||||
util.log("[debug] ws heartbeat error : "+err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
|
||||
|
||||
RED.nodes.registerType("debug",DebugNode);
|
||||
|
||||
DebugNode.send = function(msg) {
|
||||
if (msg.msg instanceof Error) {
|
||||
msg.msg = msg.msg.toString();
|
||||
} else if (typeof msg.msg === 'object') {
|
||||
var seen = [];
|
||||
var ty = "(Object) ";
|
||||
if (util.isArray(msg.msg)) { ty = "(Array) "; }
|
||||
msg.msg = ty + JSON.stringify(msg.msg, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.indexOf(value) !== -1) { return "[circular]"; }
|
||||
seen.push(value);
|
||||
}
|
||||
return value;
|
||||
}," ");
|
||||
seen = null;
|
||||
} else if (typeof msg.msg === "boolean") {
|
||||
msg.msg = "(boolean) "+msg.msg.toString();
|
||||
} else if (msg.msg === 0) {
|
||||
msg.msg = "0";
|
||||
} else if (msg.msg == null) {
|
||||
msg.msg = "[undefined]";
|
||||
}
|
||||
|
||||
if (msg.msg.length > debuglength) {
|
||||
msg.msg = msg.msg.substr(0,debuglength) +" ....";
|
||||
}
|
||||
|
||||
for (var i in DebugNode.activeConnections) {
|
||||
var ws = DebugNode.activeConnections[i];
|
||||
try {
|
||||
var p = JSON.stringify(msg);
|
||||
ws.send(p);
|
||||
} catch(err) {
|
||||
util.log("[debug] ws error : "+err);
|
||||
}
|
||||
}
|
||||
lastSentTime = (new Date()).getTime();
|
||||
}
|
||||
|
||||
DebugNode.activeConnections = [];
|
||||
|
||||
var path = RED.settings.httpAdminRoot || "/";
|
||||
path = path + (path.slice(-1) == "/" ? "":"/") + "debug/ws";
|
||||
|
||||
DebugNode.wsServer = new ws.Server({server:RED.server,path:path});
|
||||
DebugNode.wsServer.on('connection',function(ws) {
|
||||
DebugNode.activeConnections.push(ws);
|
||||
ws.on('close',function() {
|
||||
for (var i in DebugNode.activeConnections) {
|
||||
if (DebugNode.activeConnections[i] === ws) {
|
||||
DebugNode.activeConnections.splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
ws.on('error', function(err) {
|
||||
util.log("[debug] ws error : "+err);
|
||||
});
|
||||
});
|
||||
|
||||
DebugNode.wsServer.on('error', function(err) {
|
||||
util.log("[debug] ws server error : "+err);
|
||||
});
|
||||
|
||||
DebugNode.logHandler = new events.EventEmitter();
|
||||
DebugNode.logHandler.on("log",function(msg) {
|
||||
if (msg.level == "warn" || msg.level == "error") {
|
||||
DebugNode.send(msg);
|
||||
}
|
||||
});
|
||||
RED.nodes.addLogHandler(DebugNode.logHandler);
|
||||
|
||||
RED.httpAdmin.post("/debug/:id/:state", function(req,res) {
|
||||
var node = RED.nodes.getNode(req.params.id);
|
||||
var state = req.params.state;
|
||||
if (node != null) {
|
||||
if (state === "enable") {
|
||||
node.active = true;
|
||||
res.send(200);
|
||||
} else if (state === "disable") {
|
||||
node.active = false;
|
||||
res.send(201);
|
||||
} else {
|
||||
res.send(404);
|
||||
}
|
||||
} else {
|
||||
res.send(404);
|
||||
}
|
||||
});
|
@@ -32,14 +32,16 @@
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Tip: <i>spawn</i> expects only one command word - and appended args to be comma separated.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="exec">
|
||||
<p>Call to a system command.<br/>This can be very dangerous...</p>
|
||||
<p>Provides 3 outputs... stdout, stderr, and return code.</p>
|
||||
<p>By default uses exec() which calls the command, waits for completion and then returns the complete result in one go. Along with any errors.</p>
|
||||
<p>Optionally can use spawn() instead, which returns output from stdout and stderr as the command runs (ie one line at a time). On completion then returns a return code (on the 3rd output).</p>
|
||||
<p>The optional append gets added to the command after the <b>msg.payload</b> (so you can do things like pipe etc.)</p>
|
||||
<p>Calls out to a system command.<br/></p>
|
||||
<p>Provides 3 outputs... stdout, stderr, and return code.</p>
|
||||
<p>By default uses exec() which calls the command, blocks while waiting for completion, and then returns the complete result in one go, along with any errors.</p>
|
||||
<p>Optionally can use spawn() instead, which returns output from stdout and stderr as the command runs (ie one line at a time). On completion it then returns a return code (on the 3rd output).</p>
|
||||
<p>Spawn only expect one command word, with all extra parameters to be comma separated and passed as the append.</p>
|
||||
<p>The optional append gets added to the command after the <b>msg.payload</b> - so you can do things like pipe the result to another command.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
81
nodes/core/core/75-exec.js
Normal file
81
nodes/core/core/75-exec.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
function ExecNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.cmd = n.command.trim();
|
||||
this.append = n.append.trim() || "";
|
||||
this.useSpawn = n.useSpawn;
|
||||
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
if (msg != null) {
|
||||
|
||||
if (this.useSpawn == true) {
|
||||
// make the extra args into an array
|
||||
// then prepend with the msg.payload
|
||||
if (typeof(msg.payload !== "string")) { msg.payload = msg.payload.toString(); }
|
||||
var arg = [];
|
||||
if (node.append.length > 0) { arg = node.append.split(","); }
|
||||
if (msg.payload.trim() != "") { arg.unshift(msg.payload); }
|
||||
node.log(node.cmd+" ["+arg+"]");
|
||||
if (node.cmd.indexOf(" ") == -1) {
|
||||
var ex = spawn(node.cmd,arg);
|
||||
ex.stdout.on('data', function (data) {
|
||||
//console.log('[exec] stdout: ' + data);
|
||||
msg.payload = data.toString();
|
||||
node.send([msg,null,null]);
|
||||
});
|
||||
ex.stderr.on('data', function (data) {
|
||||
//console.log('[exec] stderr: ' + data);
|
||||
msg.payload = data.toString();
|
||||
node.send([null,msg,null]);
|
||||
});
|
||||
ex.on('close', function (code) {
|
||||
//console.log('[exec] result: ' + code);
|
||||
msg.payload = code;
|
||||
node.send([null,null,msg]);
|
||||
});
|
||||
}
|
||||
else { node.error("Spawn command must be just the command - no spaces or extra parameters"); }
|
||||
}
|
||||
|
||||
else {
|
||||
var cl = node.cmd+" "+msg.payload+" "+node.append;
|
||||
node.log(cl);
|
||||
var child = exec(cl, function (error, stdout, stderr) {
|
||||
msg.payload = stdout;
|
||||
var msg2 = {payload:stderr};
|
||||
//console.log('[exec] stdout: ' + stdout);
|
||||
//console.log('[exec] stderr: ' + stderr);
|
||||
if (error !== null) {
|
||||
var msg3 = {payload:error};
|
||||
//console.log('[exec] error: ' + error);
|
||||
}
|
||||
node.send([msg,msg2,msg3]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("exec",ExecNode);
|
@@ -28,13 +28,26 @@
|
||||
<label for="node-input-outputs"><i class="icon-random"></i> Outputs</label>
|
||||
<input id="node-input-outputs" style="width: 60px; height: 1.7em;" value="1">
|
||||
</div>
|
||||
<div class="form-tips">See the Info tab for help writing functions.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="function">
|
||||
<p>The generic function block where you can write code to do more interesting things.</p>
|
||||
<p>You can have multiple outputs - in which case the function expects you to return an array of messages... <pre>return [msg,msg2,...];</pre></p>
|
||||
<p>You may return null for any or all outputs if you want to.</p>
|
||||
<p>You can save your functions to a library (button to right of name field) - and likewise pick from that library.</p>
|
||||
<p>A function block where you can write code to do more interesting things.</p>
|
||||
<p>The message is passed in as a JavaScript object called <code>msg</code>.</p>
|
||||
<p>By convention it will have a <code>msg.payload</code> property containing
|
||||
the body of the message.</p>
|
||||
<p>The function should return the messages it wants to pass on to the next nodes
|
||||
in the flow. It can return:</p>
|
||||
<ul>
|
||||
<li>a single message object - passed to nodes connected to the first output</li>
|
||||
<li>an array of message objects - passed to nodes connected to the corresponding outputs</li>
|
||||
</ul>
|
||||
<p>If any element of the array is itself an array of messages, multiple
|
||||
messages are sent to the corresponding output.</p>
|
||||
<p>If null is returned, either by itself or as an element of the array, no
|
||||
message is passed on.</p>
|
||||
<p>See the <a target="_new" href="http://nodered.org/docs/writing-functions.html">online documentation</a> for more help.</p>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -43,7 +56,7 @@
|
||||
category: 'function',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
func: {value:"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n// console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n// context = {};\n\n\nreturn msg;"},
|
||||
func: {value:"\nreturn msg;"},
|
||||
outputs: {value:1}
|
||||
},
|
||||
inputs:1,
|
||||
@@ -58,7 +71,7 @@
|
||||
});
|
||||
|
||||
function functionDialogResize(ev,ui) {
|
||||
$("#node-input-func-editor").css("height",(ui.size.height-235)+"px");
|
||||
$("#node-input-func-editor").css("height",(ui.size.height-275)+"px");
|
||||
};
|
||||
|
||||
$( "#dialog" ).on("dialogresize", functionDialogResize);
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
var util = require("util");
|
||||
var vm = require("vm");
|
||||
@@ -25,7 +25,7 @@ function FunctionNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.name = n.name;
|
||||
this.func = n.func;
|
||||
var functionText = "var results = (function(msg){"+this.func+"})(msg);";
|
||||
var functionText = "var results = (function(msg){"+this.func+"\n})(msg);";
|
||||
this.topic = n.topic;
|
||||
this.context = {global:RED.settings.functionGlobalContext || {}};
|
||||
try {
|
||||
@@ -58,12 +58,12 @@ function FunctionNode(n) {
|
||||
this.send(results);
|
||||
|
||||
} catch(err) {
|
||||
this.error(err.message);
|
||||
this.error(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch(err) {
|
||||
this.error(err.message);
|
||||
this.error(err);
|
||||
}
|
||||
}
|
||||
|
@@ -29,9 +29,18 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="template">
|
||||
<p>Creates new messages based on a template.</p>
|
||||
<p>Very useful for creating boilerplate web pages, emails, tweets and so on.</p>
|
||||
<p>Uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_new">Mustache</a></i> format.</p>
|
||||
<p>Creates a new payload based on the provided template.</p>
|
||||
<p>This uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_new">mustache</a></i> format.</p>
|
||||
<p>For example, when a template of:
|
||||
<pre>Hello {{name}}. Today is {{date}}</pre>
|
||||
<p>receives a message containing:
|
||||
<pre>{
|
||||
name: "Fred",
|
||||
date: "Monday"
|
||||
payload: ...
|
||||
}</pre>
|
||||
<p>The resulting payload will be:
|
||||
<pre>Hello Fred. Today is Monday</pre>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
var mustache = require("mustache");
|
||||
var util = require("util");
|
@@ -22,6 +22,7 @@
|
||||
<select id="node-input-pauseType" style="width:270px !important">
|
||||
<option value="delay">Delay message</option>
|
||||
<option value="rate">Limit rate to</option>
|
||||
<option value="random">Random delay</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="delay-details" class="form-row">
|
||||
@@ -38,14 +39,30 @@
|
||||
|
||||
<div id="rate-details" class="form-row">
|
||||
<label for="node-input-rate"><i class="icon-time"></i> To</label>
|
||||
<input type="text" id="node-input-rate" placeholder="1" style="direction:rtl; width:50px !important">
|
||||
<label for="node-input-reateUnits">message(s) per</label>
|
||||
<input type="text" id="node-input-rate" placeholder="1" style="direction:rtl; width:30px !important">
|
||||
<label for="node-input-reateUnits">msg(s) per</label>
|
||||
<select id="node-input-rateUnits" style="width:140px !important">
|
||||
<option value="second">Second</option>
|
||||
<option value="minute">Minute</option>
|
||||
<option value="hour">Hour</option>
|
||||
<option value="day">Day</option>
|
||||
</select>
|
||||
<br/>
|
||||
<input style="margin: 20px 0 20px 100px; width: 30px;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop">drop intermediate messages</label>
|
||||
</div>
|
||||
|
||||
<div id="random-details" class="form-row">
|
||||
<label for="node-input-randomFirst"><i class="icon-time"></i> Between</label>
|
||||
<input type="text" id="node-input-randomFirst" placeholder="" style="directon:rtl; width:30px !important">
|
||||
<label for="node-input-randomLast" style="width:20px"> & </label>
|
||||
<input type="text" id="node-input-randomLast" placeholder="" style="directon:rtl; width:30px !important">
|
||||
<select id="node-input-randomUnits" style="width:140px !important">
|
||||
<option value="milliseconds">Milliseconds</option>
|
||||
<option value="seconds">Seconds</option>
|
||||
<option value="minutes">Minutes</option>
|
||||
<option value="hours">Hours</option>
|
||||
<option value="days">Days</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
@@ -72,11 +89,15 @@
|
||||
timeout: {value:"5", required:true, validate:RED.validators.number()},
|
||||
timeoutUnits: {value:"seconds"},
|
||||
rate: {value:"1", required:true, validate:RED.validators.number()},
|
||||
rateUnits: {value: "second"}
|
||||
rateUnits: {value: "second"},
|
||||
randomFirst: {value:"1", required:true, validate:RED.validators.number()},
|
||||
randomLast: {value:"5", required:true, validate:RED.validators.number()},
|
||||
randomUnits: {value: "seconds"},
|
||||
drop: {value:false}
|
||||
},
|
||||
inputs:1, // set the number of inputs - only 0 or 1
|
||||
outputs:1, // set the number of outputs - 0 to n
|
||||
icon: "arrow-in.png", // set the icon (held in public/icons)
|
||||
icon: "timer.png", // set the icon (held in public/icons)
|
||||
label: function() { // sets the default label contents
|
||||
if (this.pauseType == "delay") {
|
||||
var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
|
||||
@@ -84,6 +105,8 @@
|
||||
} else if (this.pauseType == "rate") {
|
||||
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
|
||||
return this.name||"limit "+this.rate+" msg/"+ units;
|
||||
} else if (this.pauseType == "random") {
|
||||
return this.name || "random";
|
||||
}
|
||||
return "foo";
|
||||
},
|
||||
@@ -91,15 +114,24 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$( "#node-input-timeout" ).spinner();
|
||||
$( "#node-input-rate" ).spinner();
|
||||
$( "#node-input-timeout" ).spinner({min:1,max:60});
|
||||
$( "#node-input-rate" ).spinner({min:1});
|
||||
|
||||
$( "#node-input-randomFirst" ).spinner({min:0});
|
||||
$( "#node-input-randomLast" ).spinner({min:1});
|
||||
|
||||
if (this.pauseType == "delay") {
|
||||
$("#delay-details").show();
|
||||
$("#rate-details").hide();
|
||||
$("#random-details").hide();
|
||||
} else if (this.pauseType == "rate") {
|
||||
$("#delay-details").hide();
|
||||
$("#rate-details").show();
|
||||
$("#random-details").hide();
|
||||
} else if (this.pauseType == "random") {
|
||||
$("#delay-details").hide();
|
||||
$("#rate-details").hide();
|
||||
$("#random-details").show();
|
||||
}
|
||||
|
||||
if (!this.timeoutUnits) {
|
||||
@@ -108,13 +140,25 @@
|
||||
}).attr('selected', true);
|
||||
}
|
||||
|
||||
if (!this.randomUnits) {
|
||||
$("#node-input-randomUnits option").filter(function() {
|
||||
return $(this).val() == 'seconds';
|
||||
}).attr('selected', true);
|
||||
}
|
||||
|
||||
$("#node-input-pauseType").on("change",function() {
|
||||
if (this.value == "delay") {
|
||||
$("#delay-details").show();
|
||||
$("#rate-details").hide();
|
||||
$("#random-details").hide();
|
||||
} else if (this.value == "rate") {
|
||||
$("#delay-details").hide();
|
||||
$("#rate-details").show();
|
||||
$("#random-details").hide();
|
||||
} else if (this.value == "random") {
|
||||
$("#delay-details").hide();
|
||||
$("#rate-details").hide();
|
||||
$("#random-details").show();
|
||||
}
|
||||
});
|
||||
}
|
154
nodes/core/core/89-delay.js
Normal file
154
nodes/core/core/89-delay.js
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
//Simple node to introduce a pause into a flow
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
function random(n) {
|
||||
var wait = n.randomFirst + (n.diff * Math.random());
|
||||
if (n.buffer.length > 0) {
|
||||
n.send(n.buffer.pop());
|
||||
n.randomID = setTimeout(function() {random(n);},wait);
|
||||
} else {
|
||||
n.randomID = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function DelayNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.pauseType = n.pauseType;
|
||||
this.timeoutUnits = n.timeoutUnits;
|
||||
this.randomUnits = n.randomUnits;
|
||||
this.rateUnits = n.rateUnits;
|
||||
|
||||
if (n.timeoutUnits === "milliseconds") {
|
||||
this.timeout = n.timeout;
|
||||
} else if (n.timeoutUnits === "seconds") {
|
||||
this.timeout = n.timeout * 1000;
|
||||
} else if (n.timeoutUnits === "minutes") {
|
||||
this.timeout = n.timeout * (60 * 1000);
|
||||
} else if (n.timeoutUnits === "hours") {
|
||||
this.timeout = n.timeout * (60 * 60 * 1000);
|
||||
} else if (n.timeoutUnits === "days") {
|
||||
this.timeout = n.timeout * (24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
if (n.rateUnits === "second") {
|
||||
this.rate = 1000/n.rate;
|
||||
} else if (n.rateUnits === "minute") {
|
||||
this.rate = (60 * 1000)/n.rate;
|
||||
} else if (n.rateUnits === "hour") {
|
||||
this.rate = (60 * 60 * 1000)/n.rate;
|
||||
} else if (n.rateUnits === "day") {
|
||||
this.rate = (24 * 60 * 60 * 1000)/n.rate;
|
||||
}
|
||||
|
||||
if (n.randomUnits === "milliseconds") {
|
||||
this.randomFirst = n.randomFirst;
|
||||
this.randomLast = n.randomLast;
|
||||
} else if (n.randomUnits === "seconds") {
|
||||
this.randomFirst = n.randomFirst * 1000;
|
||||
this.randomLast = n.randomLast * 1000;
|
||||
} else if (n.randomUnits === "minutes") {
|
||||
this.randomFirst = n.randomFirst * (60 * 1000);
|
||||
this.randomLast = n.randomLast * (60 * 1000);
|
||||
} else if (n.randomUnits === "hours") {
|
||||
this.randomFirst = n.randomFirst * (60 * 60 * 1000);
|
||||
this.randomLast = n.randomLast * (60 * 60 * 1000);
|
||||
} else if (n.randomUnits === "days") {
|
||||
this.randomFirst = n.randomFirst * (24 * 60 * 60 * 1000);
|
||||
this.randomLast = n.randomLast * (24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
this.diff = this.randomLast - this.randomFirst;
|
||||
this.name = n.name;
|
||||
this.idList = [];
|
||||
this.buffer = [];
|
||||
this.intervalID = -1;
|
||||
this.randomID = -1;
|
||||
this.lastSent = Date.now();
|
||||
var node = this;
|
||||
|
||||
if (this.pauseType === "delay") {
|
||||
this.on("input", function(msg) {
|
||||
var id;
|
||||
id = setTimeout(function(){
|
||||
node.idList.splice(node.idList.indexOf(id),1);
|
||||
node.send(msg);
|
||||
}, node.timeout);
|
||||
this.idList.push(id);
|
||||
});
|
||||
|
||||
this.on("close", function() {
|
||||
for (var i=0; i<this.idList.length; i++ ) {
|
||||
clearTimeout(this.idList[i]);
|
||||
}
|
||||
this.idList = [];
|
||||
});
|
||||
|
||||
} else if (this.pauseType === "rate") {
|
||||
this.on("input", function(msg) {
|
||||
if (node.drop) {
|
||||
if ( node.intervalID !== -1) {
|
||||
node.buffer.push(msg);
|
||||
if (node.buffer.length > 1000) {
|
||||
node.warn(this.name + " buffer exceeded 1000 messages");
|
||||
}
|
||||
} else {
|
||||
node.send(msg);
|
||||
node.intervalID = setInterval(function() {
|
||||
if (node.buffer.length === 0) {
|
||||
clearInterval(node.intervalID);
|
||||
node.intervalID = -1;
|
||||
}
|
||||
|
||||
if (node.buffer.length > 0) {
|
||||
node.send(node.buffer.shift());
|
||||
}
|
||||
},node.rate);
|
||||
}
|
||||
} else {
|
||||
var now = Date.now();
|
||||
if (now-node.lastSent > node.rate) {
|
||||
node.lastSent = now;
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.on("close", function() {
|
||||
clearInterval(this.intervalID);
|
||||
this.buffer = [];
|
||||
});
|
||||
|
||||
} else if (this.pauseType === "random") {
|
||||
this.on("input",function(msg){
|
||||
node.buffer.push(msg);
|
||||
if (node.randomID === -1) {
|
||||
var wait = node.randomFirst + (node.diff * Math.random());
|
||||
node.randomID = setTimeout(function() {random(node);},wait);
|
||||
}
|
||||
});
|
||||
|
||||
this.on("close", function (){
|
||||
if (this.randomID !== -1) {
|
||||
clearTimeout(this.randomID);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("delay",DelayNode);
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
function CommentNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
49
nodes/core/core/98-unknown.html
Normal file
49
nodes/core/core/98-unknown.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="unknown">
|
||||
<div class="form-tips"><p>This node is a type unknown to your installation of Node-RED.</p>
|
||||
<p><i>If you deploy with the node in this state, it will lose all of its configuration.</i></p>
|
||||
<p>See the Info side bar for more help</p></div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="unknown">
|
||||
<p>This node is a type unknown to your installation of Node-RED.</p>
|
||||
<p><i>If you deploy with the node in this state, it will lose all of its configuration.</i></p>
|
||||
<p>It is possible this node type is already installed, but is missing a dependency. Check the Node-RED start-up log for
|
||||
any error messages associated with the missing node type. Use <b>npm install <module></b> to install any missing modules
|
||||
and restart Node-RED and reimport the nodes.</p>
|
||||
<p>Otherwise, you should contact the author of the flow to obtain a copy of the missing node type.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('unknown',{
|
||||
category: 'unknown',
|
||||
color:"#fff0f0",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "",
|
||||
label: function() {
|
||||
return "("+this.name+")"||"unknown";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return "node_label_unknown";
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -14,21 +14,9 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var util = require("util");
|
||||
var WordPos = require('wordpos');
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
var wordpos = new WordPos();
|
||||
|
||||
function WordPOSNode(n) {
|
||||
function UnknownNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.on("input", function(msg) {
|
||||
var node = this;
|
||||
wordpos.getPOS(msg.payload, function (result) {
|
||||
msg.pos = result;
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("wordpos",WordPOSNode);
|
||||
RED.nodes.registerType("unknown",UnknownNode);
|
@@ -41,7 +41,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('httpget',{
|
||||
category: 'advanced-function',
|
||||
category: 'deprecated',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""},
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
|
||||
function HttpGet(n) {
|
||||
RED.nodes.createNode(this,n);
|
@@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var util = require("util");
|
||||
var firmata = require("firmata");
|
||||
var arduinoReady = false;
|
||||
var thisboard = null;
|
||||
var pins = [];
|
||||
|
||||
// The Board Definition - this opens (and closes) the connection
|
||||
function ArduinoNode(n) {
|
||||
@@ -75,10 +74,6 @@ function DuinoNodeIn(n) {
|
||||
this.state = n.state;
|
||||
this.arduino = n.arduino;
|
||||
this.serverConfig = RED.nodes.getNode(this.arduino);
|
||||
if (pins.indexOf(this.pin) > -1) {
|
||||
this.error("Arduino pin being used more than once");
|
||||
}
|
||||
else pins.push(this.pin);
|
||||
if (typeof this.serverConfig === "object") {
|
||||
this.board = this.serverConfig.board;
|
||||
this.repeat = this.serverConfig.repeat;
|
||||
@@ -133,10 +128,6 @@ function DuinoNodeOut(n) {
|
||||
this.pin = n.pin;
|
||||
this.state = n.state;
|
||||
this.arduino = n.arduino;
|
||||
if (pins.indexOf(this.pin) > -1) {
|
||||
this.error("Arduino pin being used more than once");
|
||||
}
|
||||
else pins.push(this.pin);
|
||||
this.serverConfig = RED.nodes.getNode(this.arduino);
|
||||
if (typeof this.serverConfig === "object") {
|
||||
this.board = this.serverConfig.board;
|
@@ -18,15 +18,24 @@
|
||||
<div class="form-row">
|
||||
<label for="node-input-pin"><i class="icon-asterisk"></i> GPIO Pin</label>
|
||||
<select type="text" id="node-input-pin" style="width: 150px;">
|
||||
<option value="-">select pin</option>
|
||||
<option value="7">7</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="18">18</option>
|
||||
<option value="22">22</option>
|
||||
<option value="-" disabled>select pin </option>
|
||||
<option value="3">3 - SDA0 </option>
|
||||
<option value="5">5 - SCL0 </option>
|
||||
<option value="7">7 - GPIO7</option>
|
||||
<option value="8">8 - TxD </option>
|
||||
<option value="10">10 - RxD </option>
|
||||
<option value="11">11 - GPIO0</option>
|
||||
<option value="12">12 - GPIO1</option>
|
||||
<option value="13">13 - GPIO2</option>
|
||||
<option value="15">15 - GPIO3</option>
|
||||
<option value="16">16 - GPIO4</option>
|
||||
<option value="18">18 - GPIO5</option>
|
||||
<option value="19">19 - MOSI </option>
|
||||
<option value="21">21 - MISO </option>
|
||||
<option value="22">22 - GPIO6</option>
|
||||
<option value="23">23 - SCLK </option>
|
||||
<option value="24">24 - CE0 </option>
|
||||
<option value="26">26 - CE1 </option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -46,11 +55,11 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="rpi-gpio in">
|
||||
<p>Raspberry Pi input node. Generates a <b>msg.payload</b> with either a 0 or 1 depending on the state of the input pin. Requires the gpio command to work.</p>
|
||||
<p>You may also enable the input pullup resitor or the pulldown resistor.</p>
|
||||
<p>The <b>msg.topic</b> is set to <i>pi/{the pin number}</i></p>
|
||||
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
|
||||
<p><b>Note:</b> This node currently polls the pin every 250mS. This is not ideal as it loads the cpu, and will be rewritten shortly to try to use interrupts.</p>
|
||||
<p>Raspberry Pi input node. Generates a <b>msg.payload</b> with either a 0 or 1 depending on the state of the input pin. Requires the gpio command to work.</p>
|
||||
<p>You may also enable the input pullup resitor or the pulldown resistor.</p>
|
||||
<p>The <b>msg.topic</b> is set to <i>pi/{the pin number}</i></p>
|
||||
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
|
||||
<p><b>Note:</b> This node currently polls the pin every 250mS. This is not ideal as it loads the cpu, and will be rewritten shortly to try to use interrupts.</p>
|
||||
|
||||
</script>
|
||||
|
||||
@@ -80,15 +89,24 @@
|
||||
<div class="form-row">
|
||||
<label for="node-input-pin"><i class="icon-asterisk"></i> GPIO Pin</label>
|
||||
<select type="text" id="node-input-pin" style="width: 150px;">
|
||||
<option value="-">select pin</option>
|
||||
<option value="7">7</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="18">18</option>
|
||||
<option value="22">22</option>
|
||||
<option value="-">select pin </option>
|
||||
<option value="3">3 - SDA0 </option>
|
||||
<option value="5">5 - SCL0 </option>
|
||||
<option value="7">7 - GPIO7</option>
|
||||
<option value="8">8 - TxD </option>
|
||||
<option value="10">10 - RxD </option>
|
||||
<option value="11">11 - GPIO0</option>
|
||||
<option value="12">12 - GPIO1</option>
|
||||
<option value="13">13 - GPIO2</option>
|
||||
<option value="15">15 - GPIO3</option>
|
||||
<option value="16">16 - GPIO4</option>
|
||||
<option value="18">18 - GPIO5</option>
|
||||
<option value="19">19 - MOSI </option>
|
||||
<option value="21">21 - MISO </option>
|
||||
<option value="22">22 - GPIO6</option>
|
||||
<option value="23">23 - SCLK </option>
|
||||
<option value="24">24 - CE0 </option>
|
||||
<option value="26">26 - CE1 </option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -99,9 +117,9 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="rpi-gpio out">
|
||||
<p>Raspberry Pi output node. Expects a <b>msg.payload</b> with either a 0 or 1 (or true or false). Requires the gpio command to work.</p>
|
||||
<p>Will set the selected physical pin high or low depending on the value passed in.</p>
|
||||
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
|
||||
<p>Raspberry Pi output node. Expects a <b>msg.payload</b> with either a 0 or 1 (or true or false). Requires the gpio command to work.</p>
|
||||
<p>Will set the selected physical pin high or low depending on the value passed in.</p>
|
||||
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
158
nodes/core/hardware/36-rpi-gpio.js
Normal file
158
nodes/core/hardware/36-rpi-gpio.js
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var util = require("util");
|
||||
var exec = require('child_process').exec;
|
||||
var fs = require('fs');
|
||||
|
||||
if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi
|
||||
throw "Info : Ignoring Raspberry Pi specific node.";
|
||||
}
|
||||
|
||||
if (!fs.existsSync("/usr/local/bin/gpio")) { // gpio command not installed
|
||||
throw "Info : Can't find Raspberry Pi wiringPi gpio command.";
|
||||
}
|
||||
|
||||
// Map physical P1 pins to Gordon's Wiring-Pi Pins (as they should be V1/V2 tolerant)
|
||||
var pintable = {
|
||||
// Physical : WiringPi
|
||||
"11":"0",
|
||||
"12":"1",
|
||||
"13":"2",
|
||||
"15":"3",
|
||||
"16":"4",
|
||||
"18":"5",
|
||||
"22":"6",
|
||||
"7":"7",
|
||||
"3":"8",
|
||||
"5":"9",
|
||||
"24":"10",
|
||||
"26":"11",
|
||||
"19":"12",
|
||||
"21":"13",
|
||||
"23":"14",
|
||||
"8":"15",
|
||||
"10":"16"
|
||||
}
|
||||
var tablepin = {
|
||||
// WiringPi : Physical
|
||||
"0":"11",
|
||||
"1":"12",
|
||||
"2":"13",
|
||||
"3":"15",
|
||||
"4":"16",
|
||||
"5":"18",
|
||||
"6":"22",
|
||||
"7":"7",
|
||||
"8":"3",
|
||||
"9":"5",
|
||||
"10":"24",
|
||||
"11":"26",
|
||||
"12":"19",
|
||||
"13":"21",
|
||||
"14":"23",
|
||||
"15":"8",
|
||||
"16":"10"
|
||||
}
|
||||
|
||||
function GPIOInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.buttonState = -1;
|
||||
this.pin = pintable[n.pin];
|
||||
this.intype = n.intype;
|
||||
var node = this;
|
||||
|
||||
if (this.pin) {
|
||||
exec("gpio mode "+node.pin+" "+node.intype, function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
else {
|
||||
node._interval = setInterval( function() {
|
||||
exec("gpio read "+node.pin, function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
else {
|
||||
if (node.buttonState !== Number(stdout)) {
|
||||
var previousState = node.buttonState;
|
||||
node.buttonState = Number(stdout);
|
||||
if (previousState !== -1) {
|
||||
var msg = {topic:"pi/"+tablepin[node.pin], payload:node.buttonState};
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.error("Invalid GPIO pin: "+this.pin);
|
||||
}
|
||||
|
||||
this.on("close", function() {
|
||||
clearInterval(this._interval);
|
||||
});
|
||||
}
|
||||
|
||||
function GPIOOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.pin = pintable[n.pin];
|
||||
var node = this;
|
||||
|
||||
if (this.pin) {
|
||||
process.nextTick(function() {
|
||||
exec("gpio mode "+node.pin+" out", function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
else {
|
||||
node.on("input", function(msg) {
|
||||
if (msg.payload === "true") msg.payload = true;
|
||||
if (msg.payload === "false") msg.payload = false;
|
||||
var out = Number(msg.payload);
|
||||
if ((out == 0)|(out == 1)) {
|
||||
exec("gpio write "+node.pin+" "+out, function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
});
|
||||
}
|
||||
else node.warn("Invalid input - not 0 or 1");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.error("Invalid GPIO pin: "+this.pin);
|
||||
}
|
||||
|
||||
this.on("close", function() {
|
||||
exec("gpio mode "+this.pin+" in");
|
||||
});
|
||||
}
|
||||
|
||||
exec("gpio mode 0 in",function(err,stdout,stderr) {
|
||||
if (err) {
|
||||
util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.');
|
||||
}
|
||||
exec("gpio mode 1 in");
|
||||
exec("gpio mode 2 in");
|
||||
exec("gpio mode 3 in");
|
||||
exec("gpio mode 4 in");
|
||||
exec("gpio mode 5 in");
|
||||
exec("gpio mode 6 in");
|
||||
exec("gpio mode 7 in");
|
||||
});
|
||||
|
||||
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
|
||||
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
|
@@ -30,8 +30,9 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="mqtt in">
|
||||
<p>MQTT input node. Connects to the specified broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p>
|
||||
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>. <b>msg.payload</b> is a String.</p>
|
||||
<p>MQTT input node. Connects to a broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p>
|
||||
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>.</p>
|
||||
<p><b>msg.payload</b> is a String.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -42,7 +43,7 @@
|
||||
topic: {value:"",required:true},
|
||||
broker: {type:"mqtt-broker", required:true}
|
||||
},
|
||||
color:"#c6dbef",
|
||||
color:"#d8bfd8",
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "bridge.png",
|
||||
@@ -71,9 +72,9 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="mqtt out">
|
||||
<p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> OR to the topic specified in the edit window. The value in the edit window has precedence.</p>
|
||||
<p><b>msg.qos</b> and <b>msg.retain</b> may also optionally have been set. If not set they are set to 0 and false respectively.</p>
|
||||
<p>If <b>msg.payload</b> contains a buffer or an object it will be stringified before being sent.</p>
|
||||
<p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> OR to the topic specified in the edit window. The value in the edit window has precedence.</p>
|
||||
<p><b>msg.qos</b> and <b>msg.retain</b> may also optionally have been set. If not set they are set to 0 and false respectively.</p>
|
||||
<p>If <b>msg.payload</b> contains a buffer or an object it will be stringified before being sent.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -84,7 +85,7 @@
|
||||
topic: {value:""},
|
||||
broker: {type:"mqtt-broker", required:true}
|
||||
},
|
||||
color:"#c6dbef",
|
||||
color:"#d8bfd8",
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "bridge.png",
|
||||
@@ -105,6 +106,18 @@
|
||||
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
|
||||
<input type="text" id="node-config-input-port" placeholder="Port" style="width:45px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-clientid"><i class="icon-tag"></i> Client ID</label>
|
||||
<input type="text" id="node-config-input-clientid" placeholder="Leave blank for auto generated">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-user"><i class="icon-user"></i> Username</label>
|
||||
<input type="text" id="node-config-input-user">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-pass"><i class="icon-lock"></i> Password</label>
|
||||
<input type="password" id="node-config-input-pass">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -112,10 +125,49 @@
|
||||
category: 'config',
|
||||
defaults: {
|
||||
broker: {value:"localhost",required:true},
|
||||
port: {value:1883,required:true,validate:RED.validators.number()}
|
||||
port: {value:1883,required:true,validate:RED.validators.number()},
|
||||
clientid: { value:"" }
|
||||
//user -> credentials
|
||||
//pass -> credentials
|
||||
|
||||
},
|
||||
label: function() {
|
||||
return this.broker+":"+this.port;
|
||||
return (this.clientid?this.clientid+"@":"")+this.broker+":"+this.port;
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$.getJSON('mqtt-broker/'+this.id,function(data) {
|
||||
if (data.user) {
|
||||
$('#node-config-input-user').val(data.user);
|
||||
}
|
||||
if (data.hasPassword) {
|
||||
$('#node-config-input-pass').val('__PWRD__');
|
||||
} else {
|
||||
$('#node-config-input-pass').val('');
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
var newUser = $('#node-config-input-user').val();
|
||||
var newPass = $('#node-config-input-pass').val();
|
||||
var credentials = {};
|
||||
credentials.user = newUser;
|
||||
if (newPass != '__PWRD__') {
|
||||
credentials.password = newPass;
|
||||
}
|
||||
$.ajax({
|
||||
url: 'mqtt-broker/'+this.id,
|
||||
type: 'POST',
|
||||
data: credentials,
|
||||
success:function(result){}
|
||||
});
|
||||
},
|
||||
ondelete: function() {
|
||||
$.ajax({
|
||||
url: 'mqtt-broker/'+this.id,
|
||||
type: 'DELETE',
|
||||
success: function(result) {}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var connectionPool = require("./lib/mqttConnectionPool");
|
||||
var util = require("util");
|
||||
|
||||
@@ -22,9 +22,54 @@ function MQTTBrokerNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.broker = n.broker;
|
||||
this.port = n.port;
|
||||
this.clientid = n.clientid;
|
||||
var credentials = RED.nodes.getCredentials(n.id);
|
||||
if (credentials) {
|
||||
this.username = credentials.user;
|
||||
this.password = credentials.password;
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("mqtt-broker",MQTTBrokerNode);
|
||||
|
||||
var querystring = require('querystring');
|
||||
|
||||
RED.httpAdmin.get('/mqtt-broker/:id',function(req,res) {
|
||||
var credentials = RED.nodes.getCredentials(req.params.id);
|
||||
if (credentials) {
|
||||
res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")}));
|
||||
} else {
|
||||
res.send(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
RED.httpAdmin.delete('/mqtt-broker/:id',function(req,res) {
|
||||
RED.nodes.deleteCredentials(req.params.id);
|
||||
res.send(200);
|
||||
});
|
||||
|
||||
RED.httpAdmin.post('/mqtt-broker/:id',function(req,res) {
|
||||
var body = "";
|
||||
req.on('data', function(chunk) {
|
||||
body+=chunk;
|
||||
});
|
||||
req.on('end', function(){
|
||||
var newCreds = querystring.parse(body);
|
||||
var credentials = RED.nodes.getCredentials(req.params.id)||{};
|
||||
if (newCreds.user == null || newCreds.user == "") {
|
||||
delete credentials.user;
|
||||
} else {
|
||||
credentials.user = newCreds.user;
|
||||
}
|
||||
if (newCreds.password == "") {
|
||||
delete credentials.password;
|
||||
} else {
|
||||
credentials.password = newCreds.password||credentials.password;
|
||||
}
|
||||
RED.nodes.addCredentials(req.params.id,credentials);
|
||||
res.send(200);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function MQTTInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
@@ -32,7 +77,7 @@ function MQTTInNode(n) {
|
||||
this.broker = n.broker;
|
||||
this.brokerConfig = RED.nodes.getNode(this.broker);
|
||||
if (this.brokerConfig) {
|
||||
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port);
|
||||
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password);
|
||||
var node = this;
|
||||
this.client.subscribe(this.topic,2,function(topic,payload,qos,retain) {
|
||||
var msg = {topic:topic,payload:payload,qos:qos,retain:retain};
|
||||
@@ -65,7 +110,7 @@ function MQTTOutNode(n) {
|
||||
this.brokerConfig = RED.nodes.getNode(this.broker);
|
||||
|
||||
if (this.brokerConfig) {
|
||||
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port);
|
||||
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password);
|
||||
this.on("input",function(msg) {
|
||||
if (msg != null) {
|
||||
if (this.topic) {
|
@@ -32,6 +32,7 @@
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div id="node-input-tip" class="form-tips">The url will be relative to <code><span id="node-input-path"></span></code>.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="http in">
|
||||
@@ -63,27 +64,6 @@
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('http in',{
|
||||
category: 'input',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
url: {value:"",required:true},
|
||||
method: {value:"get",required:true}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "white-globe.png",
|
||||
label: function() {
|
||||
return this.name||(this.url?("["+this.method+"] "+this.url):"http");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="http response">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
@@ -103,29 +83,6 @@
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('http response',{
|
||||
category: 'output',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
align: "right",
|
||||
icon: "white-globe.png",
|
||||
label: function() {
|
||||
return this.name||"http";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/x-red" data-template-name="http request">
|
||||
<div class="form-row">
|
||||
<label for="node-input-method"><i class="icon-tasks"></i> Method</label>
|
||||
@@ -140,6 +97,19 @@
|
||||
<label for="node-input-url"><i class="icon-tasks"></i> URL</label>
|
||||
<input type="text" id="node-input-url" placeholder="http://">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-useAuth" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-useAuth" style="width: 70%;">Use basic authentication?</label>
|
||||
</div>
|
||||
<div class="form-row node-input-useAuth-row">
|
||||
<label for="node-config-input-user"><i class="icon-user"></i> Username</label>
|
||||
<input type="text" id="node-config-input-user">
|
||||
</div>
|
||||
<div class="form-row node-input-useAuth-row">
|
||||
<label for="node-config-input-pass"><i class="icon-lock"></i> Password</label>
|
||||
<input type="password" id="node-config-input-pass">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
@@ -157,7 +127,10 @@
|
||||
pairs to be added as request headers</li>
|
||||
<li><code>payload</code> is sent as the body of the request</li>
|
||||
</ul>
|
||||
|
||||
<p>When configured within the node, the URL property can contain <a href="http://mustache.github.io/mustache.5.html" target="_new">mustache-style</a> tags. These allow the
|
||||
url to be constructed using values of the incoming message. For example, if the url is set to
|
||||
<code>example.com/{{topic}}</code>, it will have the value of <code>msg.topic</code> automatically inserted.</p>
|
||||
<p>
|
||||
The output message contains the following properties:
|
||||
<ul>
|
||||
<li><code>payload</code> is the body of the response</li>
|
||||
@@ -167,13 +140,71 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('http in',{
|
||||
category: 'input',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
url: {value:"",required:true},
|
||||
method: {value:"get",required:true}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "white-globe.png",
|
||||
label: function() {
|
||||
if (this.name) {
|
||||
return this.name;
|
||||
} else if (this.url) {
|
||||
var root = RED.settings.httpNodeRoot.slice(0,-1);
|
||||
root += this.url;
|
||||
return "["+this.method+"] "+root;
|
||||
} else {
|
||||
return "http";
|
||||
}
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var root = RED.settings.httpNodeRoot.slice(0,-1);
|
||||
if (root == "") {
|
||||
$("#node-input-tip").hide();
|
||||
} else {
|
||||
$("#node-input-path").html(root);
|
||||
$("#node-input-tip").show();
|
||||
}
|
||||
//document.getElementById("node-config-wsdocpath").innerHTML=
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
RED.nodes.registerType('http response',{
|
||||
category: 'output',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
align: "right",
|
||||
icon: "white-globe.png",
|
||||
label: function() {
|
||||
return this.name||"http";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
RED.nodes.registerType('http request',{
|
||||
category: 'function',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
method:{value:"GET"},
|
||||
url:{value:""}
|
||||
url:{value:""},
|
||||
//user -> credentials
|
||||
//pass -> credentials
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
@@ -184,6 +215,72 @@
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$.getJSON('http-request/'+this.id,function(data) {
|
||||
if (data.user) {
|
||||
$('#node-input-useAuth').prop('checked', true);
|
||||
$(".node-input-useAuth-row").show();
|
||||
$('#node-config-input-user').data("v",data.user);
|
||||
$('#node-config-input-user').val(data.user);
|
||||
} else {
|
||||
$('#node-input-useAuth').prop('checked', false);
|
||||
$(".node-input-useAuth-row").hide();
|
||||
$('#node-config-input-user').data("v",'');
|
||||
}
|
||||
if (data.hasPassword) {
|
||||
$('#node-input-useAuth').prop('checked', true);
|
||||
$(".node-input-useAuth-row").show();
|
||||
$('#node-config-input-pass').data("v",'__PWRD__');
|
||||
$('#node-config-input-pass').val('__PWRD__');
|
||||
} else {
|
||||
$('#node-config-input-pass').data("v",'');
|
||||
$('#node-config-input-pass').val('');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$("#node-input-useAuth").change(function() {
|
||||
if ($(this).is(":checked")) {
|
||||
$(".node-input-useAuth-row").show();
|
||||
} else {
|
||||
$(".node-input-useAuth-row").hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
var oldUser = $('#node-config-input-user').data("v");
|
||||
var oldPass = $('#node-config-input-pass').data("v");
|
||||
var newUser = $('#node-config-input-user').val();
|
||||
var newPass = $('#node-config-input-pass').val();
|
||||
|
||||
if (!$("#node-input-useAuth").is(":checked")) {
|
||||
newUser = "";
|
||||
newPass = "";
|
||||
}
|
||||
|
||||
if (oldUser != newUser || oldPass != newPass) {
|
||||
if (newUser == "" && newPass == "") {
|
||||
$.ajax({
|
||||
url: 'http-request/'+this.id,
|
||||
type: 'DELETE',
|
||||
success: function(result) {}
|
||||
});
|
||||
} else {
|
||||
var credentials = {};
|
||||
credentials.user = newUser;
|
||||
if (newPass != '__PWRD__') {
|
||||
credentials.password = newPass;
|
||||
}
|
||||
$.ajax({
|
||||
url: 'http-request/'+this.id,
|
||||
type: 'POST',
|
||||
data: credentials,
|
||||
success:function(result){}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
226
nodes/core/io/21-httpin.js
Normal file
226
nodes/core/io/21-httpin.js
Normal file
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var util = require("util");
|
||||
var http = require("follow-redirects").http;
|
||||
var https = require("follow-redirects").https;
|
||||
var urllib = require("url");
|
||||
var express = require("express");
|
||||
var getBody = require('raw-body');
|
||||
var mustache = require("mustache");
|
||||
|
||||
var cors = require('cors');
|
||||
var jsonParser = express.json();
|
||||
var urlencParser = express.urlencoded();
|
||||
|
||||
function rawBodyParser(req, res, next) {
|
||||
if (req._body) return next();
|
||||
req.body = "";
|
||||
req._body = true;
|
||||
getBody(req, {
|
||||
limit: '1mb',
|
||||
length: req.headers['content-length'],
|
||||
encoding: 'utf8'
|
||||
}, function (err, buf) {
|
||||
if (err) return next(err);
|
||||
req.body = buf;
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function HTTPIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
if (RED.settings.httpNodeRoot !== false) {
|
||||
|
||||
this.url = n.url;
|
||||
this.method = n.method;
|
||||
|
||||
var node = this;
|
||||
|
||||
this.errorHandler = function(err,req,res,next) {
|
||||
node.warn(err);
|
||||
res.send(500);
|
||||
};
|
||||
|
||||
this.callback = function(req,res) {
|
||||
if (node.method == "post") {
|
||||
node.send({req:req,res:res,payload:req.body});
|
||||
} else if (node.method == "get") {
|
||||
node.send({req:req,res:res,payload:req.query});
|
||||
} else {
|
||||
node.send({req:req,res:res});
|
||||
}
|
||||
}
|
||||
|
||||
var corsHandler = function(req,res,next) { next(); }
|
||||
|
||||
if (RED.settings.httpNodeCors) {
|
||||
corsHandler = cors(RED.settings.httpNodeCors);
|
||||
RED.httpNode.options(this.url,corsHandler);
|
||||
}
|
||||
|
||||
if (this.method == "get") {
|
||||
RED.httpNode.get(this.url,corsHandler,this.callback,this.errorHandler);
|
||||
} else if (this.method == "post") {
|
||||
RED.httpNode.post(this.url,corsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
} else if (this.method == "put") {
|
||||
RED.httpNode.put(this.url,corsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
} else if (this.method == "delete") {
|
||||
RED.httpNode.delete(this.url,corsHandler,this.callback,errorHandler);
|
||||
}
|
||||
|
||||
this.on("close",function() {
|
||||
var routes = RED.httpNode.routes[this.method];
|
||||
for (var i = 0; i<routes.length; i++) {
|
||||
if (routes[i].path == this.url) {
|
||||
routes.splice(i,1);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
if (RED.settings.httpNodeCors) {
|
||||
var routes = RED.httpNode.routes['options'];
|
||||
for (var i = 0; i<routes.length; i++) {
|
||||
if (routes[i].path == this.url) {
|
||||
routes.splice(i,1);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.warn("Cannot create http-in node when httpNodeRoot set to false");
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("http in",HTTPIn);
|
||||
|
||||
|
||||
function HTTPOut(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
this.on("input",function(msg) {
|
||||
if (msg.res) {
|
||||
if (msg.headers) {
|
||||
msg.res.set(msg.headers);
|
||||
}
|
||||
var statusCode = msg.statusCode || 200;
|
||||
if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
|
||||
msg.res.jsonp(statusCode,msg.payload);
|
||||
} else {
|
||||
msg.res.send(statusCode,msg.payload);
|
||||
}
|
||||
} else {
|
||||
node.warn("No response object");
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("http response",HTTPOut);
|
||||
|
||||
function HTTPRequest(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var nodeUrl = n.url;
|
||||
var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
|
||||
var nodeMethod = n.method || "GET";
|
||||
var node = this;
|
||||
var credentials = RED.nodes.getCredentials(n.id);
|
||||
this.on("input",function(msg) {
|
||||
var url;
|
||||
if (msg.url) {
|
||||
url = msg.url;
|
||||
} else if (isTemplatedUrl) {
|
||||
url = mustache.render(nodeUrl,msg);
|
||||
} else {
|
||||
url = nodeUrl;
|
||||
}
|
||||
var method = (msg.method||nodeMethod).toUpperCase();
|
||||
var opts = urllib.parse(url);
|
||||
opts.method = method;
|
||||
if (msg.headers) {
|
||||
opts.headers = msg.headers;
|
||||
}
|
||||
if (credentials) {
|
||||
opts.auth = credentials.user+":"+(credentials.password||"");
|
||||
}
|
||||
var req = ((/^https/.test(url))?https:http).request(opts,function(res) {
|
||||
res.setEncoding('utf8');
|
||||
msg.statusCode = res.statusCode;
|
||||
msg.headers = res.headers;
|
||||
msg.payload = "";
|
||||
res.on('data',function(chunk) {
|
||||
msg.payload += chunk;
|
||||
});
|
||||
res.on('end',function() {
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
req.on('error',function(err) {
|
||||
msg.payload = err.toString();
|
||||
msg.statusCode = err.code;
|
||||
node.send(msg);
|
||||
});
|
||||
if (msg.payload && (method == "POST" || method == "PUT") ) {
|
||||
if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
|
||||
req.write(msg.payload);
|
||||
} else if (typeof msg.payload == "number") {
|
||||
req.write(msg.payload+"");
|
||||
} else {
|
||||
req.write(JSON.stringify(msg.payload));
|
||||
}
|
||||
}
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("http request",HTTPRequest);
|
||||
|
||||
var querystring = require('querystring');
|
||||
|
||||
RED.httpAdmin.get('/http-request/:id',function(req,res) {
|
||||
var credentials = RED.nodes.getCredentials(req.params.id);
|
||||
if (credentials) {
|
||||
res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")}));
|
||||
} else {
|
||||
res.send(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
RED.httpAdmin.delete('/http-request/:id',function(req,res) {
|
||||
RED.nodes.deleteCredentials(req.params.id);
|
||||
res.send(200);
|
||||
});
|
||||
|
||||
RED.httpAdmin.post('/http-request/:id',function(req,res) {
|
||||
var body = "";
|
||||
req.on('data', function(chunk) {
|
||||
body+=chunk;
|
||||
});
|
||||
req.on('end', function(){
|
||||
var newCreds = querystring.parse(body);
|
||||
var credentials = RED.nodes.getCredentials(req.params.id)||{};
|
||||
if (newCreds.user == null || newCreds.user == "") {
|
||||
delete credentials.user;
|
||||
} else {
|
||||
credentials.user = newCreds.user;
|
||||
}
|
||||
if (newCreds.password == "") {
|
||||
delete credentials.password;
|
||||
} else {
|
||||
credentials.password = newCreds.password||credentials.password;
|
||||
}
|
||||
RED.nodes.addCredentials(req.params.id,credentials);
|
||||
res.send(200);
|
||||
});
|
||||
});
|
153
nodes/core/io/22-websocket.html
Normal file
153
nodes/core/io/22-websocket.html
Normal file
@@ -0,0 +1,153 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- WebSocket Input Node -->
|
||||
<script type="text/x-red" data-template-name="websocket in">
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="icon-bookmark"></i> Path</label>
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="websocket in">
|
||||
<p>WebSocket input node.</p>
|
||||
<p>By default, the data received from the WebSocket will be in <b>msg.payload</b>.
|
||||
The listener can be configured to expect a properly formed JSON string, in which
|
||||
case it will parse the JSON and send on the resulting object as the entire message.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('websocket in',{
|
||||
category: 'input',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
server: {type:"websocket-listener"}
|
||||
},
|
||||
color:"rgb(215, 215, 160)",
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "white-globe.png",
|
||||
label: function() {
|
||||
var wsNode = RED.nodes.node(this.server);
|
||||
return this.name||(wsNode?"[ws] "+wsNode.label():"websocket");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- WebSocket out Node -->
|
||||
<script type="text/x-red" data-template-name="websocket out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-server"><i class="icon-bookmark"></i> Path</label>
|
||||
<input type="text" id="node-input-server">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="websocket out">
|
||||
<p>WebSocket out node.</p>
|
||||
<p>By default, <b>msg.payload</b> will be sent over the WebSocket. The listener
|
||||
can be configured to encode the entire message object as a JSON string and send that
|
||||
over the WebSocket.</p>
|
||||
|
||||
<p>If the message arriving at this node started at a WebSocket In node, the message
|
||||
will be sent back to the client that triggered the flow. Otherwise, the message
|
||||
will be broadcast to all conntected clients.</p>
|
||||
<p>If you want to broadcast a message that started at a WebSocket In node, you
|
||||
should delete the <b>msg._session</b> property within the flow</p>.
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('websocket out',{
|
||||
category: 'output',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
server: {type:"websocket-listener", required:true}
|
||||
},
|
||||
color:"rgb(215, 215, 160)",
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "white-globe.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
var wsNode = RED.nodes.node(this.server);
|
||||
return this.name||(wsNode?"[ws] "+wsNode.label():"websocket");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- WebSocket Server configuration node -->
|
||||
<script type="text/x-red" data-template-name="websocket-listener">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-path"><i class="icon-bookmark"></i> Path</label>
|
||||
<input type="text" id="node-config-input-path" placeholder="/ws/example">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-wholemsg"> </label>
|
||||
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
|
||||
<option value="false">Send/Receive payload</option>
|
||||
<option value="true">Send/Receive entire message</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-tips">
|
||||
Be default, <code>payload</code> will contain the data to be sent over, or received from a websocket.
|
||||
The listener can be configured to send or receive the entire message object as a JSON formatted string.
|
||||
<p id="node-config-ws-tip">This path will be relative to <code><span id="node-config-ws-path"></span></code>.</p>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="websocket-listener">
|
||||
<p>This configuration node creates a WebSocket Server using the specified path</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('websocket-listener',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/) },
|
||||
wholemsg: {value:"false"}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:0,
|
||||
label: function() {
|
||||
var root = RED.settings.httpNodeRoot.slice(0,-1);
|
||||
root += this.path;
|
||||
return root;
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var root = RED.settings.httpNodeRoot.slice(0,-1);
|
||||
if (root == "") {
|
||||
$("#node-config-ws-tip").hide();
|
||||
} else {
|
||||
$("#node-config-ws-path").html(root);
|
||||
$("#node-config-ws-tip").show();
|
||||
}
|
||||
//document.getElementById("node-config-wsdocpath").innerHTML=
|
||||
}
|
||||
});
|
||||
</script>
|
172
nodes/core/io/22-websocket.js
Normal file
172
nodes/core/io/22-websocket.js
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
// Require main module
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red"),
|
||||
ws = require("ws"),
|
||||
inspect = require("sys").inspect;
|
||||
|
||||
// A node red node that sets up a local websocket server
|
||||
function WebSocketListenerNode(n) {
|
||||
// Create a RED node
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
var node = this;
|
||||
|
||||
// Store local copies of the node configuration (as defined in the .html)
|
||||
node.path = n.path;
|
||||
node.wholemsg = (n.wholemsg === "true");
|
||||
|
||||
node._inputNodes = []; // collection of nodes that want to receive events
|
||||
|
||||
var path = RED.settings.httpNodeRoot || "/";
|
||||
path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
|
||||
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Listen for 'newListener' events from RED.server
|
||||
node._serverListeners = {};
|
||||
|
||||
var storeListener = function(/*String*/event,/*function*/listener){
|
||||
if(event == "error" || event == "upgrade" || event == "listening"){
|
||||
node._serverListeners[event] = listener;
|
||||
}
|
||||
}
|
||||
|
||||
node._clients = {};
|
||||
|
||||
RED.server.addListener('newListener',storeListener);
|
||||
|
||||
// Create a WebSocket Server
|
||||
node.server = new ws.Server({server:RED.server,path:path});
|
||||
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Stop listening for new listener events
|
||||
RED.server.removeListener('newListener',storeListener);
|
||||
|
||||
node.server.on('connection', function(socket){
|
||||
var id = (1+Math.random()*4294967295).toString(16);
|
||||
node._clients[id] = socket;
|
||||
|
||||
socket.on('close',function() {
|
||||
delete node._clients[id];
|
||||
});
|
||||
socket.on('message',function(data,flags){
|
||||
node.handleEvent(id,socket,'message',data,flags);
|
||||
});
|
||||
socket.on('error', function(err) {
|
||||
node.warn("An error occured on the ws connection: "+inspect(err));
|
||||
});
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Remove listeners from RED.server
|
||||
var listener = null;
|
||||
for(var event in node._serverListeners){
|
||||
listener = node._serverListeners[event];
|
||||
if(typeof listener === "function"){
|
||||
RED.server.removeListener(event,listener);
|
||||
}
|
||||
}
|
||||
node._serverListeners = {};
|
||||
|
||||
node.server.close();
|
||||
node._inputNodes = [];
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("websocket-listener",WebSocketListenerNode);
|
||||
|
||||
WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler){
|
||||
this._inputNodes.push(handler);
|
||||
}
|
||||
|
||||
WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){
|
||||
var msg;
|
||||
if (this.wholemsg) {
|
||||
msg = JSON.parse(data);
|
||||
} else {
|
||||
msg = {
|
||||
payload:data
|
||||
};
|
||||
}
|
||||
msg._session = {type:"websocket",id:id};
|
||||
|
||||
for (var i = 0; i < this._inputNodes.length; i++) {
|
||||
this._inputNodes[i].send(msg);
|
||||
};
|
||||
}
|
||||
|
||||
WebSocketListenerNode.prototype.broadcast = function(data){
|
||||
for(var i in this.server.clients){
|
||||
this.server.clients[i].send(data);
|
||||
};
|
||||
}
|
||||
|
||||
WebSocketListenerNode.prototype.send = function(id,data){
|
||||
var session = this._clients[id];
|
||||
if (session) {
|
||||
session.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
function WebSocketInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.server = n.server;
|
||||
var node = this;
|
||||
this.serverConfig = RED.nodes.getNode(this.server);
|
||||
if (this.serverConfig) {
|
||||
this.serverConfig.registerInputNode(this);
|
||||
} else {
|
||||
this.error("Missing server configuration");
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("websocket in",WebSocketInNode);
|
||||
|
||||
function WebSocketOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
this.server = n.server;
|
||||
this.serverConfig = RED.nodes.getNode(this.server);
|
||||
if (!this.serverConfig) {
|
||||
this.error("Missing server configuration");
|
||||
}
|
||||
this.on("input", function(msg) {
|
||||
var payload;
|
||||
if (this.serverConfig.wholemsg) {
|
||||
delete msg._session;
|
||||
payload = JSON.stringify(msg);
|
||||
} else {
|
||||
payload = msg.payload;
|
||||
if (Buffer.isBuffer(payload)) {
|
||||
payload = payload.toString();
|
||||
} else if (typeof payload === "object") {
|
||||
payload = JSON.stringify(payload);
|
||||
} else if (typeof payload !== "string") {
|
||||
payload = ""+payload;
|
||||
}
|
||||
}
|
||||
if (msg._session && msg._session.type == "websocket") {
|
||||
node.serverConfig.send(msg._session.id,payload);
|
||||
} else {
|
||||
node.serverConfig.broadcast(payload,function(error){
|
||||
if(!!error){
|
||||
node.warn("An error occurred while sending:" + inspect(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("websocket out",WebSocketOutNode);
|
@@ -23,13 +23,16 @@
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div id="node-input-tip" class="form-tips">On Windows you must use double slashes \\ in any directory names.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="watch">
|
||||
<p>Watches a file or directory for any changes.</p>
|
||||
<p>You can enter a list of comma separated files, or directories if you like. You will need to put " around any that have spaces in.</p>
|
||||
<p>The filename of the file that actually changed is put into <b>msg.payload</b>, while a stringified version of the watched criteria is returned in <b>msg.topic</b>.</p>
|
||||
<p>Of course in Linux, <i>everything</i> could be a file and thus watched...</p>
|
||||
<p>Watches a file or directory for any changes.</p>
|
||||
<p>You can enter a list of comma separated files, or directories if you like. You will need to put " around any that have spaces in.</p>
|
||||
<p>On Windows you must use double slashes \\ in any directory names.</p>
|
||||
<p>The full filename of the file that actually changed is put into <b>msg.payload</b>, while a stringified version of the watched criteria is returned in <b>msg.topic</b>.</p>
|
||||
<p>msg.file contains just the short filename of the file that changed.</p>
|
||||
<p>Of course in Linux, <i>everything</i> could be a file and thus watched...</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
42
nodes/core/io/23-watch.js
Normal file
42
nodes/core/io/23-watch.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var notify = require("fs.notify");
|
||||
var fs = require("fs");
|
||||
var sep = require("path").sep;
|
||||
|
||||
function WatchNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.files = n.files.split(",");
|
||||
for (var f in this.files) {
|
||||
this.files[f] = this.files[f].trim();
|
||||
}
|
||||
this.p = (this.files.length == 1) ? this.files[0] : JSON.stringify(this.files);
|
||||
var node = this;
|
||||
var notifications = new notify(node.files);
|
||||
notifications.on('change', function (file, event, path) {
|
||||
if (fs.statSync(path).isDirectory()) { path = path + sep + file; }
|
||||
var msg = { payload: path, topic: node.p, file: file};
|
||||
node.send(msg);
|
||||
});
|
||||
|
||||
this.close = function() {
|
||||
notifications.close();
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("watch",WatchNode);
|
@@ -26,15 +26,15 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="serial in">
|
||||
<p>Reads data from a local serial port.</p>
|
||||
<p>Keeps reading from the serial port until it sees \n (default) or the character(s) requested. Only sets <b>msg.payload</b>.</p>
|
||||
<p>Reads data from a local serial port.</p>
|
||||
<p>Keeps reading from the serial port until it sees \n (default) or the character(s) requested. Only sets <b>msg.payload</b>.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('serial in',{
|
||||
category: 'input',
|
||||
defaults: {
|
||||
name: {name:""},
|
||||
name: {name:""},
|
||||
serial: {type:"serial-port",required:true}
|
||||
},
|
||||
color:"BurlyWood",
|
||||
@@ -64,15 +64,16 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="serial out">
|
||||
<p>Provides a connection to an outbound serial port.</p>
|
||||
<p>Only the <b>msg.payload</b> is sent.</p>
|
||||
<p>Provides a connection to an outbound serial port.</p>
|
||||
<p>Only the <b>msg.payload</b> is sent.</p>
|
||||
<p>Optionally the new line character used to split the input can be appended to every message sent out to the serial port.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('serial out',{
|
||||
category: 'output',
|
||||
defaults: {
|
||||
name: {name:""},
|
||||
name: {name:""},
|
||||
serial: {type:"serial-port",required:true}
|
||||
},
|
||||
color:"BurlyWood",
|
||||
@@ -88,7 +89,6 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -121,14 +121,16 @@
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-newline"><i class="icon-text-width"></i> New line</label>
|
||||
<input type="text" id="node-config-input-newline">
|
||||
<input type="text" id="node-config-input-newline" style="width: 50px;">
|
||||
</div>
|
||||
<!--
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-config-input-name" placeholder="Name">
|
||||
<label for="node-config-input-addchar"> </label>
|
||||
<select type="text" id="node-config-input-addchar" style="width: 70%;">
|
||||
<option value="false">Don't add 'New Line' to serial output</option>
|
||||
<option value="true">Add 'New line' to serial output message</option>
|
||||
</select>
|
||||
</div>
|
||||
-->
|
||||
<div class="form-tips">Tip: the new line character is used to split the input into separate messages. It can also be added to every message sent out to the serial port.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -138,7 +140,8 @@
|
||||
//name: {value:""},
|
||||
serialport: {value:"",required:true},
|
||||
serialbaud: {value:57600,required:true},
|
||||
newline: {value:"\\n"}
|
||||
newline: {value:"\\n"},
|
||||
addchar: {value:false}
|
||||
},
|
||||
label: function() {
|
||||
//return this.name||this.serialport;
|
@@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var settings = RED.settings;
|
||||
var events = require("events");
|
||||
var util = require("util");
|
||||
var serialp = require("serialport");
|
||||
var settings = RED.settings;
|
||||
|
||||
// TODO: 'serialPool' should be encapsulated in SerialPortNode
|
||||
|
||||
@@ -28,6 +27,7 @@ function SerialPortNode(n) {
|
||||
this.serialport = n.serialport;
|
||||
this.serialbaud = n.serialbaud * 1;
|
||||
this.newline = n.newline;
|
||||
this.addchar = n.addchar || "false";
|
||||
}
|
||||
RED.nodes.registerType("serial-port",SerialPortNode);
|
||||
|
||||
@@ -39,33 +39,41 @@ function SerialOutNode(n) {
|
||||
|
||||
if (this.serialConfig) {
|
||||
var node = this;
|
||||
try {
|
||||
node.port = serialPool.get(this.serialConfig.serialport,this.serialConfig.serialbaud,this.serialConfig.newline);
|
||||
} catch(err) {
|
||||
this.error(err);
|
||||
return;
|
||||
node.port = serialPool.get(this.serialConfig.serialport,this.serialConfig.serialbaud,this.serialConfig.newline);
|
||||
node.addCh = "";
|
||||
if (node.serialConfig.addchar == "true") {
|
||||
node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r");
|
||||
}
|
||||
|
||||
node.on("input",function(msg) {
|
||||
//console.log("{",msg,"}");
|
||||
node.port.write(msg.payload,function(err,res) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
var payload = msg.payload;
|
||||
if (!Buffer.isBuffer(payload)) {
|
||||
if (typeof payload === "object") {
|
||||
payload = JSON.stringify(payload);
|
||||
} else {
|
||||
payload = new String(payload);
|
||||
}
|
||||
payload += node.addCh;
|
||||
} else if (node.addCh !== "") {
|
||||
payload = Buffer.concat([payload,new Buffer(node.addCh)]);
|
||||
}
|
||||
node.port.write(payload,function(err,res) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.error("missing serial config");
|
||||
}
|
||||
}
|
||||
|
||||
this.on("close", function() {
|
||||
if (this.serialConfig) {
|
||||
serialPool.close(this.serialConfig.serialport);
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("serial out",SerialOutNode);
|
||||
|
||||
SerialOutNode.prototype.close = function() {
|
||||
if (this.serialConfig) {
|
||||
serialPool.close(this.serialConfig.serialport);
|
||||
}
|
||||
}
|
||||
|
||||
function SerialInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
@@ -74,34 +82,25 @@ function SerialInNode(n) {
|
||||
|
||||
if (this.serialConfig) {
|
||||
var node = this;
|
||||
try {
|
||||
this.port = serialPool.get(this.serialConfig.serialport,this.serialConfig.serialbaud,this.serialConfig.newline);
|
||||
} catch(err) {
|
||||
this.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this.port = serialPool.get(this.serialConfig.serialport,this.serialConfig.serialbaud,this.serialConfig.newline);
|
||||
this.port.on('data', function(msg) {
|
||||
// console.log("{",msg,"}");
|
||||
var m = { "payload": msg };
|
||||
node.send(m);
|
||||
node.send({ "payload": msg });
|
||||
});
|
||||
} else {
|
||||
this.error("missing serial config");
|
||||
}
|
||||
}
|
||||
|
||||
this.on("close", function() {
|
||||
if (this.serialConfig) {
|
||||
try {
|
||||
serialPool.close(this.serialConfig.serialport);
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("serial in",SerialInNode);
|
||||
|
||||
SerialInNode.prototype.close = function() {
|
||||
if (this.serialConfig) {
|
||||
try {
|
||||
serialPool.close(this.serialConfig.serialport);
|
||||
} catch(err) {
|
||||
}
|
||||
this.warn("Deploying with serial-port nodes is known to occasionally cause Node-RED to hang. This is due to an open issue with the underlying module.");
|
||||
}
|
||||
}
|
||||
|
||||
var serialPool = function() {
|
||||
var connections = {};
|
||||
@@ -116,52 +115,53 @@ var serialPool = function() {
|
||||
_closing: false,
|
||||
tout: null,
|
||||
on: function(a,b) { this._emitter.on(a,b); },
|
||||
close: function(cb) { this.serial.close(cb)},
|
||||
write: function(m,cb) { this.serial.write(m,cb)},
|
||||
close: function(cb) { this.serial.close(cb); },
|
||||
write: function(m,cb) { this.serial.write(m,cb); },
|
||||
}
|
||||
newline = newline.replace("\\n","\n").replace("\\r","\r");
|
||||
var setupSerial = function() {
|
||||
if (newline == "") {
|
||||
obj.serial = new serialp.SerialPort(port,{
|
||||
baudrate: baud,
|
||||
parser: serialp.parsers.raw
|
||||
});
|
||||
}
|
||||
if (newline == "") {
|
||||
obj.serial = new serialp.SerialPort(port,{
|
||||
baudrate: baud,
|
||||
parser: serialp.parsers.raw
|
||||
},true, function(err, results) { if (err) obj.serial.emit('error',err); });
|
||||
}
|
||||
else {
|
||||
obj.serial = new serialp.SerialPort(port,{
|
||||
baudrate: baud,
|
||||
parser: serialp.parsers.readline(newline)
|
||||
});
|
||||
}
|
||||
obj.serial = new serialp.SerialPort(port,{
|
||||
baudrate: baud,
|
||||
parser: serialp.parsers.readline(newline)
|
||||
},true, function(err, results) { if (err) obj.serial.emit('error',err); });
|
||||
}
|
||||
obj.serial.on('error', function(err) {
|
||||
util.log("[serial] serial port "+port+" error "+err);
|
||||
obj.tout = setTimeout(function() {
|
||||
setupSerial();
|
||||
},settings.serialReconnectTime);
|
||||
util.log("[serial] serial port "+port+" error "+err);
|
||||
obj.tout = setTimeout(function() {
|
||||
setupSerial();
|
||||
}, settings.serialReconnectTime);
|
||||
});
|
||||
obj.serial.on('close', function() {
|
||||
if (!obj._closing) {
|
||||
util.log("[serial] serial port "+port+" closed unexpectedly");
|
||||
obj.tout = setTimeout(function() {
|
||||
setupSerial();
|
||||
},settings.serialReconnectTime);
|
||||
}
|
||||
if (!obj._closing) {
|
||||
util.log("[serial] serial port "+port+" closed unexpectedly");
|
||||
obj.tout = setTimeout(function() {
|
||||
setupSerial();
|
||||
}, settings.serialReconnectTime);
|
||||
}
|
||||
});
|
||||
obj.serial.on('open',function() {
|
||||
util.log("[serial] serial port "+port+" opened at "+baud+" baud");
|
||||
obj.serial.flush();
|
||||
obj._emitter.emit('ready');
|
||||
util.log("[serial] serial port "+port+" opened at "+baud+" baud");
|
||||
if (obj.tout) { clearTimeout(obj.tout); }
|
||||
//obj.serial.flush();
|
||||
obj._emitter.emit('ready');
|
||||
});
|
||||
obj.serial.on('data',function(d) {
|
||||
if (typeof d !== "string") {
|
||||
d = d.toString();
|
||||
for (i=0; i<d.length; i++) {
|
||||
obj._emitter.emit('data',d.charAt(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeof d !== "string") {
|
||||
d = d.toString();
|
||||
for (i=0; i<d.length; i++) {
|
||||
obj._emitter.emit('data',d.charAt(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
obj._emitter.emit('data',d);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
setupSerial();
|
||||
@@ -176,17 +176,16 @@ var serialPool = function() {
|
||||
connections[port]._closing = true;
|
||||
try {
|
||||
connections[port].close(function() {
|
||||
util.log("[serial] serial port closed");
|
||||
util.log("[serial] serial port closed");
|
||||
});
|
||||
} catch(err) {
|
||||
};
|
||||
} catch(err) { };
|
||||
}
|
||||
delete connections[port];
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
RED.app.get("/serialports",function(req,res) {
|
||||
RED.httpAdmin.get("/serialports",function(req,res) {
|
||||
serialp.list(function (err, ports) {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.write(JSON.stringify(ports));
|
@@ -114,3 +114,82 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="tcp out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-beserver"><i class="icon-resize-small"></i> Type</label>
|
||||
<select id="node-input-beserver" style="width:150px; margin-right:5px;">
|
||||
<option value="server">Listen on</option>
|
||||
<option value="client">Connect to</option>
|
||||
<option value="reply">Reply to TCP</option>
|
||||
</select>
|
||||
<span id="node-input-port-row">port <input type="text" id="node-input-port" style="width: 50px"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
|
||||
at host <input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-base64" style="width: 70%;">Decode Base64 message ?</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="tcp out">
|
||||
<p>Provides a choice of tcp outputs. Can either connect to a remote tcp port,
|
||||
accept incoming connections, or reply to messages received from a TCP In node.</p>
|
||||
<p>Only <b>msg.payload</b> is sent.</p>
|
||||
<p>If <b>msg.payload</b> is a string containing a base64 encoding of binary
|
||||
data, the Base64 decoding option will cause it to be converted back to binary
|
||||
before being sent.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('tcp out',{
|
||||
category: 'output',
|
||||
color:"Silver",
|
||||
defaults: {
|
||||
host: {value:"",validate:function(v) { return (this.beserver != "client")||v.length > 0;} },
|
||||
port: {value:"",validate:function(v) { return (this.beserver == "reply")||RED.validators.number()(v) } },
|
||||
beserver: {value:"client",required:true},
|
||||
base64: {value:false,required:true},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "bridge-dash.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
|
||||
},
|
||||
labelStyle: function() {
|
||||
return (this.name)?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var updateOptions = function() {
|
||||
var sockettype = $("#node-input-beserver option:selected").val();
|
||||
if (sockettype == "reply") {
|
||||
$("#node-input-port-row").hide();
|
||||
$("#node-input-host-row").hide();
|
||||
} else {
|
||||
$("#node-input-port-row").show();
|
||||
}
|
||||
|
||||
if (sockettype == "client") {
|
||||
$("#node-input-host-row").show();
|
||||
} else {
|
||||
$("#node-input-host-row").hide();
|
||||
}
|
||||
};
|
||||
updateOptions();
|
||||
$("#node-input-beserver").change(updateOptions);
|
||||
}
|
||||
});
|
||||
</script>
|
306
nodes/core/io/31-tcpin.js
Normal file
306
nodes/core/io/31-tcpin.js
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var reconnectTime = RED.settings.socketReconnectTime||10000;
|
||||
var socketTimeout = RED.settings.socketTimeout||null;
|
||||
var net = require('net');
|
||||
|
||||
var connectionPool = {};
|
||||
|
||||
function TcpIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.host = n.host;
|
||||
this.port = n.port * 1;
|
||||
this.topic = n.topic;
|
||||
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
|
||||
this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
|
||||
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r");
|
||||
this.base64 = n.base64;
|
||||
this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
|
||||
this.closing = false;
|
||||
var node = this;
|
||||
|
||||
if (!node.server) {
|
||||
var buffer = null;
|
||||
var client;
|
||||
var reconnectTimeout;
|
||||
function setupTcpClient() {
|
||||
node.log("connecting to "+node.host+":"+node.port);
|
||||
var id = (1+Math.random()*4294967295).toString(16);
|
||||
client = net.connect(node.port, node.host, function() {
|
||||
buffer = (node.datatype == 'buffer')? new Buffer(0):"";
|
||||
node.log("connected to "+node.host+":"+node.port);
|
||||
});
|
||||
connectionPool[id] = client;
|
||||
|
||||
client.on('data', function (data) {
|
||||
if (node.datatype != 'buffer') {
|
||||
data = data.toString(node.datatype);
|
||||
}
|
||||
if (node.stream) {
|
||||
if ((node.datatype) === "utf8" && node.newline != "") {
|
||||
buffer = buffer+data;
|
||||
var parts = buffer.split(node.newline);
|
||||
for (var i = 0;i<parts.length-1;i+=1) {
|
||||
var msg = {topic:node.topic, payload:parts[i]};
|
||||
msg._session = {type:"tcp",id:id};
|
||||
node.send(msg);
|
||||
}
|
||||
buffer = parts[parts.length-1];
|
||||
} else {
|
||||
var msg = {topic:node.topic, payload:data};
|
||||
msg._session = {type:"tcp",id:id};
|
||||
node.send(msg);
|
||||
}
|
||||
} else {
|
||||
if ((typeof data) === "string") {
|
||||
buffer = buffer+data;
|
||||
} else {
|
||||
buffer = Buffer.concat([buffer,data],buffer.length+data.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
client.on('end', function() {
|
||||
if (!node.stream || (node.datatype == "utf8" && node.newline != "" && buffer.length > 0)) {
|
||||
var msg = {topic:node.topic,payload:buffer};
|
||||
msg._session = {type:"tcp",id:id};
|
||||
node.send(msg);
|
||||
buffer = null;
|
||||
}
|
||||
});
|
||||
client.on('close', function() {
|
||||
delete connectionPool[id];
|
||||
node.log("connection lost to "+node.host+":"+node.port);
|
||||
if (!node.closing) {
|
||||
reconnectTimeout = setTimeout(setupTcpClient, reconnectTime);
|
||||
}
|
||||
});
|
||||
client.on('error', function(err) {
|
||||
node.log(err);
|
||||
});
|
||||
}
|
||||
setupTcpClient();
|
||||
|
||||
this.on('close', function() {
|
||||
this.closing = true;
|
||||
client.end();
|
||||
clearTimeout(reconnectTimeout);
|
||||
});
|
||||
} else {
|
||||
var server = net.createServer(function (socket) {
|
||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||
var id = (1+Math.random()*4294967295).toString(16);
|
||||
connectionPool[id] = socket;
|
||||
|
||||
var buffer = (node.datatype == 'buffer')? new Buffer(0):"";
|
||||
socket.on('data', function (data) {
|
||||
if (node.datatype != 'buffer') {
|
||||
data = data.toString(node.datatype);
|
||||
}
|
||||
|
||||
if (node.stream) {
|
||||
if ((typeof data) === "string" && node.newline != "") {
|
||||
buffer = buffer+data;
|
||||
var parts = buffer.split(node.newline);
|
||||
for (var i = 0;i<parts.length-1;i+=1) {
|
||||
var msg = {topic:node.topic, payload:parts[i],ip:socket.remoteAddress,port:socket.remotePort};
|
||||
msg._session = {type:"tcp",id:id};
|
||||
node.send(msg);
|
||||
}
|
||||
buffer = parts[parts.length-1];
|
||||
} else {
|
||||
var msg = {topic:node.topic, payload:data};
|
||||
msg._session = {type:"tcp",id:id};
|
||||
node.send(msg);
|
||||
}
|
||||
} else {
|
||||
if ((typeof data) === "string") {
|
||||
buffer = buffer+data;
|
||||
} else {
|
||||
buffer = Buffer.concat([buffer,data],buffer.length+data.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.on('end', function() {
|
||||
if (!node.stream || (node.datatype == "utf8" && node.newline != "" && buffer.length > 0)) {
|
||||
var msg = {topic:node.topic,payload:buffer};
|
||||
msg._session = {type:"tcp",id:id};
|
||||
node.send(msg);
|
||||
buffer = null;
|
||||
}
|
||||
});
|
||||
socket.on('timeout', function() {
|
||||
node.log('timeout closed socket port '+node.port);
|
||||
socket.end();
|
||||
});
|
||||
socket.on('close', function() {
|
||||
delete connectionPool[id];
|
||||
});
|
||||
socket.on('error',function(err) {
|
||||
node.log(err);
|
||||
});
|
||||
});
|
||||
server.on('error', function(err) {
|
||||
if (err) {
|
||||
node.error('unable to listen on port '+node.port+' : '+err);
|
||||
}
|
||||
});
|
||||
server.listen(node.port, function(err) {
|
||||
if (err) {
|
||||
node.error('unable to listen on port '+node.port+' : '+err);
|
||||
} else {
|
||||
node.log('listening on port '+node.port);
|
||||
|
||||
node.on('close', function() {
|
||||
node.closing = true;
|
||||
server.close();
|
||||
node.log('stopped listening on port '+node.port);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
RED.nodes.registerType("tcp in",TcpIn);
|
||||
|
||||
function TcpOut(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.host = n.host;
|
||||
this.port = n.port * 1;
|
||||
this.base64 = n.base64;
|
||||
this.beserver = n.beserver;
|
||||
this.name = n.name;
|
||||
this.closing = false;
|
||||
var node = this;
|
||||
|
||||
if (!node.beserver||node.beserver=="client") {
|
||||
var reconnectTimeout;
|
||||
var client = null;
|
||||
var connected = false;
|
||||
|
||||
function setupTcpClient() {
|
||||
node.log("connecting to "+node.host+":"+node.port);
|
||||
client = net.connect(node.port, node.host, function() {
|
||||
connected = true;
|
||||
node.log("connected to "+node.host+":"+node.port);
|
||||
});
|
||||
client.on('error', function (err) {
|
||||
node.log('error : '+err);
|
||||
});
|
||||
client.on('end', function (err) {
|
||||
});
|
||||
client.on('close', function() {
|
||||
node.log("connection lost to "+node.host+":"+node.port);
|
||||
connected = false;
|
||||
client.destroy();
|
||||
if (!node.closing) {
|
||||
reconnectTimeout = setTimeout(setupTcpClient,reconnectTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
setupTcpClient();
|
||||
|
||||
node.on("input", function(msg) {
|
||||
if (connected && msg.payload != null) {
|
||||
if (Buffer.isBuffer(msg.payload)) {
|
||||
client.write(msg.payload);
|
||||
} else if (typeof msg.payload === "string" && node.base64) {
|
||||
client.write(new Buffer(msg.payload,'base64'));
|
||||
} else {
|
||||
client.write(new Buffer(""+msg.payload));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
this.closing = true;
|
||||
client.end();
|
||||
clearTimeout(reconnectTimeout);
|
||||
});
|
||||
|
||||
} else if (node.beserver == "reply") {
|
||||
node.on("input",function(msg) {
|
||||
if (msg._session && msg._session.type == "tcp") {
|
||||
var client = connectionPool[msg._session.id];
|
||||
if (client) {
|
||||
if (Buffer.isBuffer(msg.payload)) {
|
||||
client.write(msg.payload);
|
||||
} else if (typeof msg.payload === "string" && node.base64) {
|
||||
client.write(new Buffer(msg.payload,'base64'));
|
||||
} else {
|
||||
client.write(new Buffer(""+msg.payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var connectedSockets = [];
|
||||
var server = net.createServer(function (socket) {
|
||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||
var remoteDetails = socket.remoteAddress+":"+socket.remotePort;
|
||||
node.log("connection from "+remoteDetails);
|
||||
connectedSockets.push(socket);
|
||||
socket.on('timeout', function() {
|
||||
node.log('timeout closed socket port '+node.port);
|
||||
socket.end();
|
||||
});
|
||||
socket.on('close',function() {
|
||||
node.log("connection closed from "+remoteDetails);
|
||||
connectedSockets.splice(connectedSockets.indexOf(socket),1);
|
||||
});
|
||||
socket.on('error',function() {
|
||||
node.log("socket error from "+remoteDetails);
|
||||
connectedSockets.splice(connectedSockets.indexOf(socket),1);
|
||||
});
|
||||
});
|
||||
node.on("input", function(msg) {
|
||||
if (msg.payload != null) {
|
||||
var buffer;
|
||||
if (Buffer.isBuffer(msg.payload)) {
|
||||
buffer = msg.payload;
|
||||
} else if (typeof msg.payload === "string" && node.base64) {
|
||||
buffer = new Buffer(msg.payload,'base64');
|
||||
} else {
|
||||
buffer = new Buffer(""+msg.payload);
|
||||
}
|
||||
for (var i = 0; i<connectedSockets.length;i+=1) {
|
||||
connectedSockets[i].write(buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.on('error', function(err) {
|
||||
if (err) {
|
||||
node.error('unable to listen on port '+node.port+' : '+err);
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(node.port, function(err) {
|
||||
if (err) {
|
||||
node.error('unable to listen on port '+node.port+' : '+err);
|
||||
} else {
|
||||
node.log('listening on port '+node.port);
|
||||
node.on('close', function() {
|
||||
server.close();
|
||||
node.log('stopped listening on port '+node.port);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.registerType("tcp out",TcpOut);
|
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
<div class="form-row node-input-iface">
|
||||
<label for="node-input-iface"><i class="icon-random"></i> Interface</label>
|
||||
<input type="text" id="node-input-iface" placeholder="eth0">
|
||||
<input type="text" id="node-input-iface" placeholder="(optional) ip address of eth0">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-datatype"><i class="icon-file"></i> Output</label>
|
||||
@@ -62,7 +62,8 @@
|
||||
|
||||
<script type="text/x-red" data-help-name="udp in">
|
||||
<p>A udp input node, that produces a <b>msg.payload</b> containing a <i>BUFFER</i>, string, or base64 encoded string. Supports multicast.</p>
|
||||
<p>It also provides <b>msg.fromip</b> in the form ipaddress:port .</p>
|
||||
<p>It also provides <b>msg.ip</b> and <b>msg.port</b> to the ip address and port from which the message was received.</b>
|
||||
<p>On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -71,7 +72,6 @@
|
||||
color:"Silver",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
host: {value:""},
|
||||
iface: {value:""},
|
||||
port: {value:"",required:true,validate:RED.validators.number()},
|
||||
datatype: {value:"buffer",required:true},
|
||||
@@ -103,7 +103,7 @@
|
||||
<option value="broad">broadcast message</option>
|
||||
<option value="multi">multicast message</option>
|
||||
</select>
|
||||
to port <input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
|
||||
to port <input type="text" id="node-input-port" placeholder="port" style="width: 70px">
|
||||
</div>
|
||||
<div class="form-row node-input-addr">
|
||||
<label for="node-input-addr" id="node-input-addr-label"><i class="icon-list"></i> Address</label>
|
||||
@@ -111,21 +111,29 @@
|
||||
</div>
|
||||
<div class="form-row node-input-iface">
|
||||
<label for="node-input-iface"><i class="icon-random"></i> Interface</label>
|
||||
<input type="text" id="node-input-iface" placeholder="eth0">
|
||||
<input type="text" id="node-input-iface" placeholder="(optional) ip address of eth0">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-outport-type"> </label>
|
||||
<select id="node-input-outport-type">
|
||||
<option id="node-input-outport-type-random" value="random">use random local port</option>
|
||||
<option value="fixed">bind to local port</option>
|
||||
</select>
|
||||
<input type="text" id="node-input-outport" style="width: 70px;" placeholder="port">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<input type="checkbox" id="node-input-base64" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-base64" style="width: 70%;">Decode Base64 encoded payload ?</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Tip: leave address and port blank if you want to set using <b>msg.ip</b> and <b>msg.port</b>.</div>
|
||||
<script>
|
||||
$("#node-input-multicast").change(function() {
|
||||
var id = $("#node-input-multicast option:selected").val();
|
||||
console.log(id,$("#node-input-addr")[0].placeholder);
|
||||
if (id !== "multi") {
|
||||
$(".node-input-iface").hide();
|
||||
$("#node-input-addr-label").html('<i class="icon-list"></i> Address');
|
||||
@@ -145,8 +153,9 @@
|
||||
|
||||
<script type="text/x-red" data-help-name="udp out">
|
||||
<p>This node sends <b>msg.payload</b> to the designated udp host and port. Supports multicast.</p>
|
||||
<p>You may also use <b>msg.ip</b> and <b>msg.port</b> to set the destination values.<br/><b>Note</b>: the statically configured values have precedence.</p>
|
||||
<p>If you select broadcast either set the address to the local broadcast ip address, or maybe try 255.255.255.255, which is the global broadcast address.</p>
|
||||
<p>On some systems you may need to be root to use broadcast.</p>
|
||||
<p>On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -155,10 +164,10 @@
|
||||
color:"Silver",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
addr: {value:"",required:true},
|
||||
//group: {value:""},
|
||||
addr: {value:""},
|
||||
iface: {value:""},
|
||||
port: {value:"",required:true,validate:RED.validators.number()},
|
||||
port: {value:""},
|
||||
outport: {value:""},
|
||||
base64: {value:false,required:true},
|
||||
multicast: {value:"false"}
|
||||
},
|
||||
@@ -171,6 +180,34 @@
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var type = this.outport==""?"random":"fixed";
|
||||
$("#node-input-outport-type option").filter(function() {
|
||||
return $(this).val() == type;
|
||||
}).attr('selected',true);
|
||||
|
||||
|
||||
$("#node-input-outport-type").change(function() {
|
||||
var type = $(this).children("option:selected").val();
|
||||
if (type == "random") {
|
||||
$("#node-input-outport").val("").hide();
|
||||
} else {
|
||||
$("#node-input-outport").show();
|
||||
}
|
||||
});
|
||||
|
||||
$("#node-input-outport-type").change();
|
||||
|
||||
$("#node-input-multicast").change(function() {
|
||||
var type = $(this).children("option:selected").val();
|
||||
if (type == "false") {
|
||||
$("#node-input-outport-type-random").html("bind to random local port");
|
||||
} else {
|
||||
$("#node-input-outport-type-random").html("bind to target port");
|
||||
}
|
||||
});
|
||||
$("#node-input-multicast").change();
|
||||
}
|
||||
});
|
||||
</script>
|
168
nodes/core/io/32-udp.js
Normal file
168
nodes/core/io/32-udp.js
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var dgram = require('dgram');
|
||||
|
||||
// The Input Node
|
||||
function UDPin(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.group = n.group;
|
||||
this.port = n.port;
|
||||
this.datatype = n.datatype;
|
||||
this.iface = n.iface || null;
|
||||
this.multicast = n.multicast;
|
||||
var node = this;
|
||||
|
||||
var server = dgram.createSocket('udp4');
|
||||
|
||||
server.on("error", function (err) {
|
||||
if ((err.code == "EACCES") && (node.port < 1024)) {
|
||||
node.error("UDP access error, you may need root access for ports below 1024");
|
||||
} else {
|
||||
node.error("UDP error : "+err.code);
|
||||
}
|
||||
server.close();
|
||||
});
|
||||
|
||||
server.on('message', function (message, remote) {
|
||||
var msg;
|
||||
if (node.datatype =="base64") {
|
||||
msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port };
|
||||
} else if (node.datatype =="utf8") {
|
||||
msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port };
|
||||
} else {
|
||||
msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
|
||||
}
|
||||
node.send(msg);
|
||||
});
|
||||
|
||||
server.on('listening', function () {
|
||||
var address = server.address();
|
||||
node.log('udp listener at ' + address.address + ":" + address.port);
|
||||
if (node.multicast == "true") {
|
||||
server.setBroadcast(true);
|
||||
try {
|
||||
server.setMulticastTTL(128);
|
||||
server.addMembership(node.group,node.iface);
|
||||
node.log("udp multicast group "+node.group);
|
||||
} catch (e) {
|
||||
if (e.errno == "EINVAL") {
|
||||
node.error("Bad Multicast Address");
|
||||
} else if (e.errno == "ENODEV") {
|
||||
node.error("Must be ip address of the required interface");
|
||||
} else {
|
||||
node.error("Error :"+e.errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
try {
|
||||
server.close();
|
||||
node.log('udp listener stopped');
|
||||
} catch (err) {
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
server.bind(node.port,node.iface);
|
||||
}
|
||||
RED.nodes.registerType("udp in",UDPin);
|
||||
|
||||
|
||||
// The Output Node
|
||||
function UDPout(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
//this.group = n.group;
|
||||
this.port = n.port;
|
||||
this.outport = n.outport||"";
|
||||
this.base64 = n.base64;
|
||||
this.addr = n.addr;
|
||||
this.iface = n.iface || null;
|
||||
this.multicast = n.multicast;
|
||||
var node = this;
|
||||
|
||||
var sock = dgram.createSocket('udp4'); // only use ipv4 for now
|
||||
|
||||
if (node.multicast != "false") {
|
||||
if (node.outport == "") { node.outport = node.port; }
|
||||
sock.bind(node.outport, function() { // have to bind before you can enable broadcast...
|
||||
sock.setBroadcast(true); // turn on broadcast
|
||||
if (node.multicast == "multi") {
|
||||
try {
|
||||
sock.setMulticastTTL(128);
|
||||
sock.addMembership(node.addr,node.iface); // Add to the multicast group
|
||||
node.log('udp multicast ready : '+node.outport+' -> '+node.addr+":"+node.port);
|
||||
} catch (e) {
|
||||
if (e.errno == "EINVAL") {
|
||||
node.error("Bad Multicast Address");
|
||||
} else if (e.errno == "ENODEV") {
|
||||
node.error("Must be ip address of the required interface");
|
||||
} else {
|
||||
node.error("Error :"+e.errno);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.log('udp broadcast ready : '+node.outport+' -> '+node.addr+":"+node.port);
|
||||
}
|
||||
});
|
||||
} else if (node.outport != "") {
|
||||
sock.bind(node.outport);
|
||||
node.log('udp ready : '+node.outport+' -> '+node.addr+":"+node.port);
|
||||
} else {
|
||||
node.log('udp ready : '+node.addr+":"+node.port);
|
||||
}
|
||||
|
||||
node.on("input", function(msg) {
|
||||
if (msg.payload != null) {
|
||||
var add = node.addr || msg.ip || "";
|
||||
var por = node.port || msg.port || 0;
|
||||
if (add == "") {
|
||||
node.warn("udp: ip address not set");
|
||||
} else if (por == 0) {
|
||||
node.warn("udp: port not set");
|
||||
} else if (isNaN(por) || (por < 1) || (por > 65535)) {
|
||||
node.warn("udp: port number not valid");
|
||||
} else {
|
||||
var message;
|
||||
if (node.base64) {
|
||||
message = new Buffer(b64string, 'base64');
|
||||
} else if (msg.payload instanceof Buffer) {
|
||||
message = msg.payload;
|
||||
} else {
|
||||
message = new Buffer(""+msg.payload);
|
||||
}
|
||||
sock.send(message, 0, message.length, por, add, function(err, bytes) {
|
||||
if (err) {
|
||||
node.error("udp : "+err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
try {
|
||||
sock.close();
|
||||
node.log('udp output stopped');
|
||||
} catch (err) {
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("udp out",UDPout);
|
254
nodes/core/io/lib/mqtt.js
Normal file
254
nodes/core/io/lib/mqtt.js
Normal file
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var util = require("util");
|
||||
var mqtt = require("mqtt");
|
||||
var events = require("events");
|
||||
//var inspect = require("sys").inspect;
|
||||
|
||||
//var Client = module.exports.Client = function(
|
||||
|
||||
var port = 1883;
|
||||
var host = "localhost";
|
||||
|
||||
function MQTTClient(port,host) {
|
||||
this.port = port||1883;
|
||||
this.host = host||"localhost";
|
||||
this.messageId = 1;
|
||||
this.pendingSubscriptions = {};
|
||||
this.inboundMessages = {};
|
||||
this.lastOutbound = (new Date()).getTime();
|
||||
this.lastInbound = (new Date()).getTime();
|
||||
this.connected = false;
|
||||
|
||||
this._nextMessageId = function() {
|
||||
this.messageId += 1;
|
||||
if (this.messageId > 0xFFFF) {
|
||||
this.messageId = 1;
|
||||
}
|
||||
return this.messageId;
|
||||
}
|
||||
events.EventEmitter.call(this);
|
||||
}
|
||||
util.inherits(MQTTClient, events.EventEmitter);
|
||||
|
||||
MQTTClient.prototype.connect = function(options) {
|
||||
if (!this.connected) {
|
||||
var self = this;
|
||||
options = options||{};
|
||||
self.options = options;
|
||||
self.options.keepalive = options.keepalive||15;
|
||||
self.options.clean = self.options.clean||true;
|
||||
self.options.protocolId = 'MQIsdp';
|
||||
self.options.protocolVersion = 3;
|
||||
|
||||
self.client = mqtt.createConnection(this.port,this.host,function(err,client) {
|
||||
if (err) {
|
||||
self.connected = false;
|
||||
clearInterval(self.watchdog);
|
||||
self.connectionError = true;
|
||||
//util.log('[mqtt] ['+self.uid+'] connection error 1 : '+inspect(err));
|
||||
self.emit('connectionlost',err);
|
||||
return;
|
||||
}
|
||||
client.on('close',function(e) {
|
||||
//util.log('[mqtt] ['+self.uid+'] on close');
|
||||
clearInterval(self.watchdog);
|
||||
if (!self.connectionError) {
|
||||
if (self.connected) {
|
||||
self.connected = false;
|
||||
self.emit('connectionlost',e);
|
||||
} else {
|
||||
self.emit('disconnect');
|
||||
}
|
||||
}
|
||||
});
|
||||
client.on('error',function(e) {
|
||||
//util.log('[mqtt] ['+self.uid+'] on error : '+inspect(e));
|
||||
clearInterval(self.watchdog);
|
||||
if (self.connected) {
|
||||
self.connected = false;
|
||||
self.emit('connectionlost',e);
|
||||
}
|
||||
});
|
||||
client.on('connack',function(packet) {
|
||||
if (packet.returnCode == 0) {
|
||||
self.watchdog = setInterval(function(self) {
|
||||
var now = (new Date()).getTime();
|
||||
|
||||
//util.log('[mqtt] ['+self.uid+'] watchdog '+inspect({connected:self.connected,connectionError:self.connectionError,pingOutstanding:self.pingOutstanding,now:now,lastOutbound:self.lastOutbound,lastInbound:self.lastInbound}));
|
||||
|
||||
if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) {
|
||||
if (self.pingOutstanding) {
|
||||
//util.log('[mqtt] ['+self.uid+'] watchdog pingOustanding - disconnect');
|
||||
try {
|
||||
self.client.disconnect();
|
||||
} catch (err) {
|
||||
}
|
||||
} else {
|
||||
//util.log('[mqtt] ['+self.uid+'] watchdog pinging');
|
||||
self.lastOutbound = (new Date()).getTime();
|
||||
self.lastInbound = (new Date()).getTime();
|
||||
self.pingOutstanding = true;
|
||||
self.client.pingreq();
|
||||
}
|
||||
}
|
||||
|
||||
},self.options.keepalive*500,self);
|
||||
self.pingOutstanding = false;
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
self.lastOutbound = (new Date()).getTime()
|
||||
self.connected = true;
|
||||
self.connectionError = false;
|
||||
self.emit('connect');
|
||||
} else {
|
||||
self.connected = false;
|
||||
self.emit('connectionlost');
|
||||
}
|
||||
});
|
||||
client.on('suback',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
var topic = self.pendingSubscriptions[packet.messageId];
|
||||
self.emit('subscribe',topic,packet.granted[0]);
|
||||
delete self.pendingSubscriptions[packet.messageId];
|
||||
});
|
||||
client.on('unsuback',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
var topic = self.pendingSubscriptions[packet.messageId];
|
||||
self.emit('unsubscribe',topic,packet.granted[0]);
|
||||
delete self.pendingSubscriptions[packet.messageId];
|
||||
});
|
||||
client.on('publish',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
if (packet.qos < 2) {
|
||||
var p = packet;
|
||||
self.emit('message',p.topic,p.payload,p.qos,p.retain);
|
||||
} else {
|
||||
self.inboundMessages[packet.messageId] = packet;
|
||||
this.lastOutbound = (new Date()).getTime()
|
||||
self.client.pubrec(packet);
|
||||
}
|
||||
if (packet.qos == 1) {
|
||||
this.lastOutbound = (new Date()).getTime()
|
||||
self.client.puback(packet);
|
||||
}
|
||||
});
|
||||
|
||||
client.on('pubrel',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
var p = self.inboundMessages[packet.messageId];
|
||||
if (p) {
|
||||
self.emit('message',p.topic,p.payload,p.qos,p.retain);
|
||||
delete self.inboundMessages[packet.messageId];
|
||||
}
|
||||
self.lastOutbound = (new Date()).getTime()
|
||||
self.client.pubcomp(packet);
|
||||
});
|
||||
|
||||
client.on('puback',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
// outbound qos-1 complete
|
||||
});
|
||||
|
||||
client.on('pubrec',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
self.lastOutbound = (new Date()).getTime()
|
||||
self.client.pubrel(packet);
|
||||
});
|
||||
client.on('pubcomp',function(packet) {
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
// outbound qos-2 complete
|
||||
});
|
||||
client.on('pingresp',function(packet) {
|
||||
//util.log('[mqtt] ['+self.uid+'] received pingresp');
|
||||
self.lastInbound = (new Date()).getTime()
|
||||
self.pingOutstanding = false;
|
||||
});
|
||||
|
||||
this.lastOutbound = (new Date()).getTime()
|
||||
this.connectionError = false;
|
||||
client.connect(self.options);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MQTTClient.prototype.subscribe = function(topic,qos) {
|
||||
var self = this;
|
||||
if (self.connected) {
|
||||
var options = {
|
||||
subscriptions:[{topic:topic,qos:qos}],
|
||||
messageId: self._nextMessageId()
|
||||
};
|
||||
this.pendingSubscriptions[options.messageId] = topic;
|
||||
this.lastOutbound = (new Date()).getTime()
|
||||
self.client.subscribe(options);
|
||||
}
|
||||
}
|
||||
MQTTClient.prototype.unsubscribe = function(topic) {
|
||||
var self = this;
|
||||
if (self.connected) {
|
||||
var options = {
|
||||
topic:topic,
|
||||
messageId: self._nextMessageId()
|
||||
};
|
||||
this.pendingSubscriptions[options.messageId] = topic;
|
||||
this.lastOutbound = (new Date()).getTime()
|
||||
self.client.unsubscribe(options);
|
||||
}
|
||||
}
|
||||
|
||||
MQTTClient.prototype.publish = function(topic,payload,qos,retain) {
|
||||
var self = this;
|
||||
if (self.connected) {
|
||||
|
||||
if (Buffer.isBuffer(payload)) {
|
||||
payload = payload.toString();
|
||||
} else if (typeof payload === "object") {
|
||||
payload = JSON.stringify(payload);
|
||||
} else if (typeof payload !== "string") {
|
||||
payload = ""+payload;
|
||||
}
|
||||
var options = {
|
||||
topic: topic,
|
||||
payload: payload,
|
||||
qos: qos||0,
|
||||
retain:retain||false
|
||||
};
|
||||
if (options.qos != 0) {
|
||||
options.messageId = self._nextMessageId();
|
||||
}
|
||||
this.lastOutbound = (new Date()).getTime()
|
||||
self.client.publish(options);
|
||||
}
|
||||
}
|
||||
|
||||
MQTTClient.prototype.disconnect = function() {
|
||||
var self = this;
|
||||
if (this.connected) {
|
||||
this.connected = false;
|
||||
try {
|
||||
this.client.disconnect();
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
}
|
||||
MQTTClient.prototype.isConnected = function() {
|
||||
return this.connected;
|
||||
}
|
||||
module.exports.createClient = function(port,host) {
|
||||
var mqtt_client = new MQTTClient(port,host);
|
||||
return mqtt_client;
|
||||
}
|
||||
|
@@ -15,22 +15,31 @@
|
||||
**/
|
||||
var util = require("util");
|
||||
var mqtt = require("./mqtt");
|
||||
var settings = require("../../../red/red").settings;
|
||||
var settings = require(process.env.NODE_RED_HOME+"/red/red").settings;
|
||||
|
||||
var connections = {};
|
||||
|
||||
function matchTopic(ts,t) {
|
||||
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/#$/,".*"));
|
||||
function matchTopic(ts,t) {
|
||||
if (ts == "#") {
|
||||
return true;
|
||||
}
|
||||
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
return re.test(t);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get: function(broker,port) {
|
||||
var id = broker+":"+port;
|
||||
get: function(broker,port,clientid,username,password) {
|
||||
var id = "["+(username||"")+":"+(password||"")+"]["+(clientid||"")+"]@"+broker+":"+port;
|
||||
if (!connections[id]) {
|
||||
connections[id] = function() {
|
||||
var uid = (1+Math.random()*4294967295).toString(16);
|
||||
var client = mqtt.createClient(port,broker);
|
||||
var options = {keepalive:15,clientId:'mqtt_' + (1+Math.random()*4294967295).toString(16)};
|
||||
client.uid = uid;
|
||||
client.setMaxListeners(0);
|
||||
var options = {keepalive:15};
|
||||
options.clientId = clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16);
|
||||
options.username = username;
|
||||
options.password = password;
|
||||
var queue = [];
|
||||
var subscriptions = [];
|
||||
var connecting = false;
|
||||
@@ -65,7 +74,7 @@ module.exports = {
|
||||
client.once(a,b);
|
||||
},
|
||||
connect: function() {
|
||||
if (!client.isConnected() && !connecting) {
|
||||
if (client && !client.isConnected() && !connecting) {
|
||||
connecting = true;
|
||||
client.connect(options);
|
||||
}
|
||||
@@ -80,33 +89,33 @@ module.exports = {
|
||||
}
|
||||
};
|
||||
client.on('connect',function() {
|
||||
|
||||
util.log('[mqtt] connected to broker tcp://'+broker+':'+port);
|
||||
|
||||
connecting = false;
|
||||
for (var s in subscriptions) {
|
||||
var topic = subscriptions[s].topic;
|
||||
var qos = subscriptions[s].qos;
|
||||
var callback = subscriptions[s].callback;
|
||||
client.subscribe(topic,qos);
|
||||
}
|
||||
//console.log("connected - publishing",queue.length,"messages");
|
||||
while(queue.length) {
|
||||
var msg = queue.shift();
|
||||
//console.log(msg);
|
||||
client.publish(msg.topic,msg.payload,msg.qos,msg.retain);
|
||||
if (client) {
|
||||
util.log('[mqtt] ['+uid+'] connected to broker tcp://'+broker+':'+port);
|
||||
connecting = false;
|
||||
for (var s in subscriptions) {
|
||||
var topic = subscriptions[s].topic;
|
||||
var qos = subscriptions[s].qos;
|
||||
var callback = subscriptions[s].callback;
|
||||
client.subscribe(topic,qos);
|
||||
}
|
||||
//console.log("connected - publishing",queue.length,"messages");
|
||||
while(queue.length) {
|
||||
var msg = queue.shift();
|
||||
//console.log(msg);
|
||||
client.publish(msg.topic,msg.payload,msg.qos,msg.retain);
|
||||
}
|
||||
}
|
||||
});
|
||||
client.on('connectionlost', function(err) {
|
||||
util.log('[mqtt] connection lost to broker tcp://'+broker+':'+port);
|
||||
util.log('[mqtt] ['+uid+'] connection lost to broker tcp://'+broker+':'+port);
|
||||
connecting = false;
|
||||
setTimeout(function() {
|
||||
if (client) {
|
||||
client.connect(options);
|
||||
}
|
||||
obj.connect();
|
||||
}, settings.mqttReconnectTime||5000);
|
||||
});
|
||||
client.on('disconnect', function() {
|
||||
util.log('[mqtt] disconnected from broker tcp://'+broker+':'+port);
|
||||
connecting = false;
|
||||
util.log('[mqtt] ['+uid+'] disconnected from broker tcp://'+broker+':'+port);
|
||||
});
|
||||
|
||||
return obj
|
@@ -29,34 +29,41 @@
|
||||
</div>
|
||||
<a href="#" class="btn btn-mini" id="node-input-add-rule" style="margin-top: 4px;"><i class="icon-plus"></i> Add</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<select id="node-input-checkall" style="width:100%; margin-right:5px;">
|
||||
<option value="true">checking all rules</option>
|
||||
<option value="false">stopping after first match</option>
|
||||
</select>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="switch">
|
||||
<p>A simple function node to route messages based on its properties.</p>
|
||||
<p>When a message arrives, the selected property is evaluated against each
|
||||
of the defined rules. The message is then sent to the output of <i>all</i>
|
||||
rules that pass.</p>
|
||||
<p>A simple function node to route messages based on its properties.</p>
|
||||
<p>When a message arrives, the selected property is evaluated against each
|
||||
of the defined rules. The message is then sent to the output of <i>all</i>
|
||||
rules that pass.</p>
|
||||
<p>Note: the <i>otherwise</i> rule applies as a "not any of" the rules preceeding it.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('switch',{
|
||||
color:"#E2D96E",
|
||||
RED.nodes.registerType('switch', {
|
||||
color: "#E2D96E",
|
||||
category: 'function',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
property: {value: "payload",required:true},
|
||||
rules:{value:[{t:"eq",v:""}]},
|
||||
outputs:{value:1}
|
||||
property: {value:"payload", required:true},
|
||||
rules: {value:[{t:"eq", v:""}]},
|
||||
checkall: {value:"true", required:true},
|
||||
outputs: {value:1}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
icon: "switch.png",
|
||||
label: function() {
|
||||
return this.name;
|
||||
return this.name||"switch";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
|
||||
|
||||
var operators = [
|
||||
{v:"eq",t:"=="},
|
||||
{v:"neq",t:"!="},
|
||||
@@ -70,30 +77,29 @@
|
||||
{v:"true",t:"is true"},
|
||||
{v:"false",t:"is false"},
|
||||
{v:"null",t:"is null"},
|
||||
{v:"nnull",t:"is not null"}
|
||||
{v:"nnull",t:"is not null"},
|
||||
{v:"else",t:"otherwise"}
|
||||
];
|
||||
|
||||
|
||||
function generateRule(i,rule) {
|
||||
var container = $('<li/>',{style:"margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"});
|
||||
var row = $('<div/>').appendTo(container);
|
||||
var row2 = $('<div/>',{style:"padding-top: 5px; text-align: right;"}).appendTo(container);
|
||||
|
||||
|
||||
var selectField = $('<select/>',{style:"width:120px; margin-left: 5px; text-align: center;"}).appendTo(row);
|
||||
for (var d in operators) {
|
||||
selectField.append($("<option></option>").val(operators[d].v).text(operators[d].t));
|
||||
}
|
||||
|
||||
|
||||
var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px; width: 145px;"}).appendTo(row);
|
||||
|
||||
var btwnField = $('<span/>').appendTo(row);
|
||||
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px; width: 50px;"}).appendTo(btwnField);
|
||||
btwnField.append(" and ");
|
||||
var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 50px;margin-left:2px;"}).appendTo(btwnField);
|
||||
|
||||
|
||||
|
||||
var finalspan = $('<span/>',{style:"float: right; margin-top: 3px;margin-right: 10px;"}).appendTo(row);
|
||||
finalspan.append(' send to <span class="node-input-rule-index">'+i+'</span> ');
|
||||
|
||||
|
||||
selectField.change(function() {
|
||||
var type = selectField.children("option:selected").val();
|
||||
if (type.length < 4) {
|
||||
@@ -108,17 +114,17 @@
|
||||
btwnField.show();
|
||||
} else {
|
||||
btwnField.hide();
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull") {
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||
valueField.hide();
|
||||
} else {
|
||||
valueField.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var deleteButton = $('<a/>',{href:"#",class:"btn btn-mini", style:"margin-left: 5px;"}).appendTo(finalspan);
|
||||
$('<i/>',{class:"icon-remove"}).appendTo(deleteButton);
|
||||
|
||||
|
||||
deleteButton.click(function() {
|
||||
container.css({"background":"#fee"});
|
||||
container.fadeOut(300, function() {
|
||||
@@ -126,34 +132,32 @@
|
||||
$("#node-input-rule-container").children().each(function(i) {
|
||||
$(this).find(".node-input-rule-index").html(i+1);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("#node-input-rule-container").append(container);
|
||||
|
||||
|
||||
selectField.find("option").filter(function() {return $(this).val() == rule.t;}).attr('selected',true);
|
||||
if (rule.t == "btwn") {
|
||||
btwnValueField.val(rule.v);
|
||||
btwnValue2Field.val(rule.v2);
|
||||
} else if (rule.v) {
|
||||
} else if (typeof rule.v != "undefined") {
|
||||
valueField.val(rule.v);
|
||||
}
|
||||
selectField.change();
|
||||
|
||||
}
|
||||
|
||||
|
||||
$("#node-input-add-rule").click(function() {
|
||||
generateRule($("#node-input-rule-container").children().length+1,{t:"",v:"",v2:""});
|
||||
$("#node-input-rule-container-div").scrollTop($("#node-input-rule-container-div").get(0).scrollHeight);
|
||||
|
||||
});
|
||||
|
||||
|
||||
for (var i=0;i<this.rules.length;i++) {
|
||||
var rule = this.rules[i];
|
||||
generateRule(i+1,rule);
|
||||
}
|
||||
|
||||
|
||||
function switchDialogResize(ev,ui) {
|
||||
$("#node-input-rule-container-div").css("height",(ui.size.height-260)+"px");
|
||||
};
|
||||
@@ -172,15 +176,13 @@
|
||||
oneditsave: function() {
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
var ruleset;
|
||||
|
||||
var node = this;
|
||||
node.rules= [];
|
||||
|
||||
rules.each(function(i) {
|
||||
var rule = $(this);
|
||||
var type = rule.find("select option:selected").val();
|
||||
var r = {t:type};
|
||||
if (!(type === "true" || type === "false" || type === "null" || type === "nnull")) {
|
||||
if (!(type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else")) {
|
||||
if (type === "btwn") {
|
||||
r.v = rule.find(".node-input-rule-btwn-value").val();
|
||||
r.v2 = rule.find(".node-input-rule-btwn-value2").val();
|
||||
@@ -189,11 +191,8 @@
|
||||
}
|
||||
}
|
||||
node.rules.push(r);
|
||||
|
||||
});
|
||||
|
||||
node.outputs = node.rules.length;
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
73
nodes/core/logic/10-switch.js
Normal file
73
nodes/core/logic/10-switch.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME + "/red/red");
|
||||
|
||||
var operators = {
|
||||
'eq': function(a, b) { return a == b; },
|
||||
'neq': function(a, b) { return a != b; },
|
||||
'lt': function(a, b) { return a < b; },
|
||||
'lte': function(a, b) { return a <= b; },
|
||||
'gt': function(a, b) { return a > b; },
|
||||
'gte': function(a, b) { return a >= b; },
|
||||
'btwn': function(a, b, c) { return a >= b && a <= c; },
|
||||
'cont': function(a, b) { return (a + "").indexOf(b) != -1; },
|
||||
'regex': function(a, b) { return (a + "").match(new RegExp(b)); },
|
||||
'true': function(a) { return a === true; },
|
||||
'false': function(a) { return a === false; },
|
||||
'null': function(a) { return typeof a == "undefined"; },
|
||||
'nnull': function(a) { return typeof a != "undefined"; },
|
||||
'else': function(a) { return a === true; }
|
||||
};
|
||||
|
||||
function SwitchNode(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
this.rules = n.rules;
|
||||
this.property = n.property;
|
||||
this.checkall = n.checkall || "true";
|
||||
var propertyParts = n.property.split("."),
|
||||
node = this;
|
||||
|
||||
for (var i=0; i<this.rules.length; i+=1) {
|
||||
var rule = this.rules[i];
|
||||
if (!isNaN(Number(rule.v))) {
|
||||
rule.v = Number(rule.v);
|
||||
rule.v2 = Number(rule.v2);
|
||||
}
|
||||
}
|
||||
|
||||
this.on('input', function (msg) {
|
||||
var onward = [];
|
||||
var prop = propertyParts.reduce(function (obj, i) {
|
||||
return obj[i]
|
||||
}, msg);
|
||||
var elseflag = true;
|
||||
for (var i=0; i<node.rules.length; i+=1) {
|
||||
var rule = node.rules[i];
|
||||
var test = prop;
|
||||
if (rule.t == "else") { test = elseflag; elseflag = true; }
|
||||
if (operators[rule.t](test,rule.v, rule.v2)) {
|
||||
onward.push(msg);
|
||||
elseflag = false;
|
||||
if (node.checkall == "false") { break; }
|
||||
} else {
|
||||
onward.push(null);
|
||||
}
|
||||
}
|
||||
this.send(onward);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("switch", SwitchNode);
|
139
nodes/core/logic/15-change.html
Normal file
139
nodes/core/logic/15-change.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="change">
|
||||
<div>
|
||||
<select id="node-input-action" style="width:95%; margin-right:5px;">
|
||||
<option value="replace">Set the value of the message property</option>
|
||||
<option value="change">Search/replace the value of the message property</option>
|
||||
<option value="delete">Delete the message property</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row" style="padding-top:10px;" id="node-prop1-row">
|
||||
<label for="node-input-property">called</label> msg.<input type="text" id="node-input-property" style="width: 63%;"/>
|
||||
</div>
|
||||
<div class="form-row" id="node-from-row">
|
||||
<label for="node-input-from" id="node-input-f"></label>
|
||||
<input type="text" id="node-input-from" placeholder="this"/>
|
||||
</div>
|
||||
<div class="form-row" id="node-to-row">
|
||||
<label for="node-input-to" id="node-input-t"></label>
|
||||
<input type="text" id="node-input-to" placeholder="that"/>
|
||||
</div>
|
||||
<div class="form-row" id="node-reg-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-reg" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-reg" style="width: 70%;">Use regular expressions ?</label>
|
||||
</div>
|
||||
<div class="form-tips" id="node-tip"></div>
|
||||
<br/>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="change">
|
||||
<p>A simple function node to change, replace, add or delete properties of a message.</p>
|
||||
<p>When a message arrives, the selected property is modified by the defined rules.
|
||||
The message is then sent to the output.</p>
|
||||
<p><b>Note:</b> Replace only operates on <b>strings</b>. Anything else will be passed straight through.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('change', {
|
||||
color: "#E2D96E",
|
||||
category: 'function',
|
||||
defaults: {
|
||||
action: {value:"replace",required:true},
|
||||
property: {value:"payload",required:true},
|
||||
from: {value:"",validate: function(v) {
|
||||
if (this.action == "change" && this.reg) {
|
||||
try {
|
||||
var re = new RegExp(this.from, "g");
|
||||
return true;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}},
|
||||
to: {value:""},
|
||||
reg: {value:false},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
icon: "swap.png",
|
||||
label: function() {
|
||||
if (this.name) {
|
||||
return this.name;
|
||||
}
|
||||
if (this.action == "replace") {
|
||||
return "set msg."+this.property;
|
||||
} else {
|
||||
return this.action+" msg."+this.property
|
||||
}
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name ? "node_label_italic" : "";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
if (this.reg === null) { $("#node-input-reg").prop('checked', true); }
|
||||
$("#node-input-action").change( function() {
|
||||
var a = $("#node-input-action").val();
|
||||
if (a === "replace") {
|
||||
$("#node-input-todo").html("called");
|
||||
//$("#node-input-f").html("name");
|
||||
$("#node-input-t").html("to");
|
||||
$("#node-from-row").hide();
|
||||
$("#node-to-row").show();
|
||||
$("#node-reg-row").hide();
|
||||
$("#node-tip").show();
|
||||
$("#node-tip").html("Tip: expects a new property name and either a fixed value OR the full name of another message property eg: msg.sentiment.score");
|
||||
}
|
||||
if (a === "delete") {
|
||||
$("#node-input-todo").html("called");
|
||||
//$("#node-input-f").html("called");
|
||||
//$("#node-input-t").html("to");
|
||||
$("#node-from-row").hide();
|
||||
$("#node-to-row").hide();
|
||||
$("#node-reg-row").hide();
|
||||
$("#node-tip").hide();
|
||||
}
|
||||
if (a === "change") {
|
||||
$("#node-input-todo").html("called");
|
||||
$("#node-input-f").html("Search for");
|
||||
$("#node-input-t").html("replace with");
|
||||
$("#node-from-row").show();
|
||||
$("#node-to-row").show();
|
||||
$("#node-reg-row").show();
|
||||
$("#node-tip").show();
|
||||
$("#node-tip").html("Tip: only works on string properties. If regular expressions are used, the <i>replace with</i> field can contain capture results, eg $1.");
|
||||
}
|
||||
//if (a === "replace") {
|
||||
// $("#node-input-todo").html("called");
|
||||
// //$("#node-input-f").html("with");
|
||||
// $("#node-input-t").html("with");
|
||||
// $("#node-from-row").hide();
|
||||
// $("#node-to-row").show();
|
||||
// $("#node-tip").html("Tip: accepts either a fixed value OR the full name of another msg.property eg: msg.sentiment.score");
|
||||
//}
|
||||
});
|
||||
$("#node-input-action").change();
|
||||
}
|
||||
});
|
||||
</script>
|
73
nodes/core/logic/15-change.js
Normal file
73
nodes/core/logic/15-change.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME + "/red/red");
|
||||
|
||||
function ChangeNode(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
this.action = n.action;
|
||||
this.property = n.property || "";
|
||||
this.from = n.from || " ";
|
||||
this.to = n.to || " ";
|
||||
this.reg = (n.reg === null || n.reg);
|
||||
var node = this;
|
||||
if (node.reg === false) {
|
||||
this.from = this.from.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
}
|
||||
var makeNew = function( stem, path, value ) {
|
||||
var lastPart = (arguments.length === 3) ? path.pop() : false;
|
||||
for (var i = 0; i < path.length; i++) {
|
||||
stem = stem[path[i]] = stem[path[i]] || {};
|
||||
}
|
||||
if (lastPart) { stem = stem[lastPart] = value; }
|
||||
return stem;
|
||||
};
|
||||
|
||||
this.on('input', function (msg) {
|
||||
if (node.action == "change") {
|
||||
try {
|
||||
node.re = new RegExp(this.from, "g");
|
||||
} catch (e) {
|
||||
node.error(e.message);
|
||||
}
|
||||
if (typeof msg[node.property] === "string") {
|
||||
msg[node.property] = (msg[node.property]).replace(node.re, node.to);
|
||||
}
|
||||
}
|
||||
//else if (node.action == "replace") {
|
||||
//if (node.to.indexOf("msg.") == 0) {
|
||||
//msg[node.property] = eval(node.to);
|
||||
//}
|
||||
//else {
|
||||
//msg[node.property] = node.to;
|
||||
//}
|
||||
//}
|
||||
else if (node.action == "replace") {
|
||||
if (node.to.indexOf("msg.") == 0) {
|
||||
makeNew( msg, node.property.split("."), eval(node.to) );
|
||||
}
|
||||
else {
|
||||
makeNew( msg, node.property.split("."), node.to );
|
||||
}
|
||||
//makeNew( msg, node.property.split("."), node.to );
|
||||
}
|
||||
else if (node.action == "delete") {
|
||||
delete(msg[node.property]);
|
||||
}
|
||||
node.send(msg);
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("change", ChangeNode);
|
81
nodes/core/logic/16-range.html
Normal file
81
nodes/core/logic/16-range.html
Normal file
@@ -0,0 +1,81 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="range">
|
||||
<div class="form-row">
|
||||
<select id="node-input-action" style="width:90%; margin-right:5px;">
|
||||
<option value="scale">Scale msg.payload</option>
|
||||
<option value="clamp">Scale and limit to the target range</option>
|
||||
<option value="roll">Scale and wrap within the target range</option>
|
||||
</select>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="form-row">
|
||||
From the range:
|
||||
</div>
|
||||
<div class="form-row">
|
||||
min: <input type="text" id="node-input-minin" placeholder="0" style="width:100px;"/>
|
||||
max: <input type="text" id="node-input-maxin" placeholder="99" style="width:100px;"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
to the range:
|
||||
</div>
|
||||
<div class="form-row">
|
||||
min: <input type="text" id="node-input-minout" placeholder="0" style="width:100px;"/>
|
||||
max: <input type="text" id="node-input-maxout" placeholder="255" style="width:100px;"/>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-input-round" style="display: inline-block; width: auto; vertical-align: top;"> <label style="width: auto;" for="node-input-round">Round to nearest integer?</label></input>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips" id="node-tip">Tip: This node ONLY works with numbers.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="range">
|
||||
<p>A simple function node to remap numeric input values to another scale.</p>
|
||||
<p>Currently only does a linear scaling.</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>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('range', {
|
||||
color: "#E2D96E",
|
||||
category: 'function',
|
||||
defaults: {
|
||||
minin: {value:"",required:true,validate:RED.validators.number()},
|
||||
maxin: {value:"",required:true,validate:RED.validators.number()},
|
||||
minout: {value:"",required:true,validate:RED.validators.number()},
|
||||
maxout: {value:"",required:true,validate:RED.validators.number()},
|
||||
action: {value:"scale"},
|
||||
round: {value:false},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
icon: "range.png",
|
||||
label: function() {
|
||||
return this.name || "range";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name ? "node_label_italic" : "";
|
||||
}
|
||||
});
|
||||
</script>
|
47
nodes/core/logic/16-range.js
Normal file
47
nodes/core/logic/16-range.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME + "/red/red");
|
||||
|
||||
function RangeNode(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
this.action = n.action;
|
||||
this.round = n.round || false;
|
||||
this.minin = Number(n.minin);
|
||||
this.maxin = Number(n.maxin);
|
||||
this.minout = Number(n.minout);
|
||||
this.maxout = Number(n.maxout);
|
||||
var node = this;
|
||||
|
||||
this.on('input', function (msg) {
|
||||
var n = Number(msg.payload);
|
||||
if (!isNaN(n)) {
|
||||
if (node.action == "clamp") {
|
||||
if (n < node.minin) { n = node.minin; }
|
||||
if (n > node.maxin) { n = node.maxin; }
|
||||
}
|
||||
if (node.action == "roll") {
|
||||
if (n >= node.maxin) { n = (n - node.minin) % (node.maxin - node.minin) + node.minin; }
|
||||
if (n < node.minin) { n = (n - node.minin) % (node.maxin - node.minin) + node.maxin; }
|
||||
}
|
||||
msg.payload = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout;
|
||||
if (node.round) { msg.payload = Math.round(msg.payload); }
|
||||
node.send(msg);
|
||||
}
|
||||
else { node.log("Not a number: "+msg.payload); }
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("range", RangeNode);
|
@@ -30,7 +30,7 @@
|
||||
}
|
||||
var callback = encodeURIComponent(location.protocol+"//"+location.hostname+":"+location.port+pathname+"twitter/"+twitterConfigNodeId+"/auth/callback");
|
||||
|
||||
$("#node-config-twitter-row").html('Click <a id="node-config-twitter-start" href="/twitter/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank"><b>here</b></a> to authenticate with Twitter.');
|
||||
$("#node-config-twitter-row").html('Click <a id="node-config-twitter-start" href="twitter/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank"><b>here</b></a> to authenticate with Twitter.');
|
||||
$("#node-config-twitter-start").click(function() {
|
||||
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
|
||||
});
|
||||
@@ -102,19 +102,21 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="twitter in">
|
||||
<div class="form-row">
|
||||
<div class="form-row">
|
||||
<label for="node-input-twitter"><i class="icon-user"></i> Log in as</label>
|
||||
<input type="text" id="node-input-twitter">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-user"><i class="icon-search"></i> Search</label>
|
||||
<select type="text" id="node-input-user" style="display: inline-block; vertical-align: middle; width:60%;">
|
||||
<option value="false">all public tweets</option>
|
||||
<option value="true">the tweets of who you follow</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-tags"><i class="icon-tags"></i> for</label>
|
||||
<label for="node-input-user"><i class="icon-search"></i> Search</label>
|
||||
<select type="text" id="node-input-user" style="display: inline-block; vertical-align: middle; width:60%;">
|
||||
<option value="false">all public tweets</option>
|
||||
<option value="true">the tweets of who you follow</option>
|
||||
<option value="user">the tweets of specific users</option>
|
||||
<option value="dm">your direct messages</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row" id="node-input-tags-row">
|
||||
<label for="node-input-tags"><i class="icon-tags"></i> <span id="node-input-tags-label">for</span></label>
|
||||
<input type="text" id="node-input-tags" placeholder="comma-separated words, @ids, #tags">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -127,9 +129,19 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="twitter in">
|
||||
<p>Twitter input node. Watches either the public or the user's stream for tweets containing the configured search term.</p>
|
||||
<p>Sets the <b>msg.topic</b> to <i>tweets/</i> and then appends the senders screen name.</p>
|
||||
<p>Sets <b>msg.location</b> to the tweeters location if known.</p>
|
||||
<p>Twitter input node. Can be used to search either:
|
||||
<ul><li>the public or a user's stream for tweets containing the configured search term</li>
|
||||
<li>all tweets by specific users</li>
|
||||
<li>direct messages received by the authenticated user</li>
|
||||
</ul></p>
|
||||
<p>Use space for <i>and</i> and comma , for <i>or</i> when searching for multiple terms.</p>
|
||||
<p>Sets the <b>msg.topic</b> to <i>tweets/</i> and then appends the senders screen name.</p>
|
||||
<p>Sets <b>msg.location</b> to the tweeters location if known.</p>
|
||||
<p>Sets <b>msg.tweet</b> to the full tweet object as documented by <a href="https://dev.twitter.com/docs/platform-objects/tweets">Twitter</a>.
|
||||
<p><b>Note:</b> when set to a specific user's tweets, or your direct messages, the node is subject to
|
||||
Twitter's API rate limiting. If you deploy the flows multiple times within a 15 minute window, you may
|
||||
exceed the limit and will see errors from the node. These errors will clear when the current 15 minute window
|
||||
passes.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -138,7 +150,7 @@
|
||||
color:"#C0DEED",
|
||||
defaults: {
|
||||
twitter: {type:"twitter-credentials",required:true},
|
||||
tags: {value:"",required:true},
|
||||
tags: {value:"",validate:function(v) { return this.user == "dm" || v.length > 0;}},
|
||||
user: {value:"false",required:true},
|
||||
name: {value:""},
|
||||
topic: {value:"tweets"}
|
||||
@@ -147,16 +159,43 @@
|
||||
outputs:1,
|
||||
icon: "twitter.png",
|
||||
label: function() {
|
||||
return this.name||this.tags;
|
||||
if (this.name) {
|
||||
return this.name;
|
||||
}
|
||||
if (this.user == "dm") {
|
||||
var user = RED.nodes.node(this.twitter);
|
||||
return (user?user.label()+" ":"")+"DMs";
|
||||
} else if (this.user == "user") {
|
||||
return this.tags+" tweets";
|
||||
}
|
||||
return this.tags;
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$("#node-input-user").change(function() {
|
||||
var type = $("#node-input-user option:selected").val();
|
||||
if (type == "user") {
|
||||
$("#node-input-tags-row").show();
|
||||
$("#node-input-tags-label").html("User");
|
||||
$("#node-input-tags").attr("placeholder","comma-separated @twitter handles");
|
||||
} else if (type == "dm") {
|
||||
$("#node-input-tags-row").hide();
|
||||
} else {
|
||||
$("#node-input-tags-row").show();
|
||||
$("#node-input-tags-label").html("for");
|
||||
$("#node-input-tags").attr("placeholder","comma-separated words, @ids, #hashtags");
|
||||
}
|
||||
|
||||
});
|
||||
$("#node-input-user").change();
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script type="text/x-red" data-template-name="twitter out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-twitter"><i class="icon-user"></i> Twitter</label>
|
||||
@@ -169,7 +208,7 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="twitter out">
|
||||
<p>Twitter out node. Tweets the <b>msg.payload</b>.</p>
|
||||
<p>Twitter out node. Tweets the <b>msg.payload</b>.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
322
nodes/core/social/27-twitter.js
Normal file
322
nodes/core/social/27-twitter.js
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var ntwitter = require('twitter-ng');
|
||||
var OAuth= require('oauth').OAuth;
|
||||
|
||||
function TwitterNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.screen_name = n.screen_name;
|
||||
}
|
||||
RED.nodes.registerType("twitter-credentials",TwitterNode);
|
||||
|
||||
function TwitterInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.active = true;
|
||||
this.user = n.user;
|
||||
//this.tags = n.tags.replace(/ /g,'');
|
||||
this.tags = n.tags;
|
||||
this.twitter = n.twitter;
|
||||
this.topic = n.topic||"tweets";
|
||||
this.twitterConfig = RED.nodes.getNode(this.twitter);
|
||||
var credentials = RED.nodes.getCredentials(this.twitter);
|
||||
|
||||
if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
|
||||
var twit = new ntwitter({
|
||||
consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
|
||||
consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
|
||||
access_token_key: credentials.access_token,
|
||||
access_token_secret: credentials.access_token_secret
|
||||
});
|
||||
|
||||
|
||||
//setInterval(function() {
|
||||
// twit.get("/application/rate_limit_status.json",null,function(err,cb) {
|
||||
// console.log("direct_messages:",cb["resources"]["direct_messages"]);
|
||||
// });
|
||||
//
|
||||
//},10000);
|
||||
|
||||
var node = this;
|
||||
if (this.user === "user") {
|
||||
node.poll_ids = [];
|
||||
node.since_ids = {};
|
||||
var users = node.tags.split(",");
|
||||
for (var i=0;i<users.length;i++) {
|
||||
var user = users[i].replace(" ","");
|
||||
twit.getUserTimeline({
|
||||
screen_name:user,
|
||||
trim_user:0,
|
||||
count:1
|
||||
},function() {
|
||||
var u = user+"";
|
||||
return function(err,cb) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
return;
|
||||
}
|
||||
if (cb[0]) {
|
||||
node.since_ids[u] = cb[0].id_str;
|
||||
} else {
|
||||
node.since_ids[u] = '0';
|
||||
}
|
||||
node.poll_ids.push(setInterval(function() {
|
||||
twit.getUserTimeline({
|
||||
screen_name:u,
|
||||
trim_user:0,
|
||||
since_id:node.since_ids[u]
|
||||
},function(err,cb) {
|
||||
if (cb) {
|
||||
for (var t=cb.length-1;t>=0;t-=1) {
|
||||
var tweet = cb[t];
|
||||
var where = tweet.user.location||"";
|
||||
var la = tweet.lang || tweet.user.lang;
|
||||
//console.log(tweet.user.location,"=>",tweet.user.screen_name,"=>",pay);
|
||||
var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, location:where, lang:la, tweet:tweet };
|
||||
node.send(msg);
|
||||
if (t == 0) {
|
||||
node.since_ids[u] = tweet.id_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
},60000));
|
||||
}
|
||||
}());
|
||||
}
|
||||
} else if (this.user === "dm") {
|
||||
node.poll_ids = [];
|
||||
twit.getDirectMessages({
|
||||
screen_name:node.twitterConfig.screen_name,
|
||||
trim_user:0,
|
||||
count:1
|
||||
},function(err,cb) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
return;
|
||||
}
|
||||
if (cb[0]) {
|
||||
node.since_id = cb[0].id_str;
|
||||
} else {
|
||||
node.since_id = '0';
|
||||
}
|
||||
node.poll_ids.push(setInterval(function() {
|
||||
twit.getDirectMessages({
|
||||
screen_name:node.twitterConfig.screen_name,
|
||||
trim_user:0,
|
||||
since_id:node.since_id
|
||||
},function(err,cb) {
|
||||
if (cb) {
|
||||
for (var t=cb.length-1;t>=0;t-=1) {
|
||||
var tweet = cb[t];
|
||||
var msg = { topic:node.topic+"/"+tweet.sender.screen_name, payload:tweet.text, tweet:tweet };
|
||||
node.send(msg);
|
||||
if (t == 0) {
|
||||
node.since_id = tweet.id_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
node.error(err);
|
||||
}
|
||||
});
|
||||
},120000));
|
||||
});
|
||||
|
||||
} else if (this.tags !== "") {
|
||||
try {
|
||||
var thing = 'statuses/filter';
|
||||
if (this.user === "true") { thing = 'user'; }
|
||||
var st = { track: [node.tags] };
|
||||
var bits = node.tags.split(",");
|
||||
if ((bits.length > 0) && (bits.length % 4 == 0)) {
|
||||
if ((Number(bits[0]) < Number(bits[2])) && (Number(bits[1]) < Number(bits[3]))) {
|
||||
st = { locations: node.tags };
|
||||
}
|
||||
else {
|
||||
node.warn("twitter: possible bad geo area format. Should be lower-left lon,lat, upper-right lon,lat");
|
||||
}
|
||||
}
|
||||
|
||||
function setupStream() {
|
||||
if (node.active) {
|
||||
twit.stream(thing, st, function(stream) {
|
||||
//console.log(st);
|
||||
//twit.stream('user', { track: [node.tags] }, function(stream) {
|
||||
//twit.stream('site', { track: [node.tags] }, function(stream) {
|
||||
//twit.stream('statuses/filter', { track: [node.tags] }, function(stream) {
|
||||
node.stream = stream;
|
||||
stream.on('data', function(tweet) {
|
||||
//console.log(tweet.user);
|
||||
if (tweet.user !== undefined) {
|
||||
var where = tweet.user.location||"";
|
||||
var la = tweet.lang || tweet.user.lang;
|
||||
//console.log(tweet.user.location,"=>",tweet.user.screen_name,"=>",pay);
|
||||
var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, location:where, lang:la, tweet:tweet };
|
||||
node.send(msg);
|
||||
}
|
||||
});
|
||||
stream.on('limit', function(tweet) {
|
||||
node.log("tweet rate limit hit");
|
||||
});
|
||||
stream.on('error', function(tweet,rc) {
|
||||
node.warn(tweet);
|
||||
setTimeout(setupStream,10000);
|
||||
});
|
||||
stream.on('destroy', function (response) {
|
||||
if (this.active) {
|
||||
node.warn("twitter ended unexpectedly");
|
||||
setTimeout(setupStream,10000);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
setupStream();
|
||||
}
|
||||
catch (err) {
|
||||
node.error(err);
|
||||
}
|
||||
} else {
|
||||
this.error("Invalid tag property");
|
||||
}
|
||||
} else {
|
||||
this.error("missing twitter credentials");
|
||||
}
|
||||
|
||||
this.on('close', function() {
|
||||
if (this.stream) {
|
||||
this.active = false;
|
||||
this.stream.destroy();
|
||||
}
|
||||
if (this.poll_ids) {
|
||||
for (var i=0;i<this.poll_ids.length;i++) {
|
||||
clearInterval(this.poll_ids[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("twitter in",TwitterInNode);
|
||||
|
||||
|
||||
function TwitterOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.topic = n.topic;
|
||||
this.twitter = n.twitter;
|
||||
this.twitterConfig = RED.nodes.getNode(this.twitter);
|
||||
var credentials = RED.nodes.getCredentials(this.twitter);
|
||||
var node = this;
|
||||
|
||||
if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
|
||||
var twit = new ntwitter({
|
||||
consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
|
||||
consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
|
||||
access_token_key: credentials.access_token,
|
||||
access_token_secret: credentials.access_token_secret
|
||||
}).verifyCredentials(function (err, data) {
|
||||
if (err) {
|
||||
node.error("Error verifying credentials: " + err);
|
||||
} else {
|
||||
node.on("input", function(msg) {
|
||||
if (msg != null) {
|
||||
if (msg.payload.length > 140) {
|
||||
msg.payload = msg.payload.slice(0,139);
|
||||
node.warn("Tweet greater than 140 : truncated");
|
||||
}
|
||||
twit.updateStatus(msg.payload, function (err, data) {
|
||||
if (err) node.error(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("twitter out",TwitterOutNode);
|
||||
|
||||
var oa = new OAuth(
|
||||
"https://api.twitter.com/oauth/request_token",
|
||||
"https://api.twitter.com/oauth/access_token",
|
||||
"OKjYEd1ef2bfFolV25G5nQ",
|
||||
"meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
|
||||
"1.0",
|
||||
null,
|
||||
"HMAC-SHA1"
|
||||
);
|
||||
|
||||
var credentials = {};
|
||||
|
||||
RED.httpAdmin.get('/twitter/:id', function(req,res) {
|
||||
var credentials = RED.nodes.getCredentials(req.params.id);
|
||||
if (credentials) {
|
||||
res.send(JSON.stringify({sn:credentials.screen_name}));
|
||||
} else {
|
||||
res.send(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
RED.httpAdmin.delete('/twitter/:id', function(req,res) {
|
||||
RED.nodes.deleteCredentials(req.params.id);
|
||||
res.send(200);
|
||||
});
|
||||
|
||||
RED.httpAdmin.get('/twitter/:id/auth', function(req, res){
|
||||
var credentials = {};
|
||||
oa.getOAuthRequestToken({
|
||||
oauth_callback: req.query.callback
|
||||
},function(error, oauth_token, oauth_token_secret, results){
|
||||
if (error) {
|
||||
var resp = '<h2>Oh no!</h2>'+
|
||||
'<p>Something went wrong with the authentication process. The following error was returned:<p>'+
|
||||
'<p><b>'+error.statusCode+'</b>: '+error.data+'</p>'+
|
||||
'<p>One known cause of this type of failure is if the clock is wrong on system running Node-RED.';
|
||||
res.send(resp)
|
||||
} else {
|
||||
credentials.oauth_token = oauth_token;
|
||||
credentials.oauth_token_secret = oauth_token_secret;
|
||||
res.redirect('https://twitter.com/oauth/authorize?oauth_token='+oauth_token)
|
||||
RED.nodes.addCredentials(req.params.id,credentials);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
RED.httpAdmin.get('/twitter/:id/auth/callback', function(req, res, next){
|
||||
var credentials = RED.nodes.getCredentials(req.params.id);
|
||||
credentials.oauth_verifier = req.query.oauth_verifier;
|
||||
|
||||
oa.getOAuthAccessToken(
|
||||
credentials.oauth_token,
|
||||
credentials.token_secret,
|
||||
credentials.oauth_verifier,
|
||||
function(error, oauth_access_token, oauth_access_token_secret, results){
|
||||
if (error){
|
||||
console.log(error);
|
||||
res.send("yeah something broke.");
|
||||
} else {
|
||||
credentials = {};
|
||||
credentials.access_token = oauth_access_token;
|
||||
credentials.access_token_secret = oauth_access_token_secret;
|
||||
credentials.screen_name = "@"+results.screen_name;
|
||||
RED.nodes.addCredentials(req.params.id,credentials);
|
||||
res.send("<html><head></head><body>Authorised - you can close this window and return to Node-RED</body></html>");
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var FeedParser = require("feedparser");
|
||||
var request = require("request");
|
||||
|
||||
@@ -35,22 +35,18 @@ function FeedParseNode(n) {
|
||||
node.error(error);
|
||||
})
|
||||
.on('meta', function (meta) {})
|
||||
.on('article', function (article) {
|
||||
if (!(article.guid in node.seen) || ( node.seen[article.guid] != 0 && node.seen[article.guid] != article.date.getTime())) {
|
||||
node.seen[article.guid] = article.date?article.date.getTime():0;
|
||||
var msg = {
|
||||
topic:article.origlink||article.link,
|
||||
payload: article.description,
|
||||
article: {
|
||||
summary:article.summary,
|
||||
link:article.link,
|
||||
date: article.date,
|
||||
pubdate: article.pubdate,
|
||||
author: article.author,
|
||||
guid: article.guid,
|
||||
}
|
||||
};
|
||||
node.send(msg);
|
||||
.on('readable', function () {
|
||||
var stream = this, article;
|
||||
while (article = stream.read()) {
|
||||
if (!(article.guid in node.seen) || ( node.seen[article.guid] != 0 && node.seen[article.guid] != article.date.getTime())) {
|
||||
node.seen[article.guid] = article.date?article.date.getTime():0;
|
||||
var msg = {
|
||||
topic:article.origlink||article.link,
|
||||
payload: article.description,
|
||||
article: article
|
||||
};
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('end', function () {
|
@@ -25,8 +25,9 @@
|
||||
<p>Sends the <b>msg.payload</b> as an email, with a subject of <b>msg.topic</b>.</p>
|
||||
<p>It sends the message to the configured recipient <i>only</i>.</p>
|
||||
<p><b>msg.topic</b> is used to set the subject of the email, and <b>msg.payload</b> is the body text.</p>
|
||||
<p>Uses the nodemailer module - you also need to pre-configure your email SMTP settings in ../../emailkeys.js - see INSTALL file for details.</p>
|
||||
<p>Uses the nodemailer module - you also need to pre-configure your email SMTP settings in a file emailkeys.js like that below.</p>
|
||||
<p><pre>module.exports = { service: "Gmail", user: "blahblah@gmail.com", pass: "password", server: "imap.gmail.com", port: "993" }</pre></p>
|
||||
<p>This <b>must</b> be located in the diectory above node-red.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var nodemailer = require("nodemailer");
|
||||
var emailkey = require("../../../emailkeys.js");
|
||||
var emailkey = require(process.env.NODE_RED_HOME+"/../emailkeys.js");
|
||||
|
||||
var smtpTransport = nodemailer.createTransport("SMTP",{
|
||||
service: emailkey.service,
|
56
nodes/core/social/61-imap.html
Normal file
56
nodes/core/social/61-imap.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="imap">
|
||||
<div class="form-row node-input-repeat">
|
||||
<label for="node-input-repeat"><i class="icon-repeat"></i>Repeat (S)</label>
|
||||
<input type="text" id="node-input-repeat" placeholder="300">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="imap">
|
||||
<p>Repeatedly gets a <b>single email</b> from an IMAP server and forwards on as a msg if not already seen.</p>
|
||||
<p>The subject is loaded into <b>msg.topic</b> and <b>msg.payload</b> is the plain text body.
|
||||
If there is text/html then that is returned in <b>msg.html</b>. <b>msg.from</b> and <b>msg.date</b> are also set if you need them.</p>
|
||||
<p>Uses the imap module - you also need to pre-configure your email settings in a file emailkeys.js as per below.</p>
|
||||
<p><pre>module.exports = { service: "Gmail", user: "blahblah@gmail.com", pass: "password", server: "imap.gmail.com", port: "993" }</pre></p>
|
||||
<p>This <b>must</b> be located in the directory above node-red.</p>
|
||||
<p><b>Note:</b> this node <i>only</i> gets the most recent single email from the inbox, so set the repeat (polling) time appropriately.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('imap',{
|
||||
category: 'social-input',
|
||||
color:"#c7e9c0",
|
||||
defaults: {
|
||||
repeat: {value:"300",required:true},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "envelope.png",
|
||||
label: function() {
|
||||
return this.name||"IMAP";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return (this.name||!this.topic)?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
132
nodes/core/social/61-imap.js
Normal file
132
nodes/core/social/61-imap.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var Imap = require('imap');
|
||||
var util = require('util');
|
||||
|
||||
try {
|
||||
var emailkey = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js");
|
||||
} catch (err) {
|
||||
util.log("[imap] : Failed to load Email credentials");
|
||||
return;
|
||||
}
|
||||
|
||||
var imap = new Imap({
|
||||
user: emailkey.user,
|
||||
password: emailkey.pass,
|
||||
host: emailkey.server||"imap.gmail.com",
|
||||
port: emailkey.port||"993",
|
||||
tls: true,
|
||||
tlsOptions: { rejectUnauthorized: false }
|
||||
});
|
||||
|
||||
function openInbox(cb) {
|
||||
imap.openBox('INBOX', true, cb);
|
||||
}
|
||||
|
||||
function ImapNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.name = n.name;
|
||||
this.repeat = n.repeat * 1000 || 300000;
|
||||
var node = this;
|
||||
this.interval_id = null;
|
||||
var oldmail = {};
|
||||
|
||||
if (!isNaN(this.repeat) && this.repeat > 0) {
|
||||
node.log("repeat = "+this.repeat);
|
||||
this.interval_id = setInterval( function() {
|
||||
node.emit("input",{});
|
||||
}, this.repeat );
|
||||
}
|
||||
|
||||
this.on("input", function(msg) {
|
||||
imap.once('ready', function() {
|
||||
var pay = {};
|
||||
openInbox(function(err, box) {
|
||||
if (box.messages.total > 0) {
|
||||
var f = imap.seq.fetch(box.messages.total + ':*', { bodies: ['HEADER.FIELDS (FROM SUBJECT DATE)','TEXT'] });
|
||||
f.on('message', function(msg, seqno) {
|
||||
node.log('message: #'+ seqno);
|
||||
var prefix = '(#' + seqno + ') ';
|
||||
msg.on('body', function(stream, info) {
|
||||
var buffer = '';
|
||||
stream.on('data', function(chunk) {
|
||||
buffer += chunk.toString('utf8');
|
||||
});
|
||||
stream.on('end', function() {
|
||||
if (info.which !== 'TEXT') {
|
||||
pay.from = Imap.parseHeader(buffer).from[0];
|
||||
pay.topic = Imap.parseHeader(buffer).subject[0];
|
||||
pay.date = Imap.parseHeader(buffer).date[0];
|
||||
} else {
|
||||
var parts = buffer.split("Content-Type");
|
||||
for (var p in parts) {
|
||||
if (parts[p].indexOf("text/plain") >= 0) {
|
||||
pay.payload = parts[p].split("\n").slice(1,-2).join("\n").trim();
|
||||
}
|
||||
if (parts[p].indexOf("text/html") >= 0) {
|
||||
pay.html = parts[p].split("\n").slice(1,-2).join("\n").trim();
|
||||
}
|
||||
}
|
||||
//pay.body = buffer;
|
||||
}
|
||||
});
|
||||
});
|
||||
msg.on('end', function() {
|
||||
//node.log('Finished: '+prefix);
|
||||
});
|
||||
});
|
||||
f.on('error', function(err) {
|
||||
node.warn('fetch error: ' + err);
|
||||
});
|
||||
f.on('end', function() {
|
||||
if (JSON.stringify(pay) !== oldmail) {
|
||||
node.send(pay);
|
||||
oldmail = JSON.stringify(pay);
|
||||
node.log('sent new message: '+pay.topic);
|
||||
}
|
||||
else { node.log('duplicate not sent: '+pay.topic); }
|
||||
imap.end();
|
||||
});
|
||||
}
|
||||
else {
|
||||
// node.log("you have achieved inbox zero");
|
||||
imap.end();
|
||||
}
|
||||
});
|
||||
});
|
||||
imap.connect();
|
||||
});
|
||||
|
||||
imap.on('error', function(err) {
|
||||
util.log(err);
|
||||
});
|
||||
|
||||
this.on("error", function(err) {
|
||||
node.log("error: ",err);
|
||||
});
|
||||
|
||||
this.on("close", function() {
|
||||
if (this.interval_id != null) {
|
||||
clearInterval(this.interval_id);
|
||||
}
|
||||
imap.destroy();
|
||||
});
|
||||
|
||||
node.emit("input",{});
|
||||
}
|
||||
RED.nodes.registerType("imap",ImapNode);
|
@@ -19,15 +19,21 @@
|
||||
<label for="node-input-ircserver"><i class="icon-tasks"></i> IRC Server</label>
|
||||
<input type="text" id="node-input-ircserver">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-channel"><i class="icon-tasks"></i> Channel</label>
|
||||
<input type="text" id="node-input-channel" placeholder="#nodered">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="irc in">
|
||||
<p>Connects to a channel on an IRC server</p>
|
||||
<p>Any messages on that channel will appear on the <b>msg.payload</b> at the output, while <b>msg.topic</b> will contain who it is from.</p>
|
||||
<p>Connects to a channel on an IRC server</p>
|
||||
<p>Any messages on that channel will appear on the <b>msg.payload</b> at the output, while <b>msg.topic</b> will contain who it is from.</p>
|
||||
<p>The second output provides a <b>msg.payload</b> that has any status messages such as joins, parts, kicks etc.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -35,11 +41,12 @@
|
||||
category: 'social-input',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
ircserver: {type:"irc-server", required:true}
|
||||
ircserver: {type:"irc-server", required:true},
|
||||
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)}
|
||||
},
|
||||
color:"Silver",
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
outputs:2,
|
||||
icon: "hash.png",
|
||||
label: function() {
|
||||
var ircNode = RED.nodes.node(this.ircserver);
|
||||
@@ -47,31 +54,48 @@
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
if (this.ircserver !== undefined) {
|
||||
this.channel = this.channel || RED.nodes.node(this.ircserver).channel;
|
||||
$("#node-input-channel").val(this.channel);
|
||||
}
|
||||
else { this.channel = this.channel; }
|
||||
$("#node-input-channel").val(this.channel);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-red" data-template-name="irc out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-ircserver"><i class="icon-tasks"></i> IRC Server</label>
|
||||
<input type="text" id="node-input-ircserver">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-sendObject" placeholder="" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-sendObject" style="width: 70%;">Send complete msg object ?</label>
|
||||
<label for="node-input-channel"><i class="icon-tasks"></i> Channel</label>
|
||||
<input type="text" id="node-input-channel" placeholder="#nodered">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-sendObject"><i class="icon-check"></i> Action</label>
|
||||
<select type="text" id="node-input-sendObject" style="display: inline-block; vertical-align: middle; width:70%;">
|
||||
<option value="pay">Send to channel</option>
|
||||
<option value="true">Send to userid in msg.topic as PRIVMSG</option>
|
||||
<option value="false">Send complete msg object to channel</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Sending the complete object will stringify the whole msg object before sending.</div>
|
||||
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)<br/>
|
||||
Sending the complete object will stringify the whole msg object before sending.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="irc out">
|
||||
<p>Sends messages to a channel on an IRC server</p>
|
||||
<p>If you send something with NO <b>msg.topic</b> it will go to the configured channel - otherwise it will go to the id in the <b>msg.topic</b> field.</p>
|
||||
<p>You can either just send the <b>msg.payload</b>, or you can send the complete <b>msg</b> object.</p>
|
||||
<p>Sends messages to a channel on an IRC server</p>
|
||||
<p>You can send just the <b>msg.payload</b>, or the complete <b>msg</b> object to the selected channel,
|
||||
or you can select to use <b>msg.topic</b> to send the <b>msg.payload</b> to a specific user in the channel (private conversation).</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -79,8 +103,9 @@
|
||||
category: 'social-output',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
sendObject: {value:false},
|
||||
ircserver: {type:"irc-server", required:true}
|
||||
sendObject: {value:"pay", required:true},
|
||||
ircserver: {type:"irc-server", required:true},
|
||||
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)}
|
||||
},
|
||||
color:"Silver",
|
||||
inputs:1,
|
||||
@@ -92,36 +117,38 @@
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
if (this.ircserver !== undefined) {
|
||||
this.channel = this.channel || RED.nodes.node(this.ircserver).channel;
|
||||
$("#node-input-channel").val(this.channel);
|
||||
}
|
||||
else { this.channel = this.channel; }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-red" data-template-name="irc-server">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-server"><i class="icon-tasks"></i> IRC Server</label>
|
||||
<input type="text" id="node-config-input-server" placeholder="irc.UK-IRC.net">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-channel"><i class="icon-tasks"></i> Channel</label>
|
||||
<input type="text" id="node-config-input-channel" placeholder="#node-red">
|
||||
<input type="text" id="node-config-input-server" placeholder="irc.freenode.net">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-nickname"><i class="icon-tasks"></i> Nickname</label>
|
||||
<input type="text" id="node-config-input-nickname" placeholder="joe123">
|
||||
</div>
|
||||
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('irc-server',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)},
|
||||
server: {value:"",required:true},
|
||||
nickname: {value:"",required:true}
|
||||
},
|
||||
label: function() {
|
||||
return this.server+":"+this.channel;
|
||||
return this.server;
|
||||
}
|
||||
});
|
||||
</script>
|
140
nodes/core/social/91-irc.js
Normal file
140
nodes/core/social/91-irc.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var irc = require("irc");
|
||||
var util = require("util");
|
||||
|
||||
// The Server Definition - this opens (and closes) the connection
|
||||
function IRCServerNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.server = n.server;
|
||||
this.channel = n.channel;
|
||||
this.nickname = n.nickname;
|
||||
this.ircclient = null;
|
||||
this.on("close", function() {
|
||||
if (this.ircclient != null) {
|
||||
this.ircclient.disconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("irc-server",IRCServerNode);
|
||||
|
||||
|
||||
// The Input Node
|
||||
function IrcInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.ircserver = n.ircserver;
|
||||
this.serverConfig = RED.nodes.getNode(this.ircserver);
|
||||
this.channel = n.channel || this.serverConfig.channel;
|
||||
if (this.serverConfig.ircclient == null) {
|
||||
this.serverConfig.ircclient = new irc.Client(this.serverConfig.server, this.serverConfig.nickname, {
|
||||
channels: [this.channel]
|
||||
});
|
||||
this.serverConfig.ircclient.addListener('error', function(message) {
|
||||
util.log('[irc] '+ JSON.stringify(message));
|
||||
});
|
||||
}
|
||||
this.ircclient = this.serverConfig.ircclient;
|
||||
var node = this;
|
||||
|
||||
this.ircclient.addListener('message', function (from, to, message) {
|
||||
//util.log(from + ' => ' + to + ': ' + message);
|
||||
var msg = { "topic":from, "from":from, "to":to, "payload":message };
|
||||
node.send([msg,null]);
|
||||
});
|
||||
this.ircclient.addListener('pm', function(from, message) {
|
||||
var msg = { "topic":from, "from":from, "to":"PRIV", "payload":message };
|
||||
node.send([msg,null]);
|
||||
});
|
||||
|
||||
this.ircclient.addListener('join', function(channel, who) {
|
||||
var msg = { "payload": { "type":"join", "who":who, "channel":channel } };
|
||||
node.send([null,msg]);
|
||||
node.log(who+' has joined '+channel);
|
||||
});
|
||||
this.ircclient.addListener('invite', function(channel, from, message) {
|
||||
var msg = { "payload": { "type":"invite", "who":from, "channel":channel, "message":message } };
|
||||
node.send([null,msg]);
|
||||
node.log(from+' sent invite to '+channel+': '+message);
|
||||
});
|
||||
this.ircclient.addListener('part', function(channel, who, reason) {
|
||||
var msg = { "payload": { "type":"part", "who":who, "channel":channel, "reason":reason } };
|
||||
node.send([null,msg]);
|
||||
node.log(who+'has left '+channel+': '+reason);
|
||||
});
|
||||
this.ircclient.addListener('quit', function(nick, reason, channels, message) {
|
||||
var msg = { "payload": { "type":"quit", "who":nick, "channel":channels, "reason":reason } };
|
||||
node.send([null,msg]);
|
||||
node.log(nick+'has quit '+channels+': '+reason);
|
||||
});
|
||||
this.ircclient.addListener('kick', function(channel, who, by, reason) {
|
||||
var msg = { "payload": { "type":"kick", "who":who, "channel":channel, "by":by, "reason":reason } };
|
||||
node.send([null,msg]);
|
||||
node.log(who+' was kicked from '+channel+' by '+by+': '+reason);
|
||||
});
|
||||
|
||||
}
|
||||
RED.nodes.registerType("irc in",IrcInNode);
|
||||
|
||||
|
||||
// The Output Node
|
||||
function IrcOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.sendAll = n.sendObject;
|
||||
this.ircserver = n.ircserver;
|
||||
this.serverConfig = RED.nodes.getNode(this.ircserver);
|
||||
this.channel = n.channel || this.serverConfig.channel;
|
||||
if (this.serverConfig.ircclient == null) {
|
||||
this.serverConfig.ircclient = new irc.Client(this.serverConfig.server, this.serverConfig.nickname, {
|
||||
channels: [this.channel]
|
||||
});
|
||||
this.serverConfig.ircclient.addListener('error', function(message) {
|
||||
util.log('[irc] '+ JSON.stringify(message));
|
||||
});
|
||||
}
|
||||
this.ircclient = this.serverConfig.ircclient;
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
if (Object.prototype.toString.call( msg.raw ) === '[object Array]') {
|
||||
var m = msg.raw;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (typeof m[i] !== "string") { m[i] = ""; }
|
||||
m[i] = m[i].replace(/"/g, "");
|
||||
}
|
||||
util.log("[irc] RAW command:"+m);
|
||||
node.ircclient.send(m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9]);
|
||||
}
|
||||
else {
|
||||
if (msg._topic) { delete msg._topic; }
|
||||
if (node.sendAll == "false") {
|
||||
node.ircclient.say(node.channel, JSON.stringify(msg));
|
||||
}
|
||||
else {
|
||||
if (typeof msg.payload === "object") { msg.payload = JSON.stringify(msg.payload); }
|
||||
if (node.sendAll == "pay") {
|
||||
node.ircclient.say(node.channel, msg.payload);
|
||||
}
|
||||
else {
|
||||
var to = msg.topic || node.channel;
|
||||
node.ircclient.say(to, msg.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("irc out",IrcOutNode);
|
56
nodes/core/storage/28-tail.js
Normal file
56
nodes/core/storage/28-tail.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var fs = require("fs");
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
function TailNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.filename = n.filename;
|
||||
this.split = n.split;
|
||||
var node = this;
|
||||
|
||||
var err = "";
|
||||
var tail = spawn("tail", ["-f", this.filename]);
|
||||
tail.stdout.on("data", function (data) {
|
||||
var msg = {topic:node.filename};
|
||||
if (node.split) {
|
||||
var strings = data.toString().split("\n");
|
||||
for (s in strings) {
|
||||
if (strings[s] != "") {
|
||||
msg.payload = strings[s];
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
msg.payload = data.toString();
|
||||
node.send(msg);
|
||||
}
|
||||
});
|
||||
|
||||
tail.stderr.on("data", function(data) {
|
||||
node.warn(data.toString());
|
||||
});
|
||||
|
||||
this.on("close", function() {
|
||||
if (tail) tail.kill();
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("tail",TailNode);
|
@@ -36,9 +36,12 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="file">
|
||||
<p>Writes the <b>msg.payload</b> to the file specified, e.g. to create a log.</p>
|
||||
<p>A newline is added to every message. But this can be turned off if required, for example, to allow binary files to be written.</p>
|
||||
<p>The default behaviour is to append to the file. This can be changed to overwrite the file each time, for example if you want to output a "static" web page or report.</p>
|
||||
<p>Writes <b>msg.payload</b> to the file specified, e.g. to create a log.</p>
|
||||
<p>The filename can be overridden by the <code>.filename</code> property
|
||||
of the incoming message.</p>
|
||||
<p>A newline is added to every message. But this can be turned off if required, for example, to allow binary files to be written.</p>
|
||||
<p>The default behaviour is to append to the file. This can be changed to overwrite the file each time, for example if you want to output a "static" web page or report.</p>
|
||||
<p>If a <code>.delete</code> property exists then the file will be deleted instead.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -46,7 +49,7 @@
|
||||
category: 'storage-output',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
filename: {value:"",required:true},
|
||||
filename: {value:""},
|
||||
appendNewline: {value:true},
|
||||
overwriteFile: {value:false}
|
||||
},
|
62
nodes/core/storage/50-file.js
Normal file
62
nodes/core/storage/50-file.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var fs = require("fs");
|
||||
|
||||
function FileNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.filename = n.filename;
|
||||
this.appendNewline = n.appendNewline;
|
||||
this.overwriteFile = n.overwriteFile;
|
||||
var node = this;
|
||||
this.on("input",function(msg) {
|
||||
var filename = msg.filename || this.filename;
|
||||
|
||||
if (filename == "") {
|
||||
node.warn('No filename specified');
|
||||
} else if (typeof msg.payload != "undefined") {
|
||||
var data = msg.payload;
|
||||
if (typeof data == "object") { data = JSON.stringify(data); }
|
||||
if (typeof data == "boolean") { data = data.toString(); }
|
||||
if (this.appendNewline) {
|
||||
data += "\n";
|
||||
}
|
||||
if (msg.hasOwnProperty('delete')) {
|
||||
fs.unlink(filename, function (err) {
|
||||
if (err) node.warn('Failed to delete file : '+err);
|
||||
//console.log('Deleted file",filename);
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (this.overwriteFile) {
|
||||
fs.writeFile(filename, data, function (err) {
|
||||
if (err) node.warn('Failed to write to file : '+err);
|
||||
//console.log('Message written to file',filename);
|
||||
});
|
||||
}
|
||||
else {
|
||||
fs.appendFile(filename, data, function (err) {
|
||||
if (err) node.warn('Failed to append to file : '+err);
|
||||
//console.log('Message appended to file',filename);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("file",FileNode);
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var util = require("util");
|
||||
var redis = require("redis");
|
||||
|
@@ -29,6 +29,14 @@
|
||||
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-config-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-user"><i class="icon-user"></i> Username</label>
|
||||
<input type="text" id="node-config-input-user">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-pass"><i class="icon-lock"></i> Password</label>
|
||||
<input type="password" id="node-config-input-pass">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -39,10 +47,47 @@
|
||||
hostname: { value:"127.0.0.1",required:true},
|
||||
port: { value: 27017,required:true},
|
||||
db: { value:"",required:true},
|
||||
name: { value:"" }
|
||||
name: { value:"" },
|
||||
//user -> credentials
|
||||
//pass -> credentials
|
||||
},
|
||||
label: function() {
|
||||
return this.name||this.hostname+":"+this.port+"//"+this.db;
|
||||
return this.name||this.hostname+":"+this.port+"/"+this.db;
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$.getJSON('mongodb/'+this.id,function(data) {
|
||||
if (data.user) {
|
||||
$('#node-config-input-user').val(data.user);
|
||||
}
|
||||
if (data.hasPassword) {
|
||||
$('#node-config-input-pass').val('__PWRD__');
|
||||
} else {
|
||||
$('#node-config-input-pass').val('');
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
var newUser = $('#node-config-input-user').val();
|
||||
var newPass = $('#node-config-input-pass').val();
|
||||
var credentials = {};
|
||||
credentials.user = newUser;
|
||||
if (newPass != '__PWRD__') {
|
||||
credentials.password = newPass;
|
||||
}
|
||||
$.ajax({
|
||||
url: 'mongodb/'+this.id,
|
||||
type: 'POST',
|
||||
data: credentials,
|
||||
success:function(result){}
|
||||
});
|
||||
},
|
||||
ondelete: function() {
|
||||
$.ajax({
|
||||
url: 'mongodb/'+this.id,
|
||||
type: 'DELETE',
|
||||
success: function(result) {}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -60,8 +105,9 @@
|
||||
<div class="form-row">
|
||||
<label for="node-input-operation"><i class="icon-wrench"></i> Operation</label>
|
||||
<select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;">
|
||||
<option value=store>Store</option>
|
||||
<option value=delete>Delete</option>
|
||||
<option value=store>save</option>
|
||||
<option value=insert>insert</option>
|
||||
<option value=delete>remove</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-input-payonly">
|
||||
@@ -83,13 +129,13 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="mongodb out">
|
||||
<p>A simple MongoDB output node. Stores the <b>msg</b> object in a chosen collection.</p>
|
||||
<p>By default MongoDB creates an <i>_id</i> property as the primary key - so repeated injections of the same <b>msg</b> will result in many database entries.</p>
|
||||
<p>If this is NOT the desired behaviour - ie you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p>
|
||||
<p>This could be a unique constant or you could create one based on some other msg property.</p>
|
||||
<p>Currently we do not limit or cap the collection size at all... this may well change.</p>
|
||||
<p>You can also choose to <b>remove</b> items. To do so the <b>msg.payload</b> <i>MUST</i> contain an object that will select the items(s) to remove.
|
||||
A blank object will delete <i>all of the objects</i> in the collection. You have been warned...</p>
|
||||
<p>A simple MongoDB output node. Stores the <b>msg</b> object in a chosen collection.</p>
|
||||
<p>By default MongoDB creates an <i>_id</i> property as the primary key - so repeated injections of the same <b>msg</b> will result in many database entries.</p>
|
||||
<p>If this is NOT the desired behaviour - ie you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p>
|
||||
<p>This could be a unique constant or you could create one based on some other msg property.</p>
|
||||
<p>Currently we do not limit or cap the collection size at all... this may well change.</p>
|
||||
<p>You can also choose to <b>remove</b> items. To do so the <b>msg.payload</b> <i>MUST</i> contain an object that will select the items(s) to remove.
|
||||
A blank object will delete <i>all of the objects</i> in the collection. You have been warned...</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -109,7 +155,7 @@
|
||||
align: "right",
|
||||
label: function() {
|
||||
var mongoNode = RED.nodes.node(this.mongodb);
|
||||
return this.name||(mongoNode?mongoNode.label()+"//"+this.collection:"mongodb");
|
||||
return this.name||(mongoNode?mongoNode.label()+" "+this.collection:"mongodb");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
@@ -134,9 +180,9 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="mongodb in">
|
||||
<p>Queries a MongoDB collection by using the <b>msg.payload</b> to be a MongoDB query statement as per the .find() function.</p>
|
||||
<p>You may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object and a <b>msg.limit</b> object.</p>
|
||||
<p>All are optional - see the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB find docs</i></a> for examples.</p>
|
||||
<p>Queries a MongoDB collection by using the <b>msg.payload</b> to be a MongoDB query statement as per the .find() function.</p>
|
||||
<p>You may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object and a <b>msg.limit</b> object.</p>
|
||||
<p>All are optional - see the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB find docs</i></a> for examples.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -153,7 +199,7 @@
|
||||
icon: "mongodb.png",
|
||||
label: function() {
|
||||
var mongoNode = RED.nodes.node(this.mongodb);
|
||||
return this.name||(mongoNode?mongoNode.label()+"//"+this.collection:"mongodb");
|
||||
return this.name||(mongoNode?mongoNode.label()+" "+this.collection:"mongodb");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
178
nodes/core/storage/66-mongodb.js
Normal file
178
nodes/core/storage/66-mongodb.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require(process.env.NODE_RED_HOME+"/red/red");
|
||||
var mongo = require('mongodb');
|
||||
var MongoClient = mongo.MongoClient;
|
||||
|
||||
function MongoNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.hostname = n.hostname;
|
||||
this.port = n.port;
|
||||
this.db = n.db;
|
||||
this.name = n.name;
|
||||
var credentials = RED.nodes.getCredentials(n.id);
|
||||
if (credentials) {
|
||||
this.username = credentials.user;
|
||||
this.password = credentials.password;
|
||||
}
|
||||
|
||||
var url = "mongodb://";
|
||||
if (this.username && this.password) {
|
||||
url += this.username+":"+this.password+"@";
|
||||
}
|
||||
url += this.hostname+":"+this.port+"/"+this.db;
|
||||
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
RED.nodes.registerType("mongodb",MongoNode);
|
||||
|
||||
var querystring = require('querystring');
|
||||
|
||||
RED.httpAdmin.get('/mongodb/:id',function(req,res) {
|
||||
var credentials = RED.nodes.getCredentials(req.params.id);
|
||||
if (credentials) {
|
||||
res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")}));
|
||||
} else {
|
||||
res.send(JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
|
||||
RED.httpAdmin.delete('/mongodb/:id',function(req,res) {
|
||||
RED.nodes.deleteCredentials(req.params.id);
|
||||
res.send(200);
|
||||
});
|
||||
|
||||
RED.httpAdmin.post('/mongodb/:id',function(req,res) {
|
||||
var body = "";
|
||||
req.on('data', function(chunk) {
|
||||
body+=chunk;
|
||||
});
|
||||
req.on('end', function(){
|
||||
var newCreds = querystring.parse(body);
|
||||
var credentials = RED.nodes.getCredentials(req.params.id)||{};
|
||||
if (newCreds.user == null || newCreds.user == "") {
|
||||
delete credentials.user;
|
||||
} else {
|
||||
credentials.user = newCreds.user;
|
||||
}
|
||||
if (newCreds.password == "") {
|
||||
delete credentials.password;
|
||||
} else {
|
||||
credentials.password = newCreds.password||credentials.password;
|
||||
}
|
||||
RED.nodes.addCredentials(req.params.id,credentials);
|
||||
res.send(200);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function MongoOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.collection = n.collection;
|
||||
this.mongodb = n.mongodb;
|
||||
this.payonly = n.payonly || false;
|
||||
this.operation = n.operation;
|
||||
this.mongoConfig = RED.nodes.getNode(this.mongodb);
|
||||
|
||||
if (this.mongoConfig) {
|
||||
var node = this;
|
||||
MongoClient.connect(this.mongoConfig.url, function(err,db) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
} else {
|
||||
node.clientDb = db;
|
||||
var coll = db.collection(node.collection);
|
||||
node.on("input",function(msg) {
|
||||
if (node.operation == "store") {
|
||||
delete msg._topic;
|
||||
if (node.payonly) {
|
||||
if (typeof msg.payload !== "object") { msg.payload = {"payload":msg.payload}; }
|
||||
coll.save(msg.payload,function(err,item){ if (err){node.error(err);} });
|
||||
} else {
|
||||
coll.save(msg,function(err,item){if (err){node.error(err);}});
|
||||
}
|
||||
}
|
||||
else if (node.operation == "insert") {
|
||||
delete msg._topic;
|
||||
if (node.payonly) {
|
||||
if (typeof msg.payload !== "object") { msg.payload = {"payload":msg.payload}; }
|
||||
coll.insert(msg.payload,function(err,item){ if (err){node.error(err);} });
|
||||
} else {
|
||||
coll.insert(msg,function(err,item){if (err){node.error(err);}});
|
||||
}
|
||||
}
|
||||
if (node.operation == "delete") {
|
||||
coll.remove(msg.payload, {w:1}, function(err, items){ if (err) node.error(err); });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.error("missing mongodb configuration");
|
||||
}
|
||||
|
||||
this.on("close", function() {
|
||||
if (this.clientDb) {
|
||||
this.clientDb.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("mongodb out",MongoOutNode);
|
||||
|
||||
|
||||
function MongoInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.collection = n.collection;
|
||||
this.mongodb = n.mongodb;
|
||||
this.mongoConfig = RED.nodes.getNode(this.mongodb);
|
||||
|
||||
if (this.mongoConfig) {
|
||||
var node = this;
|
||||
MongoClient.connect(this.mongoConfig.url, function(err,db) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
} else {
|
||||
node.clientDb = db;
|
||||
var coll = db.collection(node.collection);
|
||||
node.on("input",function(msg) {
|
||||
msg.projection = msg.projection || {};
|
||||
coll.find(msg.payload,msg.projection).sort(msg.sort).limit(msg.limit).toArray(function(err, items) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
} else {
|
||||
msg.payload = items;
|
||||
delete msg.projection;
|
||||
delete msg.sort;
|
||||
delete msg.limit;
|
||||
node.send(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.error("missing mongodb configuration");
|
||||
}
|
||||
|
||||
this.on("close", function() {
|
||||
if (this.clientDb) {
|
||||
this.clientDb.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("mongodb in",MongoInNode);
|
@@ -1,69 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="rpi-gpio in">
|
||||
<div class="form-row">
|
||||
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
|
||||
<select type="text" id="node-input-pin" style="width: 150px;">
|
||||
<option value="7">7</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="18">18</option>
|
||||
<option value="22">22</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-resistor"><i class=" icon-resize-full"></i> Resistor?</label>
|
||||
<select type="text" id="node-input-resistor" style="width: 150px;">
|
||||
<option value="no">no</option>
|
||||
<option value="pullup">pullup</option>
|
||||
<option value="pulldown">pulldown</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-tips">Tip: if pull up/down resistor is selected, the <code>gpio-admin</code> command <em>must</em> be used
|
||||
to do the actual enabling. If 'no' resistor is selected, nothing further needs to be done
|
||||
to use this node. See <code>man gpio-admin</code> for more details.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('rpi-gpio in',{
|
||||
category: 'advanced-input',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:""},
|
||||
resistor: { value: "no"},
|
||||
pin: {value:"",required:true},
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "rpi.png",
|
||||
label: function() {
|
||||
return this.name||"Pin: "+this.pin;
|
||||
//+(this.resistor == "no"?"":" ("+(this.resistor=="pullup"?"":"↓")+")");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var gpio = require("pi-gpio");
|
||||
|
||||
function GPIOInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.buttonState = -1;
|
||||
this.pin = n.pin;
|
||||
this.resistor = n.resistor;
|
||||
|
||||
var node = this;
|
||||
|
||||
if (this.pin) {
|
||||
var setupPin = function(err) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
} else {
|
||||
node._interval = setInterval(function(){
|
||||
gpio.read(node.pin, function(err, value) {
|
||||
if(err){
|
||||
node.error(err);
|
||||
} else{
|
||||
if(node.buttonState !== value){
|
||||
var previousState = node.buttonState;
|
||||
node.buttonState = value;
|
||||
if (previousState !== -1) {
|
||||
var msg = {payload:node.buttonState};
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 50);
|
||||
}
|
||||
};
|
||||
if (this.resistor == "no") {
|
||||
gpio.open(this.pin,"input",setupPin());
|
||||
} else {
|
||||
// Assume enabled externally via gpio-admin
|
||||
setupPin();
|
||||
}
|
||||
} else {
|
||||
this.error("Invalid GPIO pin: "+this.pin);
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
|
||||
|
||||
GPIOInNode.prototype.close = function() {
|
||||
clearInterval(this._interval);
|
||||
if (this.resistor == "no") {
|
||||
gpio.close(this.pin);
|
||||
}
|
||||
}
|
||||
|
@@ -1,59 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="rpi-gpio out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
|
||||
<select type="text" id="node-input-pin" style="width: 150px;">
|
||||
<option value="7">7</option>
|
||||
<option value="11">11</option>
|
||||
<option value="12">12</option>
|
||||
<option value="13">13</option>
|
||||
<option value="15">15</option>
|
||||
<option value="16">16</option>
|
||||
<option value="18">18</option>
|
||||
<option value="22">22</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('rpi-gpio out',{
|
||||
category: 'advanced-output',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:""},
|
||||
resistor: { value: "no"},
|
||||
pin: {value:"",required:true},
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "rpi.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name||"Pin: "+this.pin;
|
||||
//+(this.resistor == "no"?"":" ("+(this.resistor=="pullup"?"":"↓")+")");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var gpio = require("pi-gpio");
|
||||
|
||||
function GPIOOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.pin = n.pin;
|
||||
|
||||
var node = this;
|
||||
|
||||
if (this.pin) {
|
||||
gpio.open(this.pin,"output",function(err) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
} else {
|
||||
node.on("input",function(msg) {
|
||||
gpio.write(node.pin,msg.payload,function(err) {
|
||||
if (err) node.error(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.error("Invalid GPIO pin: "+this.pin);
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
|
||||
|
||||
GPIOOutNode.prototype.close = function() {
|
||||
gpio.close(this.pin);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,143 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var util = require("util");
|
||||
var exec = require('child_process').exec;
|
||||
var fs = require('fs');
|
||||
|
||||
if (!fs.existsSync("/usr/local/bin/gpio")) {
|
||||
exec("cat /proc/cpuinfo | grep BCM27",function(err,stdout,stderr) {
|
||||
if (stdout.indexOf('BCM27') > -1) {
|
||||
util.log('[36-rpi-gpio.js] Error: Cannot find Wiring-Pi "gpio" command');
|
||||
}
|
||||
// else not on a Pi so don't worry anyone with needless messages.
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Map physical P1 pins to Gordon's Wiring-Pi Pins (as they should be V1/V2 tolerant)
|
||||
var pintable = {
|
||||
// Physical : WiringPi
|
||||
"7":"7",
|
||||
"11":"0",
|
||||
"12":"1",
|
||||
"13":"2",
|
||||
"15":"3",
|
||||
"16":"4",
|
||||
"18":"5",
|
||||
"22":"6"
|
||||
}
|
||||
var tablepin = {
|
||||
// WiringPi : Physical
|
||||
"7":"7",
|
||||
"0":"11",
|
||||
"1":"12",
|
||||
"2":"13",
|
||||
"3":"15",
|
||||
"4":"16",
|
||||
"5":"18",
|
||||
"6":"22"
|
||||
}
|
||||
|
||||
function GPIOInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.buttonState = -1;
|
||||
this.pin = pintable[n.pin];
|
||||
this.intype = n.intype;
|
||||
var node = this;
|
||||
|
||||
if (this.pin) {
|
||||
exec("gpio mode "+node.pin+" "+node.intype, function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
else {
|
||||
node._interval = setInterval( function() {
|
||||
exec("gpio read "+node.pin, function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
else {
|
||||
if (node.buttonState !== Number(stdout)) {
|
||||
var previousState = node.buttonState;
|
||||
node.buttonState = Number(stdout);
|
||||
if (previousState !== -1) {
|
||||
var msg = {topic:"pi/"+tablepin[node.pin], payload:node.buttonState};
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.error("Invalid GPIO pin: "+this.pin);
|
||||
}
|
||||
}
|
||||
|
||||
function GPIOOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.pin = pintable[n.pin];
|
||||
var node = this;
|
||||
|
||||
if (this.pin) {
|
||||
process.nextTick(function() {
|
||||
exec("gpio mode "+node.pin+" out", function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
else {
|
||||
node.on("input", function(msg) {
|
||||
if (msg.payload === "true") msg.payload = true;
|
||||
if (msg.payload === "false") msg.payload = false;
|
||||
var out = Number(msg.payload);
|
||||
if ((out == 0)|(out == 1)) {
|
||||
exec("gpio write "+node.pin+" "+out, function(err,stdout,stderr) {
|
||||
if (err) node.error(err);
|
||||
});
|
||||
}
|
||||
else node.warn("Invalid input - not 0 or 1");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.error("Invalid GPIO pin: "+this.pin);
|
||||
}
|
||||
}
|
||||
|
||||
exec("gpio mode 0 in",function(err,stdout,stderr) {
|
||||
if (err) {
|
||||
util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.');
|
||||
}
|
||||
exec("gpio mode 1 in");
|
||||
exec("gpio mode 2 in");
|
||||
exec("gpio mode 3 in");
|
||||
exec("gpio mode 4 in");
|
||||
exec("gpio mode 5 in");
|
||||
exec("gpio mode 6 in");
|
||||
exec("gpio mode 7 in",function(err,stdout,stderr) {
|
||||
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
|
||||
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
|
||||
|
||||
GPIOInNode.prototype.close = function() {
|
||||
clearInterval(this._interval);
|
||||
}
|
||||
|
||||
GPIOOutNode.prototype.close = function() {
|
||||
exec("gpio mode "+this.pin+" in");
|
||||
}
|
||||
|
||||
});
|
||||
});
|
@@ -1,49 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="blinkstick">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Expects a msg.payload with either hex #rrggbb or decimal red,green,blue.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="blinkstick">
|
||||
<p>BlinkStick output node. Expects a <b>msg.payload</b> with either a hex string #rrggbb triple or red,green,blue as three 0-255 values.</p>
|
||||
<p><b>NOTE:</b> currently only works with a single BlinkStick. (As it uses the findFirst() function to attach).</p>
|
||||
<p>For more info see the <i><a href="http://blinkstick.com/" target="_new">BlinkStick website</a></i> or the <i><a href="https://github.com/arvydas/blinkstick-node" target="_new">node module</a></i> documentation.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('blinkstick',{
|
||||
category: 'output',
|
||||
color:"GoldenRod",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "light.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name||"blinkstick";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var blinkstick = require("blinkstick");
|
||||
|
||||
Object.size = function(obj) {
|
||||
var size = 0, key;
|
||||
for (key in obj) { if (obj.hasOwnProperty(key)) size++; }
|
||||
return size;
|
||||
};
|
||||
|
||||
function BlinkStick(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var p1 = /^\#[A-Fa-f0-9]{6}$/
|
||||
var p2 = /[0-9]+,[0-9]+,[0-9]+/
|
||||
this.led = blinkstick.findFirst(); // maybe try findAll() (one day)
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
if (msg != null) {
|
||||
if (Object.size(node.led) !== 0) {
|
||||
try {
|
||||
if (p2.test(msg.payload)) {
|
||||
var rgb = msg.payload.split(",");
|
||||
node.led.setColor(parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255);
|
||||
}
|
||||
else {
|
||||
node.led.setColor(msg.payload);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
node.warn("BlinkStick missing ?");
|
||||
node.led = blinkstick.findFirst();
|
||||
}
|
||||
}
|
||||
else {
|
||||
//node.warn("No BlinkStick found");
|
||||
node.led = blinkstick.findFirst();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (Object.size(node.led) === 0) {
|
||||
node.error("No BlinkStick found");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType("blinkstick",BlinkStick);
|
@@ -1,52 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="blink1">
|
||||
<div class="form-row">
|
||||
<label for="node-input-fade"><i class="icon-signal"></i> Fade (mS)</label>
|
||||
<input type="text" id="node-input-fade" placeholder="0">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Expects a msg.payload with three part csv string of r,g,b.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="blink1">
|
||||
<p>Thingm Blink1 output node. Expects a msg.payload with a three part csv string of r,g,b.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('blink1',{
|
||||
category: 'output',
|
||||
color:"GoldenRod",
|
||||
defaults: {
|
||||
fade: {value:"0",required:true,validate:RED.validators.number()},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "light.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name||"blink1";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return (this.name||!this.topic)?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var Blink1 = require("node-blink1");
|
||||
|
||||
function Blink1Node(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.fade = n.fade||0;
|
||||
var node = this;
|
||||
|
||||
try {
|
||||
var p1 = /^\#[A-Fa-f0-9]{6}$/
|
||||
var p2 = /[0-9]+,[0-9]+,[0-9]+/
|
||||
this.on("input", function(msg) {
|
||||
if (blink1) {
|
||||
if (p1.test(msg.payload)) {
|
||||
// if it is a hex colour string
|
||||
var r = parseInt(msg.payload.slice(1,3),16);
|
||||
var g = parseInt(msg.payload.slice(3,5),16);
|
||||
var b = parseInt(msg.payload.slice(5),16);
|
||||
if (node.fade == 0) { blink1.setRGB( r, g, b ); }
|
||||
else { blink1.fadeToRGB(node.fade, r, g, b ); }
|
||||
}
|
||||
else if (p2.test(msg.payload)) {
|
||||
// if it is a r,g,b triple
|
||||
var rgb = msg.payload.split(',');
|
||||
if (node.fade == 0) { blink1.setRGB(parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255); }
|
||||
else { blink1.fadeToRGB(node.fade, parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255); }
|
||||
}
|
||||
else {
|
||||
// you can do fancy colours by name here if you want...
|
||||
node.warn("Blink1 : invalid msg : "+msg.payload);
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.warn("No Blink1 found");
|
||||
}
|
||||
});
|
||||
var blink1 = new Blink1.Blink1();
|
||||
}
|
||||
catch(e) {
|
||||
node.error("No Blink1 found");
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.registerType("blink1",Blink1Node);
|
@@ -1,50 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="ledborg">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Expects a msg.payload with PiBorg three digit rgb colour string. 000 -> 222</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="ledborg">
|
||||
<p>PiBorg LedBorg LED output node. Expects a <b>msg.payload</b> with a three digit rgb triple, from <b>000</b> to <b>222</b>.</p>
|
||||
<p>See <i><a href="http://www.piborg.com/ledborg/install" target="_new">the PiBorg site</a></i> for more information.</p>
|
||||
<p>You can also now use a <b>msg.payload</b> in the standard hex format "#rrggbb". The clip levels are :</p>
|
||||
<p><pre>0x00 - 0x57 = off<br/>0x58 - 0xA7 = 50%<br/>0xA8 - 0xFF = fully on</pre></p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('ledborg',{
|
||||
category: 'output',
|
||||
color:"GoldenRod",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "light.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name||"ledborg";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var util = require('util');
|
||||
var fs = require('fs');
|
||||
|
||||
// check if /dev/ledborg exists - if not then don't even show the node.
|
||||
if (!fs.existsSync("/dev/ledborg")) {
|
||||
util.log("[78-ledborg.js] Error: PiBorg hardware : LedBorg not found");
|
||||
return;
|
||||
}
|
||||
|
||||
function LedBorgNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var p1 = /[0-2][0-2][0-2]/
|
||||
var p2 = /^\#[A-Fa-f0-9]{6}$/
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
if (p1.test(msg.payload)) {
|
||||
fs.writeFile('/dev/ledborg', msg.payload, function (err) {
|
||||
if (err) node.warn(msg.payload+" : No LedBorg found");
|
||||
});
|
||||
}
|
||||
if (p2.test(msg.payload)) {
|
||||
var r = Math.floor(parseInt(msg.payload.slice(1,3),16)/88).toString();
|
||||
var g = Math.floor(parseInt(msg.payload.slice(3,5),16)/88).toString();
|
||||
var b = Math.floor(parseInt(msg.payload.slice(5),16)/88).toString();
|
||||
fs.writeFile('/dev/ledborg', r+g+b, function (err) {
|
||||
if (err) node.warn(r+g+b+" : No LedBorg found");
|
||||
});
|
||||
}
|
||||
else {
|
||||
node.warn("Invalid LedBorg colour code");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("ledborg",LedBorgNode);
|
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var util = require("util");
|
||||
var http = require("http");
|
||||
var https = require("https");
|
||||
var urllib = require("url");
|
||||
var bodyParser = require("express").bodyParser();
|
||||
|
||||
function HTTPIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.url = n.url;
|
||||
this.method = n.method;
|
||||
|
||||
var node = this;
|
||||
this.callback = function(req,res) {
|
||||
node.send({req:req,res:res});
|
||||
}
|
||||
if (this.method == "get") {
|
||||
RED.app.get(this.url,this.callback);
|
||||
} else if (this.method == "post") {
|
||||
RED.app.post(this.url,bodyParser,this.callback);
|
||||
} else if (this.method == "put") {
|
||||
RED.app.put(this.url,bodyParser,this.callback);
|
||||
} else if (this.method == "delete") {
|
||||
RED.app.delete(this.url,this.callback);
|
||||
}
|
||||
|
||||
this.on("close",function() {
|
||||
var routes = RED.app.routes[this.method];
|
||||
for (var i in routes) {
|
||||
if (routes[i].path == this.url) {
|
||||
routes.splice(i,1);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("http in",HTTPIn);
|
||||
|
||||
|
||||
function HTTPOut(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
this.on("input",function(msg) {
|
||||
if (msg.res) {
|
||||
if (msg.headers) {
|
||||
res.set(msg.headers);
|
||||
}
|
||||
var statusCode = msg.statusCode || 200;
|
||||
msg.res.send(statusCode,msg.payload);
|
||||
} else {
|
||||
node.warn("No response object");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("http response",HTTPOut);
|
||||
|
||||
function HTTPRequest(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var url = n.url;
|
||||
var method = n.method || "GET";
|
||||
var httplib = (/^https/.test(url))?https:http;
|
||||
var node = this;
|
||||
this.on("input",function(msg) {
|
||||
|
||||
var opts = urllib.parse(msg.url||url);
|
||||
opts.method = (msg.method||method).toUpperCase();
|
||||
if (msg.headers) {
|
||||
opts.header = msg.headers;
|
||||
}
|
||||
var req = httplib.request(opts,function(res) {
|
||||
res.setEncoding('utf8');
|
||||
var message = {
|
||||
statusCode: res.statusCode,
|
||||
headers: res.headers,
|
||||
payload: ""
|
||||
};
|
||||
res.on('data',function(chunk) {
|
||||
message.payload += chunk;
|
||||
});
|
||||
res.on('end',function() {
|
||||
node.send(message);
|
||||
});
|
||||
});
|
||||
req.on('error',function(err) {
|
||||
msg.payload = err.toString();
|
||||
msg.statusCode = err.code;
|
||||
node.send(msg);
|
||||
});
|
||||
if (msg.payload && (method == "PUSH" || method == "PUT") ) {
|
||||
if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
|
||||
req.write(msg.payload);
|
||||
} else if (typeof msg.payload == "number") {
|
||||
req.write(msg.payload+"");
|
||||
} else {
|
||||
req.write(JSON.stringify(msg.payload));
|
||||
}
|
||||
}
|
||||
req.end();
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("http request",HTTPRequest);
|
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var notify = require("fs.notify");
|
||||
|
||||
function WatchNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.files = n.files.split(",");
|
||||
for (var f in this.files) {
|
||||
this.files[f] = this.files[f].trim();
|
||||
}
|
||||
var node = this;
|
||||
var notifications = new notify(this.files);
|
||||
notifications.on('change', function (file) {
|
||||
node.log('file changed '+file);
|
||||
var msg = { payload: file, topic: JSON.stringify(node.files) };
|
||||
node.send(msg);
|
||||
});
|
||||
|
||||
this._close = function() {
|
||||
notifications.close();
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.registerType("watch",WatchNode);
|
||||
|
||||
WatchNode.prototype.close = function() {
|
||||
this._close();
|
||||
}
|
||||
|
@@ -1,69 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="socket in">
|
||||
<div class="form-row">
|
||||
<label for="node-input-transport"><i class="icon-tasks"></i> Type</label>
|
||||
<select type="text" id="node-input-transport" style="width: 150px;">
|
||||
<option value="http">http listen</option>
|
||||
<option value="tcp">tcp server</option>
|
||||
<!-- <option value="tcpc">tcp client</option> -->
|
||||
<option value="udp">udp socket</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-port"><i class="icon-random"></i> Port</label>
|
||||
<input type="text" id="node-input-port" placeholder="Port">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
|
||||
<input type="text" id="node-input-topic" placeholder="Topic">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
<div class="form-tips">Tip: sends the received data as a Buffer object.</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="socket in">
|
||||
<p>Provides a input node for http, tcp or udp sockets. Topic is optional. These are server like sockets.</p>
|
||||
<p>The TCP and UDP sockets produce a <i>BUFFER</i> object msg.payload and NOT a String. If you need a String then use .toString() on msg.payload in your next function block.</p>
|
||||
<p>TCP and UDP sockets also provide <b>msg.fromip</b> of the form ipaddress:port</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('socket in',{
|
||||
category: 'deprecated',
|
||||
color:"Silver",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
topic: {value:""},
|
||||
port: {value:"",required:true},
|
||||
transport: {value:"tcp",required:true}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "bridge-dash.png",
|
||||
label: function() {
|
||||
return this.name||this.topic||("socket "+this.transport+":"+this.port);
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
@@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
|
||||
function SocketIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.warn("node type deprecated");
|
||||
this.port = n.port;
|
||||
this.topic = n.topic;
|
||||
this.trans = (n.transport||n.trans||"").toLowerCase();
|
||||
var node = this;
|
||||
if (this.trans == "http") {
|
||||
var http = require('http');
|
||||
var server = http.createServer(function (req, res) {
|
||||
//node.log("http "+req.url);
|
||||
var msg = {topic:node.topic,payload:req.url.slice(1)};
|
||||
node.send(msg);
|
||||
res.writeHead(304, {'Content-Type': 'text/plain'});
|
||||
res.end('\n');
|
||||
}).listen(node.port);
|
||||
server.on('error', function (e) {
|
||||
if (e.code == 'EADDRINUSE') {
|
||||
setTimeout(node.error('TCP port is already in use - please reconfigure socket.'),250);
|
||||
}
|
||||
else { console.log(e); }
|
||||
server = null;
|
||||
});
|
||||
node.log('http listener at http://127.0.0.1:'+node.port+'/');
|
||||
|
||||
this._close = function() {
|
||||
if (server) server.close();
|
||||
node.log('http listener stopped');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.trans == "tcp") {
|
||||
var net = require('net');
|
||||
var server = net.createServer(function (socket) {
|
||||
var buffer = null;
|
||||
socket.on('data', function (chunk) {
|
||||
if (buffer == null) {
|
||||
buffer = chunk;
|
||||
} else {
|
||||
buffer = Buffer.concat([buffer,chunk]);
|
||||
}
|
||||
});
|
||||
socket.on('end', function() {
|
||||
var msg = {topic:node.topic, payload:buffer, fromip:socket.remoteAddress+':'+socket.remotePort};
|
||||
node.send(msg);
|
||||
});
|
||||
});
|
||||
server.on('error', function (e) {
|
||||
if (e.code == 'EADDRINUSE') {
|
||||
setTimeout(node.error('TCP port is already in use - please reconfigure socket.'),250);
|
||||
}
|
||||
else { console.log(e); }
|
||||
server = null;
|
||||
});
|
||||
server.listen(node.port);
|
||||
node.log('tcp listener on port :'+node.port);
|
||||
|
||||
this._close = function() {
|
||||
if (server) server.close();
|
||||
node.log('tcp listener stopped');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.trans == "tcpc") {
|
||||
var net = require('net');
|
||||
var client;
|
||||
var to;
|
||||
function setupTcpClient() {
|
||||
node.log('tcpc connecting to port :'+node.port);
|
||||
client = net.connect({port: node.port}, function() {
|
||||
node.log("tcpc connected");
|
||||
});
|
||||
|
||||
client.on('data', function (data) {
|
||||
var msg = {topic:node.topic, payload:data};
|
||||
node.send(msg);
|
||||
});
|
||||
|
||||
client.on('end', function() {
|
||||
node.log("tcpc socket ended");
|
||||
});
|
||||
|
||||
client.on('close', function() {
|
||||
node.log('tcpc socket closed');
|
||||
to = setTimeout(setupTcpClient, 10000); //Try to reconnect
|
||||
});
|
||||
|
||||
client.on('error', function() {
|
||||
node.log('tcpc socket error');
|
||||
client = null;
|
||||
to = setTimeout(setupTcpClient, 10000); //Try to reconnect
|
||||
});
|
||||
}
|
||||
setupTcpClient();
|
||||
|
||||
this._close = function() {
|
||||
if (client) client.end();
|
||||
//client.destroy();
|
||||
clearTimeout(to);
|
||||
node.log('tcpc stopped client');
|
||||
}
|
||||
setupTcpClient();
|
||||
}
|
||||
|
||||
if (this.trans == "udp") {
|
||||
var dgram = require('dgram');
|
||||
var server = dgram.createSocket('udp4');
|
||||
server.on('listening', function () {
|
||||
var address = server.address();
|
||||
node.log('udp listener at ' + address.address + ":" + address.port);
|
||||
});
|
||||
server.on('message', function (message, remote) {
|
||||
var msg = {topic:node.topic,payload:message,fromip:remote.address+':'+remote.port};
|
||||
node.send(msg);
|
||||
});
|
||||
server.on('error', function (e) {
|
||||
console.log(e);
|
||||
server = null;
|
||||
});
|
||||
server.bind(node.port);
|
||||
|
||||
this._close = function() {
|
||||
if (server) server.close();
|
||||
node.log('udp listener stopped');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType("socket in",SocketIn);
|
||||
|
||||
SocketIn.prototype.close = function() {
|
||||
this._close();
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="socket out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-host"><i class="icon-bookmark"></i> Host</label>
|
||||
<input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;" >
|
||||
|
||||
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
|
||||
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-transport"><i class="icon-tasks"></i> Type</label>
|
||||
<select type="text" id="node-input-transport" style="width: 150px;">
|
||||
<option value="http">http</option>
|
||||
<option value="tcp">tcp</option>
|
||||
<option value="udp">udp</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="socket out">
|
||||
<p>Provides a choice of http, tcp or udp output connections. All connect, send their <b>msg.payload</b> and disconnect.</p>
|
||||
<p>To use broadcast select udp and set the host to be either the subnet broadcast address required or 255.255.255.255</p>
|
||||
<p>If you need a response from an http request use the httpget node instead.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('socket out',{
|
||||
category: 'deprecated',
|
||||
color:"Silver",
|
||||
defaults: {
|
||||
host: {value:"127.0.0.1",required:true},
|
||||
port: {value:"",required:true},
|
||||
name: {value:""},
|
||||
transport: {value:"tcp",required:true}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "bridge-dash.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name||this.topic||("socket "+this.transport+":"+this.port);
|
||||
},
|
||||
labelStyle: function() {
|
||||
return (this.name||!this.topic)?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
|
||||
function SocketOut(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.warn("node type deprecated");
|
||||
this.host = n.host;
|
||||
this.port = n.port * 1;
|
||||
this.name = n.name;
|
||||
this.trans = n.transport||n.trans||"";
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
if (msg != null) {
|
||||
if (this.trans == "http") {
|
||||
var http = require("http");
|
||||
http.get(msg.payload, function(res) {
|
||||
node.log("http : response : " + res.statusCode);
|
||||
}).on('error', function(e) {
|
||||
node.error("http : error : " + e.message);
|
||||
});
|
||||
}
|
||||
if (this.trans == "tcp") {
|
||||
var net = require('net');
|
||||
var client = new net.Socket();
|
||||
client.on('error', function (err) {
|
||||
node.error('tcp : '+err);
|
||||
});
|
||||
client.connect(this.port, this.host, function() {
|
||||
try { client.end(msg.payload); }
|
||||
catch (e) { node.error(e); }
|
||||
});
|
||||
}
|
||||
if (this.trans == "udp") {
|
||||
var dgram = require('dgram');
|
||||
var sock = dgram.createSocket('udp4'); // only use ipv4 for now
|
||||
sock.bind(this.port); // have to bind before you can enable broadcast...
|
||||
sock.setBroadcast(true); // turn on broadcast
|
||||
var buf = new Buffer(msg.payload);
|
||||
sock.send(buf, 0, buf.length, this.port, this.host, function(err, bytes) {
|
||||
if (err) node.error("udp : "+err);
|
||||
//util.log('[socket out] udp :' +bytes);
|
||||
sock.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
var node = this;
|
||||
}
|
||||
|
||||
RED.nodes.registerType("socket out",SocketOut);
|
@@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var RED = require("../../red/red");
|
||||
var reconnectTime = RED.settings.socketReconnectTime||10000;
|
||||
var net = require('net');
|
||||
|
||||
function TcpIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.host = n.host;
|
||||
this.port = n.port * 1;
|
||||
this.topic = n.topic;
|
||||
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
|
||||
this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
|
||||
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r");
|
||||
this.base64 = n.base64;
|
||||
this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
|
||||
this.closing = false;
|
||||
var node = this;
|
||||
|
||||
if (!node.server) {
|
||||
var buffer = null;
|
||||
var client;
|
||||
var reconnectTimeout;
|
||||
function setupTcpClient() {
|
||||
node.log("connecting to "+node.host+":"+node.port);
|
||||
client = net.connect(node.port, node.host, function() {
|
||||
buffer = (node.datatype == 'buffer')? new Buffer(0):"";
|
||||
node.log("connected to "+node.host+":"+node.port);
|
||||
});
|
||||
|
||||
client.on('data', function (data) {
|
||||
if (node.datatype != 'buffer') {
|
||||
data = data.toString(node.datatype);
|
||||
}
|
||||
if (node.stream) {
|
||||
if ((node.datatype) === "utf8" && node.newline != "") {
|
||||
buffer = buffer+data;
|
||||
var parts = buffer.split(node.newline);
|
||||
for (var i = 0;i<parts.length-1;i+=1) {
|
||||
var msg = {topic:node.topic, payload:parts[i]};
|
||||
node.send(msg);
|
||||
}
|
||||
buffer = parts[parts.length-1];
|
||||
} else {
|
||||
var msg = {topic:node.topic, payload:data};
|
||||
node.send(msg);
|
||||
}
|
||||
} else {
|
||||
if ((typeof data) === "string") {
|
||||
buffer = buffer+data;
|
||||
} else {
|
||||
buffer = Buffer.concat([buffer,data],buffer.length+data.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
client.on('end', function() {
|
||||
if (!node.stream || (node.datatype == "utf8" && node.newline != "" && buffer.length > 0)) {
|
||||
var msg = {topic:node.topic,payload:buffer};
|
||||
node.send(msg);
|
||||
buffer = null;
|
||||
}
|
||||
});
|
||||
|
||||
client.on('close', function() {
|
||||
node.log("connection lost to "+node.host+":"+node.port);
|
||||
if (!node.closing) {
|
||||
reconnectTimeout = setTimeout(setupTcpClient, reconnectTime);
|
||||
}
|
||||
});
|
||||
|
||||
client.on('error', function(err) {
|
||||
node.log(err);
|
||||
});
|
||||
}
|
||||
setupTcpClient();
|
||||
|
||||
this._close = function() {
|
||||
this.closing = true;
|
||||
client.end();
|
||||
clearTimeout(reconnectTimeout);
|
||||
}
|
||||
} else {
|
||||
var server = net.createServer(function (socket) {
|
||||
var buffer = (node.datatype == 'buffer')? new Buffer(0):"";
|
||||
socket.on('data', function (data) {
|
||||
if (node.datatype != 'buffer') {
|
||||
data = data.toString(node.datatype);
|
||||
}
|
||||
|
||||
if (node.stream) {
|
||||
if ((typeof data) === "string" && node.newline != "") {
|
||||
buffer = buffer+data;
|
||||
var parts = buffer.split(node.newline);
|
||||
for (var i = 0;i<parts.length-1;i+=1) {
|
||||
var msg = {topic:node.topic, payload:parts[i]};
|
||||
node.send(msg);
|
||||
}
|
||||
buffer = parts[parts.length-1];
|
||||
} else {
|
||||
var msg = {topic:node.topic, payload:data};
|
||||
node.send(msg);
|
||||
}
|
||||
} else {
|
||||
if ((typeof data) === "string") {
|
||||
buffer = buffer+data;
|
||||
} else {
|
||||
buffer = Buffer.concat([buffer,data],buffer.length+data.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.on('end', function() {
|
||||
if (!node.stream || (node.datatype == "utf8" && node.newline != "" && buffer.length > 0)) {
|
||||
var msg = {topic:node.topic,payload:buffer};
|
||||
node.send(msg);
|
||||
buffer = null;
|
||||
}
|
||||
});
|
||||
socket.on('error',function(err) {
|
||||
node.log(err);
|
||||
});
|
||||
});
|
||||
server.listen(node.port);
|
||||
node.log('listening on port '+node.port);
|
||||
|
||||
this._close = function() {
|
||||
this.closing = true;
|
||||
server.close();
|
||||
node.log('stopped listening on port '+node.port);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType("tcp in",TcpIn);
|
||||
|
||||
TcpIn.prototype.close = function() {
|
||||
this._close();
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="tcp out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-beserver"><i class="icon-resize-small"></i> Type</label>
|
||||
<select id="node-input-beserver" style="width:120px; margin-right:5px;">
|
||||
<option value="server">Listen on</option>
|
||||
<option value="client">Connect to</option>
|
||||
</select>
|
||||
port <input type="text" id="node-input-port" style="width: 50px">
|
||||
</div>
|
||||
|
||||
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
|
||||
at host <input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-base64" style="width: 70%;">Decode Base64 message ?</label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="Name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="tcp out">
|
||||
<p>Provides a choice of tcp outputs. Can either connect to a remote tcp port,
|
||||
or accept incoming connections.</p>
|
||||
<p>Only <b>msg.payload</b> is sent.</p>
|
||||
<p>If <b>msg.payload</b> is a string containing a base64 encoding of binary
|
||||
data, the Base64 decoding option will cause it to be converted back to binary
|
||||
before being sent.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('tcp out',{
|
||||
category: 'output',
|
||||
color:"Silver",
|
||||
defaults: {
|
||||
host: {value:"",validate:function(v) { return (this.beserver == "server")||v.length > 0;} },
|
||||
port: {value:"",required:true},
|
||||
beserver: {value:"client",required:true},
|
||||
base64: {value:false,required:true},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "bridge-dash.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
|
||||
},
|
||||
labelStyle: function() {
|
||||
return (this.name)?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var updateOptions = function() {
|
||||
var sockettype = $("#node-input-beserver option:selected").val();
|
||||
if (sockettype == "client") {
|
||||
$("#node-input-host-row").show();
|
||||
} else {
|
||||
$("#node-input-host-row").hide();
|
||||
}
|
||||
};
|
||||
updateOptions();
|
||||
$("#node-input-beserver").change(updateOptions);
|
||||
}
|
||||
});
|
||||
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user