diff --git a/hardware/BBB/145-BBB-hardware.js b/hardware/BBB/145-BBB-hardware.js index 70205a48..78ba7151 100644 --- a/hardware/BBB/145-BBB-hardware.js +++ b/hardware/BBB/145-BBB-hardware.js @@ -16,13 +16,7 @@ // Require main module var RED = require(process.env.NODE_RED_HOME + "/red/red"); - -// Require bonescript -try { - var bonescript = require("bonescript"); -} catch (err) { - require("util").log("[145-BBB-hardware] Error: cannot find module 'bonescript'"); -} +var bonescript = require("bonescript"); // Node constructor for bbb-analogue-in function AnalogueInputNode(n) { @@ -46,7 +40,7 @@ function AnalogueInputNode(n) { // Variables used for input averaging var sum; // accumulates the input readings to be averaged var count; // keep track of the number of measurements made - + // The callback function for analogRead. Accumulates the required number of // measurements, then divides the total number, applies output scaling and // sends the result @@ -113,11 +107,11 @@ function DiscreteInputNode(n) { this.starting = true; this.debouncing = false; // True after a change of state while waiting for the 7ms debounce time to elapse this.debounceTimer = null; - + // Define 'node' to allow us to access 'this' from within callbacks var node = this; - // This function is called by the input pin change-of-state interrupt. If + // This function is called by the input pin change-of-state interrupt. If // debounce is disabled, send the output message. Otherwise, if we are // currently debouncing, ignore this interrupt. If we are not debouncing, // schedule a re-read of the input pin in 7ms time, and set the debouncing flag @@ -146,7 +140,7 @@ function DiscreteInputNode(n) { sendStateMessage(x); } }; - + // This function is called when either the interruptCallback or the debounceCallback // have determined we have a 'genuine' change of state. Update the currentState and // ActiveTime variables, and send a message on the first output with the new state @@ -165,7 +159,7 @@ function DiscreteInputNode(n) { node.send([msg, null]); } }; - + // This function is called by the timer. It updates the ActiveTime variables, and sends a // message on the second output with the latest value of the total active time, in seconds var timerCallback = function () { @@ -270,7 +264,7 @@ function PulseInputNode(n) { // Define 'node' to allow us to access 'this' from within callbacks var node = this; - // Called by the edge or pulse interrupt. If this is a valid interrupt, record the + // Called by the edge or pulse interrupt. If this is a valid interrupt, record the // pulse time and count the pulse var interruptCallback = function (x) { if (x.value !== undefined) { @@ -350,12 +344,12 @@ function DiscreteOutputNode(n) { this.defaultState = Number(n.defaultState); // What state to set up as this.inverting = n.inverting; this.toggle = n.toggle; - + // Working variables this.currentState = this.defaultState; - + var node = this; - + // If the input message paylod is numeric, values > 0.5 are 'true', otherwise use // the truthiness of the payload. Apply the inversion flag before setting the output var inputCallback = function (msg) { @@ -378,7 +372,7 @@ function DiscreteOutputNode(n) { node.send({ topic:node.topic, payload:newState }); node.currentState = newState; }; - + // If we have a valid pin, set it as an output and set the default state if (["P8_7", "P8_8", "P8_9", "P8_10", "P8_11", "P8_12", "P8_13", "P8_14", "P8_15", "P8_16", "P8_17", "P8_18", "P8_19", "P8_26", "P9_11", "P9_12", "P9_13", "P9_14", @@ -407,12 +401,12 @@ function PulseOutputNode(n) { this.defaultState = this.pulseState === 1 ? 0 : 1; this.retriggerable = n.retriggerable; this.pulseTime = n.pulseTime * 1000; // Pulse width in milliseconds - + // Working variables this.pulseTimer = null; // Non-null while a pulse is being generated - + var node = this; - + // Generate a pulse in response to an input message. If the topic includes the text // 'time' (case insensitive) and the payload is numeric, use this value as the // pulse time. Otherwise use the value from the properties dialog. @@ -444,14 +438,14 @@ function PulseOutputNode(n) { } } }; - + // At the end of the pulse, restore the default state and set the timer to null var endPulseCallback = function () { node.pulseTimer = null; bonescript.digitalWrite(node.pin, node.defaultState); node.send({ topic:node.topic, payload:node.defaultState }); }; - + // If we have a valid pin, set it as an output and set the default state if (["P8_7", "P8_8", "P8_9", "P8_10", "P8_11", "P8_12", "P8_13", "P8_14", "P8_15", "P8_16", "P8_17", "P8_18", "P8_19", "P8_26", "P9_11", "P9_12", "P9_13", "P9_14", diff --git a/hardware/Pi/37-rpi-piface.html b/hardware/Pi/37-rpi-piface.html index 36e91117..d5472b28 100644 --- a/hardware/Pi/37-rpi-piface.html +++ b/hardware/Pi/37-rpi-piface.html @@ -52,10 +52,11 @@ + + + + + + + + + + + diff --git a/hardware/Pi/38-rpi-pibrella.js b/hardware/Pi/38-rpi-pibrella.js new file mode 100644 index 00000000..1a254f3e --- /dev/null +++ b/hardware/Pi/38-rpi-pibrella.js @@ -0,0 +1,172 @@ +/** + * 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. + * 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 + "Amber LED":"0", + "Buzzer ":"1", + "Red LED":"2", + "Out E":"3", + "Out F":"4", + "Out G":"5", + "Out H":"6", + "Green LED":"7", + "In C":"10", + "In B":"11", + "In D":"12", + "In A":"13", + "Red Button":"14", +} +var tablepin = { +// WiringPi : Physical + "0":"Amber", + "1":"Buzzer", + "2":"Red", + "3":"E", + "4":"F", + "5":"G", + "6":"H", + "7":"Green", + "10":"C", + "11":"B", + "12":"D", + "13":"A", + "14":"R", +} + +function PibrellaIn(n) { + RED.nodes.createNode(this,n); + this.buttonState = -1; + this.pin = pintable[n.pin]; + var node = this; + + if (this.pin) { + exec("gpio mode "+node.pin+" in", 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:"pibrella/"+tablepin[node.pin], payload:node.buttonState}; + node.send(msg); + } + } + } + }); + }, 200); + } + }); + } + else { + this.error("Invalid GPIO pin: "+this.pin); + } + + this.on("close", function() { + clearInterval(this._interval); + }); +} + +function PibrellaOut(n) { + RED.nodes.createNode(this,n); + this.pin = pintable[n.pin]; + var node = this; + + if (this.pin == "1") { + exec("gpio mode 1 pwm"); + process.nextTick(function() { + exec("gpio pwm-ms"); + node.on("input", function(msg) { + var out = Number(msg.payload); + if (out == 1) { // fixed buzz + exec("gpio pwm 1 511"); + exec("gpio pwmc 100"); + } + else if ((out >= 2) && (out <= 9999)) { // set buzz to a value + exec("gpio pwm 1 511"); + exec("gpio pwmc "+out); + } + else { exec("gpio pwm 1 0"); } // turn it off + }); + }); + } + else 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 out",function(err,stdout,stderr) { + if (err) { + util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.'); + } + exec("gpio mode 1 out"); + exec("gpio mode 2 out"); + exec("gpio mode 3 out"); + exec("gpio mode 4 out"); + exec("gpio mode 5 out"); + exec("gpio mode 6 out"); + exec("gpio mode 7 out"); + exec("gpio mode 10 in"); + exec("gpio mode 11 in"); + exec("gpio mode 12 in"); + exec("gpio mode 13 in"); + exec("gpio mode 14 in"); +}); + +RED.nodes.registerType("rpi-pibrella in",PibrellaIn); +RED.nodes.registerType("rpi-pibrella out",PibrellaOut); diff --git a/hardware/Pi/78-ledborg.html b/hardware/Pi/78-ledborg.html index 0fefa704..55d128fe 100644 --- a/hardware/Pi/78-ledborg.html +++ b/hardware/Pi/78-ledborg.html @@ -27,6 +27,8 @@
See the PiBorg site for more information.
You can also now use a msg.payload in the standard hex format "#rrggbb". The clip levels are :
0x00 - 0x57 = off+
0x58 - 0xA7 = 50%
0xA8 - 0xFF = fully on
You can also use the @cheerlight colour names - red, amber, green, blue, cyan, magenta, yeloow, orange, pink, purple, + white, warmwhite, black
+ + diff --git a/social/twilio/56-twilio.js b/social/twilio/56-twilio.js index 8aca55ff..5db40726 100644 --- a/social/twilio/56-twilio.js +++ b/social/twilio/56-twilio.js @@ -17,47 +17,97 @@ var RED = require(process.env.NODE_RED_HOME+"/red/red"); var util = require('util'); - -// Either add a line like this to settings.js -// twilio: { account:'My-ACCOUNT-SID', authtoken:'TWILIO-TOKEN',from:'FROM-NUMBER' }, -// Or as a twiliokey.js file in the directory ABOVE node-red. -// module.exports = { account:'My-ACCOUNT-SID', authtoken:'TWILIO-TOKEN',from:'FROM-NUMBER' } +var twilio = require('twilio'); try { var twiliokey = RED.settings.twilio || require(process.env.NODE_RED_HOME+"/../twiliokey.js"); } catch(err) { - util.log("[56-twilio.js] Error: Failed to load Twilio credentials"); } -if (twiliokey) { - var twilioClient = require('twilio')(twiliokey.account, twiliokey.authtoken); - var fromNumber = twiliokey.from; -} +var querystring = require('querystring'); +RED.httpAdmin.get('/twilio-api/global',function(req,res) { + res.send(JSON.stringify({hasToken:!(twiliokey && twiliokey.account && twiliokey.authtoken)})); +}); +RED.httpAdmin.get('/twilio-api/:id',function(req,res) { + var credentials = RED.nodes.getCredentials(req.params.id); + if (credentials) { + res.send(JSON.stringify({hasToken:(credentials.token&&credentials.token!="")})); + } else { + res.send(JSON.stringify({})); + } +}); + +RED.httpAdmin.delete('/twilio-api/:id',function(req,res) { + RED.nodes.deleteCredentials(req.params.id); + res.send(200); +}); + +RED.httpAdmin.post('/twilio-api/: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.token == "") { + delete credentials.token; + } else { + credentials.token = newCreds.token; + } + RED.nodes.addCredentials(req.params.id,credentials); + res.send(200); + }); +}); + +function TwilioAPINode(n) { + RED.nodes.createNode(this,n); + this.sid = n.sid; + this.from = n.from; + this.name = n.name; + var credentials = RED.nodes.getCredentials(n.id); + if (credentials) { + this.token = credentials.token; + } +} +RED.nodes.registerType("twilio-api",TwilioAPINode); + + function TwilioOutNode(n) { RED.nodes.createNode(this,n); this.number = n.number; + + this.api = RED.nodes.getNode(n.twilio); + + if (this.api) { + this.twilioClient = twilio(this.api.sid,this.api.token); + this.fromNumber = this.api.from; + } else if (twiliokey) { + this.twilioClient = twilio(twiliokey.account, twiliokey.authtoken); + this.fromNumber = twiliokey.from; + } else { + this.error("missing twilio credentials"); + return; + } + var node = this; this.on("input",function(msg) { if (typeof(msg.payload) == 'object') { msg.payload = JSON.stringify(msg.payload); } - if (twiliokey) { - try { - // Send SMS - var tonum = node.number || msg.topic; - twilioClient.sendMessage( {to: tonum, from: fromNumber, body: msg.payload}, function(err, response) { - if (err) node.error(err); - //console.log(response); - }); - } - catch (err) { - node.error(err); - } - } - else { - node.warn("Twilio credentials not set/found. See node info."); + try { + // Send SMS + var tonum = node.number || msg.topic; + node.twilioClient.sendMessage( {to: tonum, from: node.fromNumber, body: msg.payload}, function(err, response) { + if (err) { + node.error(err); + } + //console.log(response); + }); + } catch (err) { + node.error(err); } }); } diff --git a/time/79-suncalc.js b/time/79-suncalc.js index 2c5e8b54..2b265391 100644 --- a/time/79-suncalc.js +++ b/time/79-suncalc.js @@ -38,7 +38,7 @@ function SunNode(n) { var mins2 = times[node.end].getUTCMinutes(); var e1 = (hour*60+mins) - (hour1*60+mins1); var e2 = (hour*60+mins) - (hour2*60+mins2); - var moon = parseInt(SunCalc.getMoonFraction(now)*100)/100; + var moon = SunCalc.getMoonIllumination(now).fraction; msg = { payload:0, topic:"sun", moon:moon }; if ((e1 > 0) & (e2 < 0)) { msg.payload = 1; } if (oldval == null) { oldval = msg.payload; }