diff --git a/coder-apps/common/eyeball/app/meta.json b/coder-apps/common/eyeball/app/meta.json
index 84a07da4..079a953d 100644
--- a/coder-apps/common/eyeball/app/meta.json
+++ b/coder-apps/common/eyeball/app/meta.json
@@ -1,6 +1,6 @@
{
"created": "2013-03-15",
- "modified": "2013-10-07",
+ "modified": "2014-01-14",
"color": "#f39c12",
"author": "Justin Windle",
"name": "Eyeball",
diff --git a/coder-apps/common/eyeball/static/css/index.css b/coder-apps/common/eyeball/static/css/index.css
index de3bf624..9f3a2a5d 100644
--- a/coder-apps/common/eyeball/static/css/index.css
+++ b/coder-apps/common/eyeball/static/css/index.css
@@ -4,7 +4,7 @@
}
html, body {
- background: #aa66dd;
+ background: #f9de2a;
text-align: center;
}
diff --git a/coder-apps/common/hello_coder/app/meta.json b/coder-apps/common/hello_coder/app/meta.json
index a849d826..418ab2d5 100644
--- a/coder-apps/common/hello_coder/app/meta.json
+++ b/coder-apps/common/hello_coder/app/meta.json
@@ -1,8 +1,8 @@
{
"created": "2013-05-08",
- "modified": "2013-07-17",
+ "modified": "2014-01-14",
"color": "#d977d4",
"author": "Jason Striegel",
"name": "Hello Coder",
"hidden": false
-}
+}
\ No newline at end of file
diff --git a/coder-apps/common/hello_coder/static/js/index.js b/coder-apps/common/hello_coder/static/js/index.js
index 88414dd6..c188f333 100644
--- a/coder-apps/common/hello_coder/static/js/index.js
+++ b/coder-apps/common/hello_coder/static/js/index.js
@@ -49,6 +49,11 @@ var initialize = function() {
$canvas.attr('width', w);
$canvas.attr('height', h);
+ // Make sure numLines isn't a crazy number
+ if ( numLines > 10000 ) {
+ numLines = 10000;
+ }
+
for ( var x=0; x < numLines; x++ ) {
// top x position
diff --git a/coder-apps/pi/auth/app/app.js b/coder-apps/pi/auth/app/app.js
index 68822f5d..e5b7847b 100644
--- a/coder-apps/pi/auth/app/app.js
+++ b/coder-apps/pi/auth/app/app.js
@@ -530,6 +530,7 @@ exports.api_logout_handler = function( req, res ) {
var saveDeviceSettings = function() {
err = fs.writeFileSync( process.cwd() + "/device.json", JSON.stringify(device_settings, null, 4), 'utf8' );
+ fs.chmodSync(process.cwd() + '/device.json', '600');
return err;
};
diff --git a/coder-apps/pi/coder/app/meta.json b/coder-apps/pi/coder/app/meta.json
index 7a2a65c2..24036299 100644
--- a/coder-apps/pi/coder/app/meta.json
+++ b/coder-apps/pi/coder/app/meta.json
@@ -1,6 +1,6 @@
{
"created": "2013-03-05",
- "modified": "2013-07-23",
+ "modified": "2014-01-07",
"color": "#bdc3c7",
"author": "Jason Striegel",
"name": "Coder",
diff --git a/coder-apps/pi/coder/static/css/index.css b/coder-apps/pi/coder/static/css/index.css
index 67b032d4..80fecd33 100644
--- a/coder-apps/pi/coder/static/css/index.css
+++ b/coder-apps/pi/coder/static/css/index.css
@@ -50,6 +50,12 @@
top: 0px;
right: 0px;
padding: 12px;
+ opacity: 0.5;
+ font-weight: bold;
+}
+.appitem .editbutton:hover {
+ opacity: 1;
+ cursor: pointer;
}
#addapp_button {
@@ -67,14 +73,14 @@
margin-left: -20px;
margin-top: -20px;
background-color: transparent;
- border-radius: 4px;
+ /*border-radius: 4px;*/
position: absolute;
background-image: url(http://github.com/static/common/media/coder_icons.png);
background-position: -167px 0px;
opacity: 0.8;
}
#addapp_button:hover #addapp_icon {
- background-color: rgba(255,255,255,0.1);
+ /*background-color: rgba(255,255,255,0.1);*/
opacity: 1;
cursor: pointer;
}
@@ -194,6 +200,10 @@
margin: 0;
border: 0;
background-color: #f00;
+ cursor: pointer;
+}
+#import_file:hover {
+ cursor: pointer;
}
@@ -226,7 +236,7 @@
}
.settingsEnabled #settings_button {
background-color: rgba( 255,255,255,.2 );
- border-color: #ffffff;
+ border-color: rgba( 255,255,255,.4 );
}
diff --git a/coder-apps/pi/coder/static/js/index.js b/coder-apps/pi/coder/static/js/index.js
index a45f043e..f2ce10e9 100644
--- a/coder-apps/pi/coder/static/js/index.js
+++ b/coder-apps/pi/coder/static/js/index.js
@@ -131,7 +131,7 @@ var handleFileImport = function( ev ) {
var importfile = files[0];
//console.log( importfile );
- if (!importfile.type.match('application/zip')) {
+ if (!importfile.type.match('application/zip') && !importfile.name.match(/\.zip$/i)) {
alert('This doesn\'t appear to be a Coder project zip file');
return false;
}
diff --git a/coder-apps/pi/wifi/static/media/.gitignore b/coder-apps/pi/wifi/static/media/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/coder-apps/tests/gpio_test/app/app.js b/coder-apps/tests/gpio_test/app/app.js
index dc6f7bd5..f5d734ad 100644
--- a/coder-apps/tests/gpio_test/app/app.js
+++ b/coder-apps/tests/gpio_test/app/app.js
@@ -1,9 +1,19 @@
var gpio = require("gpio");
gpio.logging = true;
-var gpioID = 7; //pin 26, bottom right header
-var gpioDevice;
-var connected = false; //ensure only one process talks to us at a time.
+
+// The gpio ids we're using. Note that these aren't the pin numbers, but
+// the IDs exposed by the Pi. Search for Pi GPIO pinout for details.
+var ledGPIOID = 4; //actually pin 7, 4 down on left header
+var buttonGPIOID = 17;
+
+// Handles for our connected gpio devices
+var ledDevice;
+var buttonDevice;
+
+// A collection of all connected sockets.
+// Maps socketid => { socket: sockethandle, id: socketid }
+var connections = {};
exports.settings={};
//These are dynamically updated by the runtime
@@ -15,89 +25,66 @@ exports.settings={};
//settings.coder_owner - name of the user, Ie. "Suzie Q."
//settings.coder_color - hex css color given to this coder.
+// Incoming get routes that our app knows how to respond to
exports.get_routes = [
- { path:'/', handler:'index_handler' },
+ { path:'/', handler:'index_handler' }, // Render out main html page
];
+// Incoming post routes that our app knows how to respond to
+// (None in this example)
exports.post_routes = [
];
+// Incoming socket events that this module will expose.
exports.socketio_routes = [
- { key:'connect', handler:'on_socket_connect' },
- { key:'gpio', handler:'on_socket_gpio' },
+ { key:'connect', handler:'on_socket_connect' }, // sent by client once socket is loaded
+ { key:'setled', handler:'on_socket_setled' }, // sent by client to turn on/off the led
];
-var connections = {};
+//
+// Handles sending the HTML page to the browser
+//
exports.index_handler = function( req, res ) {
+ // Set up some template variables that are substituted in our HTML.
+ // Look in the HTML head tag to see where these are inserted.
var tmplvars = {};
tmplvars['static_url'] = exports.settings.staticurl;
tmplvars['app_name'] = exports.settings.appname;
tmplvars['app_url'] = exports.settings.appurl;
tmplvars['device_name'] = exports.settings.device_name;
+ // Send the HTML document to the web browser.
res.render( exports.settings.viewpath + '/index', tmplvars );
};
-var enableGPIO = function() {
- console.log("Enabling GPIO " + gpioID );
- gpioDevice = gpio.export( gpioID, {
- ready: function() {
- //Pause briefly after pin is exported.
- //There seems to be an error if you try to immediately access it.
- setTimeout( function() {
- console.log("GPIO value: on");
- gpioDevice.setDirection("out");
- gpioDevice.set(1, function() {
- console.log("GPIO should be on");
- });
- //blinkLED();
- }, 100 );
- }
- });
-};
-var disableGPIO = function() {
- console.log("Disabling GPIO" + gpioID );
- gpioDevice.removeAllListeners();
- gpioDevice.reset();
- gpioDevice.unexport();
-};
-
-var ledval = 0;
-var blinkLED = function() {
- if ( !connected ) {
- return;
- }
-
- gpioDevice.set( ledval );
- if ( ledval == 0 ) {
- ledval = 1;
- } else {
- ledval = 0;
- }
-
- //run this method again after half a second
- setTimeout( blinkLED, 500 );
-};
-
-
-
+//
+// Respond to the "connect" message sent by a new socket client.
+//
+// We do two things here:
+// 1. save the socket object into the "connections" variable so we can talk to it later.
+// 2. initialize the GPIO pins if this is the first time a socket has connected.
+//
exports.on_socket_connect = function( socket, data ) {
console.log( 'socket connect from ID: ' + socket.socketID );
console.log( data );
- if ( !connected ) {
+ // Enable the GPIO pins if this is the first connection
+ if ( Object.keys( connections ).length <= 0 ) {
enableGPIO();
- connected = true;
}
+ // Store information about this socket so we can communicate with
+ // all connected sockets in the future.
connections[socket.socketID] = {
socket: socket,
- name: data.name,
id: socket.socketID
};
+
+ // Watch for this socket to disconnect so that we can remove it from
+ // our collection of connected sockets.
socket.on('disconnect', function() {
console.log( 'socket disconnect from ID: ' + socket.socketID );
delete connections[socket.socketID];
@@ -111,41 +98,117 @@ exports.on_socket_connect = function( socket, data ) {
};
+//
+// Respond to a "setled" message from a socket connection to update the LED value
+//
+exports.on_socket_setled = function( socket, data ) {
+ if ( data.value !== "undefined" ) {
+ setLED( data.value );
+ }
+};
+
+
+
+//
+// This is called once from our first socket connection.
+// - set up the LED GPIO as an output
+// - set up the Button GPIO as an input and tie it to send a "change" message
+//
+var enableGPIO = function() {
+
+ // Set up the LED output GPIO
+ console.log("Setting up LED as an output on GPIO " + ledGPIOID );
+ ledDevice = gpio.export( ledGPIOID, {
+ ready: function() {
+ // This works around a bug in gpio, where sometimes this device
+ // doesn't become immediately available.
+ setTimeout( function() {
+ ledDevice.setDirection("out");
+ }, 100); //wait 100 ms before setting direction
+ }
+ });
+
+ // Set up the button input GPIO
+ console.log("Setting up Button as an input on GPIO" + buttonGPIOID);
+ buttonDevice = gpio.export( buttonGPIOID, {
+ direction: "in",
+ ready: function() {
+
+ // Set up buttonDevice to call the buttonChange
+ // function (below) whenever its value changes.
+ buttonDevice.on("change", buttonChange);
+ }
+ });
+
+};
+
+//
+// This is called when the last socket disconnects.
+// It releases our GPIO pins so they can be used by another program.
+//
+var disableGPIO = function() {
+ console.log("Disabling GPIO" + ledGPIOID );
+ ledDevice.removeAllListeners();
+ ledDevice.reset();
+ ledDevice.unexport();
+
+ console.log("Disabling GPIO" + buttonGPIOID );
+ buttonDevice.removeAllListeners();
+ buttonDevice.reset();
+ buttonDevice.unexport();
+};
-exports.on_socket_gpio = function( socket, data ) {
- switch (data.command) {
- case "set":
- setGPIO( data.value );
- break;
- case "direction":
- setDirection( data.direction );
- break;
- case "value":
- sendValue( socket );
- break;
+//
+// This is triggered by the GPIO "change" event on buttonDevice. This was
+// set up inside emabledGPIO().
+//
+// The change event sends this function a value, either 0 (off) or 1 (on).
+//
+var buttonChange = function( val ) {
+ // Recall that this code is running on the device. We need to send a
+ // socket message with the button data to our javascript in the
+ // web browser. In fact, we need to send this data to every connected
+ // socket, since there may be more than one browser window looking at
+ // this page.
+
+ console.log( "buttonChange event with value: " + val );
+
+ // Iterate through all of our socket connections
+ for ( var socketid in connections ) {
+ // Get the socket object for this socket
+ var socket = connections[socketid].socket;
+
+ // The "appdata" event will be received by the Coder.socketConnection
+ // object in the front end code and sent to the appropriate listener
+ // that we've defined.
+ // The "buttonupdate" key refers to a listener we set up on the front
+ // end with the code:
+ // Coder.socketConnection.addListener( "buttonupdate", function... )
+ socket.emit( "appdata", {
+ key: "buttonupdate",
+ data: val
+ });
}
};
-var setGPIO = function( val ) {
+//
+// Set the value on the LED GPIO device, either 0 (off) or 1 (on).
+//
+var setLED = function( val ) {
val = parseInt( val );
if ( val != 0 ) {
val = 1;
}
- gpioDevice.set( val );
-};
-var setDirection = function( dir ) {
- if ( dir !== "in") {
- dir = "out";
- }
- gpioDevice.setDirection( dir );
-};
-var sendValue = function( socket ) {
- socket.emit( "apdata", {
- key: "gpiovalue",
- data: gpioDevice.value
- });
+ ledDevice.set( val );
};
+//
+// Called by Coder whenever this module is reloaded. This usually happens when
+// you save your code in the editor. This is a good place to destroy any intervals
+// or clean up any long running code or handles.
+//
exports.on_destroy = function() {
};
+
+
diff --git a/coder-apps/tests/gpio_test/app/meta.json b/coder-apps/tests/gpio_test/app/meta.json
index 916925d4..ba5193b7 100644
--- a/coder-apps/tests/gpio_test/app/meta.json
+++ b/coder-apps/tests/gpio_test/app/meta.json
@@ -1,6 +1,6 @@
{
"created": "2013-11-30",
- "modified": "2013-12-03",
+ "modified": "2014-01-14",
"color": "#2ecc71",
"author": "",
"name": "GPIO Test",
diff --git a/coder-apps/tests/gpio_test/static/css/index.css b/coder-apps/tests/gpio_test/static/css/index.css
index 408ef623..f589f589 100644
--- a/coder-apps/tests/gpio_test/static/css/index.css
+++ b/coder-apps/tests/gpio_test/static/css/index.css
@@ -1,4 +1,61 @@
.pagecontent {
padding: 24px;
+ min-width: 880px;
+}
+
+.buttons {
+ float: left;
+ width: 240px;
+}
+.diagram {
+ width: 640px;
+ float: left;
+ padding-bottom: 100px;
+}
+.clear {
+ clear: both;
+}
+
+#buttonval {
+ width: 200px;
+ height: 150px;
+ margin: 24px 0;
+ color: #fff;
+ background-color: #000000;
+ text-align: center;
+ font-weight: bold;
+ line-height: 150px;
+}
+#buttonval.on {
+ background-color: #F03050;
+}
+
+
+.button {
+ cursor: pointer;
+ width: 200px;
+ height: 150px;
+ margin: 24px 0;
+ background-color: #3498D8;
+ line-height: 150px;
+ text-align: center;
+ color: #fff;
+ font-weight: bold;
+}
+
+.button.press {
+ background-color: #2488A8;
+}
+
+
+#output {
+ position: fixed;
+ left:0;
+ right:0;
+ bottom:0;
+ height: 100px;
+ color: #fff;
+ background-color: #000;
+ overflow: hidden;
}
\ No newline at end of file
diff --git a/coder-apps/tests/gpio_test/static/js/index.js b/coder-apps/tests/gpio_test/static/js/index.js
index 8cb76a31..6c14c96b 100644
--- a/coder-apps/tests/gpio_test/static/js/index.js
+++ b/coder-apps/tests/gpio_test/static/js/index.js
@@ -1,69 +1,144 @@
+///////////////////////
+// GPIO Test
+// Sample code to interact with Raspberry Pi hardware, blink an LED,
+// and detect a physical button press.
+//
+// This part of the code runs in your web browser. It's responsible
+// for handling user input from the web browser, sending commands
+// to the Raspberry Pi, and listening for updates that the device
+// sends back.
+//
+// The code here communicates with another program that runs directly
+// on the Pi (not in your browser). That device-side code can be found
+// in the Node tab.
+////////////////////////
$(document).ready( function() {
- //This code will run after your page loads
+ // Connection can take a second. Let the user know what's happening.
+ addOutputMessage( "Connecting... see the debug console for log messages." );
+
+ // This establishes a socket connection to the Coder device. A
+ // socket conection stays open while the user is viewing this page,
+ // which allows us to send a receive data very quickly from the device
+ // instead of checking for updates multiple times a second.
+ //
+ // Coder.socketConnection.init takes a callback function that will
+ // be executed once the connection is established. Anything that
+ // requires an established connection in order to function
+ // correctly should be placed in here.
Coder.socketConnection.init(function(){
-
+
+ // Each connection gets a unique ID.
addOutputMessage( "Connected with ID: " + Coder.socketConnection.socketID );
- addOutputMessage( "Click ON or OFF to enable blinking" );
- Coder.socketConnection.sendData( 'connect', {'name':'testing'} );
-
- Coder.socketConnection.addListener( 'Received gpio value', function( d ){
- console.log("gpio value: " + d);
- });
+ // Send a "connect" message to our Node page when we first connect.
+ Coder.socketConnection.sendData( 'connect', {} );
- // Blink every 100ms if enabled is on
- // enabled is set below by a button click.
- var blinkval = 0;
- setInterval( function() {
-
- if ( blinkval ) {
- blinkval = 0;
+ // Listen for a "buttonupdate" socket message from the device
+ Coder.socketConnection.addListener( 'buttonupdate', function( d ){
+ console.log("button gpio value: " + d);
+
+ // The data we get should be an integer, 0 (off) or 1 (button pressed).
+ var val = parseInt( d );
+ if ( d === 1 ) {
+ $("#buttonval").addClass('on');
} else {
- blinkval = 1;
+ $("#buttonval").removeClass('on');
}
+ });
- if ( enabled ) {
-
- Coder.socketConnection.sendData( 'gpio', {
- command: "set",
- value: blinkval
- });
+
+ // Set an interval that will repeatedly send LED on and
+ // LED off messages to the device. This will cause the led
+ // to blink.
+ blinkEnabled = false; // it's off by default
+ setInterval( function() {
+ // Only blink the LED if the user has
+ // turned this feature on.
+ if ( blinkEnabled ) {
+ ledToggle(); // toggles the led. see below.
}
- }, 100 );
-
+ }, 100 ); // This function is repeatedly called every 100ms
- //Enable or disable the blinker
- var enabled = false;
- $("#on").click( function() {
- Coder.socketConnection.sendData( 'gpio', {
- command: "set",
- value: 1
- });
- enabled = true;
+
+ $("#blinkon").click( function() {
+ startBlink();
+ ledOn(); // start out with the light in the on state.
+ });
+ $("#blinkoff").click( function() {
+ stopBlink();
+ ledOff(); // also make sure the light is off
});
- $("#off").click( function() {
- Coder.socketConnection.sendData( 'gpio', {
- command: "set",
- value: 0
- });
- enabled = false;
+ $("#toggle").click( function() {
+ stopBlink(); // discontinue blinking in case it was running
+ ledToggle(); // flip the LED state
});
- });
+ // Treat this like a push button that turns the light off while
+ // the mouse is pressed, and turns it off immediately on release.
+ $("#push").on("mousedown", function() {
+ stopBlink();
+ ledOn();
+ }).on("mouseup", function() {
+ stopBlink();
+ ledOff();
+ });
- addOutputMessage( "Connecting... see the debug console for log messages." );
+
+ });
});
+// The "ledValue" variable lets us keep track of the LED's
+// current state. 1 is on, 0 is off.
+var ledValue = 0;
+
+// Send a "setled" message to the device with a value of 1 (on)
+var ledOn = function() {
+ ledValue = 1;
+ Coder.socketConnection.sendData( 'setled', {
+ 'value': ledValue
+ });
+};
+
+// Send a "setled" message to the device with a value of 0 (on)
+var ledOff = function() {
+ ledValue = 0;
+ Coder.socketConnection.sendData( 'setled', {
+ 'value': ledValue
+ });
+};
+
+// Switch the led state.
+// If it's on, turn it off. If it's off, turn it on.
+var ledToggle = function() {
+ if ( ledValue === 1 ) {
+ ledOff();
+ } else {
+ ledOn();
+ }
+};
+
+var blinkEnabled = false;
+var startBlink = function() {
+ blinkEnabled = true; // enables blinking in our interval timer (above)
+ $("#blinkon").hide();
+ $("#blinkoff").show();
+};
+var stopBlink = function() {
+ blinkEnabled = false; // enables blinking in our interval timer (above)
+ $("#blinkon").show();
+ $("#blinkoff").hide();
+};
+// Append a new P tag to the #output DIV
var addOutputMessage = function( text ) {
var $output = $("#output");
- $output.prepend( $("").text( text ) );
+ $output.append( $("").text( text ) );
console.log( text );
-}
+};
diff --git a/coder-apps/tests/gpio_test/static/media/wiring_diagram.jpg b/coder-apps/tests/gpio_test/static/media/wiring_diagram.jpg
new file mode 100644
index 00000000..1ad680ed
Binary files /dev/null and b/coder-apps/tests/gpio_test/static/media/wiring_diagram.jpg differ
diff --git a/coder-apps/tests/gpio_test/views/index.html b/coder-apps/tests/gpio_test/views/index.html
index a2effaa2..82ab5343 100644
--- a/coder-apps/tests/gpio_test/views/index.html
+++ b/coder-apps/tests/gpio_test/views/index.html
@@ -26,8 +26,17 @@
GPIO Test
-
ON
-
OFF
+
+
BLINK
+
STOP BLINK
+
TOGGLE
+
PUSH
+
HARDWARE PRESS
+
+
+
+
+
diff --git a/coder-apps/tests/relay_test/app/app.js b/coder-apps/tests/relay_test/app/app.js
new file mode 100644
index 00000000..2bb4ed84
--- /dev/null
+++ b/coder-apps/tests/relay_test/app/app.js
@@ -0,0 +1,249 @@
+var gpio = require("gpio");
+gpio.logging = true;
+
+
+// The gpio ids we're using. Note that these aren't the pin numbers, but
+// the IDs exposed by the Pi. Search for Pi GPIO pinout for details.
+var ledGPIOID = 4; //actually pin 7, 4 down on left header
+var buttonGPIOID = 17;
+var relayGPIOID = 18;
+
+// Handles for our connected gpio devices
+var ledDevice;
+var buttonDevice;
+var relayDevice;
+
+// A collection of all connected sockets.
+// Maps socketid => { socket: sockethandle, id: socketid }
+var connections = {};
+
+exports.settings={};
+//These are dynamically updated by the runtime
+//settings.appname - the app id (folder) where your app is installed
+//settings.viewpath - prefix to where your view html files are located
+//settings.staticurl - base url path to static assets /static/apps/appname
+//settings.appurl - base url path to this app /app/appname
+//settings.device_name - name given to this coder by the user, Ie."Billy's Coder"
+//settings.coder_owner - name of the user, Ie. "Suzie Q."
+//settings.coder_color - hex css color given to this coder.
+
+// Incoming get routes that our app knows how to respond to
+exports.get_routes = [
+ { path:'/', handler:'index_handler' }, // Render out main html page
+];
+
+// Incoming post routes that our app knows how to respond to
+// (None in this example)
+exports.post_routes = [
+];
+
+// Incoming socket events that this module will expose.
+exports.socketio_routes = [
+ { key:'connect', handler:'on_socket_connect' }, // sent by client once socket is loaded
+ { key:'setled', handler:'on_socket_setled' }, // sent by client to turn on/off the led
+ { key:'setrelay', handler:'on_socket_setrelay' }, // sent by client to turn on/off the relay
+];
+
+
+
+//
+// Handles sending the HTML page to the browser
+//
+exports.index_handler = function( req, res ) {
+ // Set up some template variables that are substituted in our HTML.
+ // Look in the HTML head tag to see where these are inserted.
+ var tmplvars = {};
+ tmplvars['static_url'] = exports.settings.staticurl;
+ tmplvars['app_name'] = exports.settings.appname;
+ tmplvars['app_url'] = exports.settings.appurl;
+ tmplvars['device_name'] = exports.settings.device_name;
+
+ // Send the HTML document to the web browser.
+ res.render( exports.settings.viewpath + '/index', tmplvars );
+};
+
+
+//
+// Respond to the "connect" message sent by a new socket client.
+//
+// We do two things here:
+// 1. save the socket object into the "connections" variable so we can talk to it later.
+// 2. initialize the GPIO pins if this is the first time a socket has connected.
+//
+exports.on_socket_connect = function( socket, data ) {
+ console.log( 'socket connect from ID: ' + socket.socketID );
+ console.log( data );
+
+ // Enable the GPIO pins if this is the first connection
+ if ( Object.keys( connections ).length <= 0 ) {
+ enableGPIO();
+ }
+
+ // Store information about this socket so we can communicate with
+ // all connected sockets in the future.
+ connections[socket.socketID] = {
+ socket: socket,
+ id: socket.socketID
+ };
+
+ // Watch for this socket to disconnect so that we can remove it from
+ // our collection of connected sockets.
+ socket.on('disconnect', function() {
+ console.log( 'socket disconnect from ID: ' + socket.socketID );
+ delete connections[socket.socketID];
+
+ //Free up the GPIO when the last socket disconnects
+ if ( Object.keys( connections ).length <= 0 ) {
+ disableGPIO();
+ connected = false;
+ }
+ });
+
+};
+
+//
+// Respond to a "setled" message from a socket connection to update the LED value
+//
+exports.on_socket_setled = function( socket, data ) {
+ if ( data.value !== "undefined" ) {
+ setLED( data.value );
+ }
+};
+
+//
+// Respond to a "setrelay" message from a socket connection to update the relay value
+//
+exports.on_socket_setrelay = function( socket, data ) {
+ if ( data.value !== "undefined" ) {
+ setRelay( data.value );
+ }
+};
+
+//
+// This is called once from our first socket connection.
+// - set up the LED GPIO as an output
+// - set up the Button GPIO as an input and tie it to send a "change" message
+//
+var enableGPIO = function() {
+
+ // Set up the LED output GPIO
+ console.log("Setting up LED as an output on GPIO " + ledGPIOID );
+ ledDevice = gpio.export( ledGPIOID, {
+ ready: function() {
+ // This works around a bug in gpio, where sometimes this device
+ // doesn't become immediately available.
+ setTimeout( function() {
+ ledDevice.setDirection("out");
+ }, 100); //wait 100 ms before setting direction
+ }
+ });
+
+ console.log("Setting up Relay as an output on GPIO " + relayGPIOID );
+ relayDevice = gpio.export( relayGPIOID, {
+ direction: "out",
+ ready: function() {
+ // This works around a bug in gpio, where sometimes this device
+ // doesn't become immediately available.
+ setTimeout( function() {
+ relayDevice.setDirection("out");
+ }, 400); //wait 100 ms before setting direction
+ }
+ });
+
+ // Set up the button input GPIO
+ console.log("Setting up Button as an input on GPIO" + buttonGPIOID);
+ buttonDevice = gpio.export( buttonGPIOID, {
+ direction: "in",
+ ready: function() {
+
+ // Set up buttonDevice to call the buttonChange
+ // function (below) whenever its value changes.
+ buttonDevice.on("change", buttonChange);
+ }
+ });
+
+};
+
+//
+// This is called when the last socket disconnects.
+// It releases our GPIO pins so they can be used by another program.
+//
+var disableGPIO = function() {
+ console.log("Disabling GPIO" + ledGPIOID );
+ ledDevice.removeAllListeners();
+ ledDevice.reset();
+ ledDevice.unexport();
+
+ console.log("Disabling GPIO" + relayGPIOID );
+ relayDevice.removeAllListeners();
+ relayDevice.reset();
+ relayDevice.unexport();
+
+ console.log("Disabling GPIO" + buttonGPIOID );
+ buttonDevice.removeAllListeners();
+ buttonDevice.reset();
+ buttonDevice.unexport();
+};
+
+//
+// This is triggered by the GPIO "change" event on buttonDevice. This was
+// set up inside emabledGPIO().
+//
+// The change event sends this function a value, either 0 (off) or 1 (on).
+//
+var buttonChange = function( val ) {
+ // Recall that this code is running on the device. We need to send a
+ // socket message with the button data to our javascript in the
+ // web browser. In fact, we need to send this data to every connected
+ // socket, since there may be more than one browser window looking at
+ // this page.
+
+ console.log( "buttonChange event with value: " + val );
+
+ // Iterate through all of our socket connections
+ for ( var socketid in connections ) {
+ // Get the socket object for this socket
+ var socket = connections[socketid].socket;
+
+ // The "appdata" event will be received by the Coder.socketConnection
+ // object in the front end code and sent to the appropriate listener
+ // that we've defined.
+ // The "buttonupdate" key refers to a listener we set up on the front
+ // end with the code:
+ // Coder.socketConnection.addListener( "buttonupdate", function... )
+ socket.emit( "appdata", {
+ key: "buttonupdate",
+ data: val
+ });
+ }
+};
+
+//
+// Set the value on the LED GPIO device, either 0 (off) or 1 (on).
+//
+var setLED = function( val ) {
+ val = parseInt( val );
+ if ( val != 0 ) {
+ val = 1;
+ }
+ ledDevice.set( val );
+};
+
+var setRelay = function( val ) {
+ val = parseInt( val );
+ if ( val != 0 ) {
+ val = 1;
+ }
+ relayDevice.set( val );
+};
+
+//
+// Called by Coder whenever this module is reloaded. This usually happens when
+// you save your code in the editor. This is a good place to destroy any intervals
+// or clean up any long running code or handles.
+//
+exports.on_destroy = function() {
+};
+
+
+
diff --git a/coder-apps/tests/relay_test/app/meta.json b/coder-apps/tests/relay_test/app/meta.json
new file mode 100644
index 00000000..6f34598f
--- /dev/null
+++ b/coder-apps/tests/relay_test/app/meta.json
@@ -0,0 +1,8 @@
+{
+ "created": "2013-11-30",
+ "modified": "2014-01-14",
+ "color": "#2ecc71",
+ "author": "",
+ "name": "Relay Test",
+ "hidden": false
+}
\ No newline at end of file
diff --git a/coder-apps/tests/relay_test/static/css/index.css b/coder-apps/tests/relay_test/static/css/index.css
new file mode 100644
index 00000000..52c0643d
--- /dev/null
+++ b/coder-apps/tests/relay_test/static/css/index.css
@@ -0,0 +1,63 @@
+
+.pagecontent {
+ padding: 24px;
+ min-width: 880px;
+}
+
+.buttons {
+ /* float: left;
+ width: 240px;
+ */
+}
+.diagram {
+ width: 640px;
+ float: left;
+ padding-bottom: 100px;
+}
+.clear {
+ clear: both;
+}
+
+#buttonval {
+ width: 200px;
+ height: 150px;
+ margin: 24px 0;
+ color: #fff;
+ background-color: #000000;
+ text-align: center;
+ font-weight: bold;
+ line-height: 150px;
+}
+#buttonval.on {
+ background-color: #F03050;
+}
+
+
+.button {
+ cursor: pointer;
+ width: 200px;
+ height: 150px;
+ margin: 24px 0;
+ background-color: #3498D8;
+ line-height: 150px;
+ text-align: center;
+ color: #fff;
+ font-weight: bold;
+ display: inline-block;
+}
+
+.button.press {
+ background-color: #2488A8;
+}
+
+
+#output {
+ position: fixed;
+ left:0;
+ right:0;
+ bottom:0;
+ height: 100px;
+ color: #fff;
+ background-color: #000;
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/coder-apps/tests/relay_test/static/js/index.js b/coder-apps/tests/relay_test/static/js/index.js
new file mode 100644
index 00000000..7837a544
--- /dev/null
+++ b/coder-apps/tests/relay_test/static/js/index.js
@@ -0,0 +1,160 @@
+///////////////////////
+// GPIO Test
+// Sample code to interact with Raspberry Pi hardware, blink an LED,
+// and detect a physical button press.
+//
+// This part of the code runs in your web browser. It's responsible
+// for handling user input from the web browser, sending commands
+// to the Raspberry Pi, and listening for updates that the device
+// sends back.
+//
+// The code here communicates with another program that runs directly
+// on the Pi (not in your browser). That device-side code can be found
+// in the Node tab.
+////////////////////////
+
+$(document).ready( function() {
+
+ // Connection can take a second. Let the user know what's happening.
+ addOutputMessage( "Connecting... see the debug console for log messages." );
+
+ // This establishes a socket connection to the Coder device. A
+ // socket conection stays open while the user is viewing this page,
+ // which allows us to send a receive data very quickly from the device
+ // instead of checking for updates multiple times a second.
+ //
+ // Coder.socketConnection.init takes a callback function that will
+ // be executed once the connection is established. Anything that
+ // requires an established connection in order to function
+ // correctly should be placed in here.
+ Coder.socketConnection.init(function(){
+
+ // Each connection gets a unique ID.
+ addOutputMessage( "Connected with ID: " + Coder.socketConnection.socketID );
+
+ // Send a "connect" message to our Node page when we first connect.
+ Coder.socketConnection.sendData( 'connect', {} );
+
+
+ // Listen for a "buttonupdate" socket message from the device
+ Coder.socketConnection.addListener( 'buttonupdate', function( d ){
+ console.log("button gpio value: " + d);
+
+ // The data we get should be an integer, 0 (off) or 1 (button pressed).
+ var val = parseInt( d );
+ if ( d === 1 ) {
+ $("#buttonval").addClass('on');
+ } else {
+ $("#buttonval").removeClass('on');
+ }
+ });
+
+
+ // Set an interval that will repeatedly send LED on and
+ // LED off messages to the device. This will cause the led
+ // to blink.
+ blinkEnabled = false; // it's off by default
+ setInterval( function() {
+ // Only blink the LED if the user has
+ // turned this feature on.
+ if ( blinkEnabled ) {
+ ledToggle(); // toggles the led. see below.
+ }
+ }, 100 ); // This function is repeatedly called every 100ms
+
+
+
+ $("#blinkon").click( function() {
+ startBlink();
+ ledOn(); // start out with the light in the on state.
+ });
+ $("#blinkoff").click( function() {
+ stopBlink();
+ ledOff(); // also make sure the light is off
+ });
+ $("#toggle").click( function() {
+ stopBlink(); // discontinue blinking in case it was running
+ ledToggle(); // flip the LED state
+ });
+
+ // Treat this like a push button that turns the light off while
+ // the mouse is pressed, and turns it off immediately on release.
+ $("#push").on("mousedown", function() {
+ stopBlink();
+ ledOn();
+ }).on("mouseup", function() {
+ stopBlink();
+ ledOff();
+ });
+
+ $("#push2").on("mousedown", function() {
+ relayOn();
+ }).on("mouseup", function() {
+ relayOff();
+ });
+ });
+
+});
+
+
+var relayOn = function() {
+ Coder.socketConnection.sendData( 'setrelay', {
+ 'value': 1
+ });
+};
+var relayOff = function() {
+ Coder.socketConnection.sendData( 'setrelay', {
+ 'value': 0
+ });
+};
+
+// The "ledValue" variable lets us keep track of the LED's
+// current state. 1 is on, 0 is off.
+var ledValue = 0;
+
+// Send a "setled" message to the device with a value of 1 (on)
+var ledOn = function() {
+ ledValue = 1;
+ Coder.socketConnection.sendData( 'setled', {
+ 'value': ledValue
+ });
+};
+
+// Send a "setled" message to the device with a value of 0 (on)
+var ledOff = function() {
+ ledValue = 0;
+ Coder.socketConnection.sendData( 'setled', {
+ 'value': ledValue
+ });
+};
+
+// Switch the led state.
+// If it's on, turn it off. If it's off, turn it on.
+var ledToggle = function() {
+ if ( ledValue === 1 ) {
+ ledOff();
+ } else {
+ ledOn();
+ }
+};
+
+var blinkEnabled = false;
+var startBlink = function() {
+ blinkEnabled = true; // enables blinking in our interval timer (above)
+ $("#blinkon").hide();
+ $("#blinkoff").show();
+};
+var stopBlink = function() {
+ blinkEnabled = false; // enables blinking in our interval timer (above)
+ $("#blinkon").show();
+ $("#blinkoff").hide();
+};
+
+
+// Append a new P tag to the #output DIV
+var addOutputMessage = function( text ) {
+ var $output = $("#output");
+ $output.append( $("").text( text ) );
+ console.log( text );
+};
+
diff --git a/coder-apps/tests/relay_test/static/media/.gitignore b/coder-apps/tests/relay_test/static/media/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/coder-apps/tests/relay_test/views/index.html b/coder-apps/tests/relay_test/views/index.html
new file mode 100644
index 00000000..e4efedbd
--- /dev/null
+++ b/coder-apps/tests/relay_test/views/index.html
@@ -0,0 +1,46 @@
+
+
+
+ Coder
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
GPIO Test
+
+
BLINK
+
STOP BLINK
+
TOGGLE
+
PUSH
+
PUSH 2
+
HARDWARE PRESS
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coder-apps/tests/spi_test/app/app.js b/coder-apps/tests/spi_test/app/app.js
new file mode 100644
index 00000000..8da16d0b
--- /dev/null
+++ b/coder-apps/tests/spi_test/app/app.js
@@ -0,0 +1,202 @@
+var SPI = require('pi-spi');
+
+
+// The SPI linux device. Either /dev/spidev0.0 or /dev/spidev0.1
+var spidev = '/dev/spidev0.0';
+
+// The handle to the device driver
+var spiDevice = null;
+
+
+
+// A collection of all connected sockets.
+// Maps socketid => { socket: sockethandle, id: socketid }
+var connections = {};
+
+exports.settings={};
+//These are dynamically updated by the runtime
+//settings.appname - the app id (folder) where your app is installed
+//settings.viewpath - prefix to where your view html files are located
+//settings.staticurl - base url path to static assets /static/apps/appname
+//settings.appurl - base url path to this app /app/appname
+//settings.device_name - name given to this coder by the user, Ie."Billy's Coder"
+//settings.coder_owner - name of the user, Ie. "Suzie Q."
+//settings.coder_color - hex css color given to this coder.
+
+// Incoming get routes that our app knows how to respond to
+exports.get_routes = [
+ { path:'/', handler:'index_handler' }, // Render out main html page
+];
+
+// Incoming post routes that our app knows how to respond to
+// (None in this example)
+exports.post_routes = [
+];
+
+// Incoming socket events that this module will expose.
+exports.socketio_routes = [
+ { key:'connect', handler:'on_socket_connect' }, // sent by client once socket is loaded
+];
+
+
+
+//
+// Handles sending the HTML page to the browser
+//
+exports.index_handler = function( req, res ) {
+ // Set up some template variables that are substituted in our HTML.
+ // Look in the HTML head tag to see where these are inserted.
+ var tmplvars = {};
+ tmplvars['static_url'] = exports.settings.staticurl;
+ tmplvars['app_name'] = exports.settings.appname;
+ tmplvars['app_url'] = exports.settings.appurl;
+ tmplvars['device_name'] = exports.settings.device_name;
+
+ // Send the HTML document to the web browser.
+ res.render( exports.settings.viewpath + '/index', tmplvars );
+};
+
+
+//
+// Respond to the "connect" message sent by a new socket client.
+//
+// We do two things here:
+// 1. save the socket object into the "connections" variable so we can talk to it later.
+// 2. initialize the SPI device if this is the first time a socket has connected.
+//
+exports.on_socket_connect = function( socket, data ) {
+ console.log( 'socket connect from ID: ' + socket.socketID );
+ console.log( data );
+
+ // Enable the SPI device if this is the first connection
+ if ( Object.keys( connections ).length <= 0 ) {
+ enableSPI();
+ }
+
+ // Store information about this socket so we can communicate with
+ // all connected sockets in the future.
+ connections[socket.socketID] = {
+ socket: socket,
+ id: socket.socketID
+ };
+
+ // Watch for this socket to disconnect so that we can remove it from
+ // our collection of connected sockets.
+ socket.on('disconnect', function() {
+ console.log( 'socket disconnect from ID: ' + socket.socketID );
+ delete connections[socket.socketID];
+
+ //Free up the GPIO when the last socket disconnects
+ if ( Object.keys( connections ).length <= 0 ) {
+ disableSPI();
+ connected = false;
+ spiDevice = null;
+ }
+ });
+
+};
+
+
+
+
+
+//
+// This is called once from our first socket connection.
+// - set up the SPI device
+// - read input from the device and send spiupdate events
+//
+var updateInterval;
+var enableSPI = function() {
+
+ // Set up the SPI device
+ console.log("Setting up SPI on " + spidev );
+ spiDevice = SPI.initialize( spidev );
+
+
+ //Poll the device for updates once a second.
+ updateInterval = setInterval( readADCData, 1000 );
+
+
+};
+
+//
+// This is called when the last socket disconnects.
+// It releases our GPIO pins so they can be used by another program.
+//
+var disableSPI = function() {
+
+ console.log("Disabling SPI device" + spidev );
+
+ clearInterval( updateInterval );
+ spiDevice.close();
+
+};
+
+var toBin = function( d ) {
+ return ("00000000" + d.toString(2)).substr( -8 );
+};
+
+//
+// Reads channel 0 analog value from an MCP3008 ADC chip
+//
+var readADCData = function() {
+ // Recall that this code is running on the device. We need to send a
+ // socket message with the data to our javascript in the
+ // web browser. In fact, we need to send this data to every connected
+ // socket, since there may be more than one browser window looking at
+ // this page.
+
+ // The message used to tell an MCP3008 ADC chip to return the
+ // analog reading of channel 0;
+ var channel = 0;
+ var message = new Buffer([1, (8+channel)<<4, 0]);
+
+ spiDevice.transfer( message, message.length, function( error, data ) {
+ if ( error ) {
+ console.log( "read error " + error );
+ } else {
+
+ //last 2 bits of 2nd byte, shifted left 8 bits, added to all 8 bits of third byte = 10 data bits.
+ var val = ((data[1]&3) << 8) + data[2];
+ //val = data[2];
+ console.log( "raw data: " + toBin( data[0] ) + " " + toBin( data[1] ) + " " + toBin( data[2] ) );
+ //console.log( "value: " + val );
+
+ // Iterate through all of our socket connections
+ for ( var socketid in connections ) {
+ // Get the socket object for this socket
+ var socket = connections[socketid].socket;
+
+ // The "appdata" event will be received by the Coder.socketConnection
+ // object in the front end code and sent to the appropriate listener
+ // that we've defined.
+ // The "analogdata" key refers to a listener we set up on the front
+ // end with the code:
+ // Coder.socketConnection.addListener( "analogdata", function... )
+ socket.emit( "appdata", {
+ key: "analogdata",
+ data: val
+ });
+ }
+
+
+ }
+ });
+
+};
+
+
+//
+// Called by Coder whenever this module is reloaded. This usually happens when
+// you save your code in the editor. This is a good place to destroy any intervals
+// or clean up any long running code or handles.
+//
+exports.on_destroy = function() {
+ if ( spiDevice !== null ) {
+ disableSPI();
+ }
+
+};
+
+
+
diff --git a/coder-apps/tests/spi_test/app/meta.json b/coder-apps/tests/spi_test/app/meta.json
new file mode 100644
index 00000000..ab803eca
--- /dev/null
+++ b/coder-apps/tests/spi_test/app/meta.json
@@ -0,0 +1,8 @@
+{
+ "created": "2013-11-30",
+ "modified": "2014-04-02",
+ "color": "#2ecc71",
+ "author": "",
+ "name": "SPI Test",
+ "hidden": false
+}
\ No newline at end of file
diff --git a/coder-apps/tests/spi_test/static/css/index.css b/coder-apps/tests/spi_test/static/css/index.css
new file mode 100644
index 00000000..e00b9e26
--- /dev/null
+++ b/coder-apps/tests/spi_test/static/css/index.css
@@ -0,0 +1,65 @@
+
+.pagecontent {
+ padding: 24px;
+ min-width: 880px;
+}
+
+.buttons {
+ float: left;
+ width: 240px;
+}
+.diagram {
+ width: 640px;
+ float: left;
+ padding-bottom: 100px;
+}
+.clear {
+ clear: both;
+}
+
+.reading {
+ margin-top: 20px;
+}
+
+#buttonval {
+ width: 200px;
+ height: 150px;
+ margin: 24px 0;
+ color: #fff;
+ background-color: #000000;
+ text-align: center;
+ font-weight: bold;
+ line-height: 150px;
+}
+#buttonval.on {
+ background-color: #F03050;
+}
+
+
+.button {
+ cursor: pointer;
+ width: 200px;
+ height: 150px;
+ margin: 24px 0;
+ background-color: #3498D8;
+ line-height: 150px;
+ text-align: center;
+ color: #fff;
+ font-weight: bold;
+}
+
+.button.press {
+ background-color: #2488A8;
+}
+
+
+#output {
+ position: fixed;
+ left:0;
+ right:0;
+ bottom:0;
+ height: 100px;
+ color: #fff;
+ background-color: #000;
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/coder-apps/tests/spi_test/static/js/index.js b/coder-apps/tests/spi_test/static/js/index.js
new file mode 100644
index 00000000..bd125ef6
--- /dev/null
+++ b/coder-apps/tests/spi_test/static/js/index.js
@@ -0,0 +1,61 @@
+///////////////////////
+// SPI Test
+// Sample code to interact with Raspberry Pi hardware, talk to an
+// MCP3008 ADC chip and return the analog value of channel 0.
+//
+// This part of the code runs in your web browser. It's responsible
+// for handling user input from the web browser, sending commands
+// to the Raspberry Pi, and listening for updates that the device
+// sends back.
+//
+// The code here communicates with another program that runs directly
+// on the Pi (not in your browser). That device-side code can be found
+// in the Node tab.
+////////////////////////
+
+$(document).ready( function() {
+
+ // Connection can take a second. Let the user know what's happening.
+ addOutputMessage( "Connecting... see the debug console for log messages." );
+
+ // This establishes a socket connection to the Coder device. A
+ // socket conection stays open while the user is viewing this page,
+ // which allows us to send a receive data very quickly from the device
+ // instead of checking for updates multiple times a second.
+ //
+ // Coder.socketConnection.init takes a callback function that will
+ // be executed once the connection is established. Anything that
+ // requires an established connection in order to function
+ // correctly should be placed in here.
+ Coder.socketConnection.init(function(){
+
+ // Each connection gets a unique ID.
+ addOutputMessage( "Connected with ID: " + Coder.socketConnection.socketID );
+
+ // Send a "connect" message to our Node page when we first connect.
+ Coder.socketConnection.sendData( 'connect', {} );
+
+
+
+ Coder.socketConnection.addListener( 'analogdata', function( d ){
+ console.log("analog value: " + d);
+ $('#analogdata').text( d );
+ //addOutputMessage( "Analog value: " + d );
+ });
+
+
+
+ });
+
+});
+
+
+
+
+// Append a new P tag to the #output DIV
+var addOutputMessage = function( text ) {
+ var $output = $("#output");
+ $output.append( $("").text( text ) );
+ console.log( text );
+};
+
diff --git a/coder-apps/tests/spi_test/static/media/.gitignore b/coder-apps/tests/spi_test/static/media/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/coder-apps/tests/spi_test/views/index.html b/coder-apps/tests/spi_test/views/index.html
new file mode 100644
index 00000000..c3bbb41a
--- /dev/null
+++ b/coder-apps/tests/spi_test/views/index.html
@@ -0,0 +1,40 @@
+
+
+
+ Coder
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
SPI Test
+
Analog value:
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coder-base/package.json b/coder-base/package.json
index e42be37a..8292e5d2 100644
--- a/coder-base/package.json
+++ b/coder-base/package.json
@@ -1,17 +1,17 @@
{
"name": "coder-base",
- "description": "kid-friendly web programming environment for pi",
- "version": "0.0.1",
- "private": true,
- "dependencies": {
- "express": "3.1.0",
- "redis": "0.8.2",
- "mustache": "0.7.2",
- "consolidate": "0.8.0",
- "socket.io": "0.9.13",
- "express-params": "0.0.3",
- "bcrypt": "0.7.4",
- "connect": "*",
- "cookie": "*"
- }
+ "description": "A simple way to make cool web things",
+ "version": "0.0.7",
+ "private": true,
+ "dependencies": {
+ "express": "3.1.0",
+ "redis": "0.8.2",
+ "mustache": "0.7.2",
+ "consolidate": "0.8.0",
+ "socket.io": "0.9.13",
+ "express-params": "0.0.3",
+ "bcrypt-nodejs": "*",
+ "connect": "2.14.3",
+ "cookie": "0.1.1"
+ }
}
diff --git a/coder-base/static/common/media/coder_icons.png b/coder-base/static/common/media/coder_icons.png
index 11690851..77e32941 100644
Binary files a/coder-base/static/common/media/coder_icons.png and b/coder-base/static/common/media/coder_icons.png differ
diff --git a/installer/macosx/CoderSetup.py b/installer/macosx/CoderSetup.py
index c49684cc..73535888 100644
--- a/installer/macosx/CoderSetup.py
+++ b/installer/macosx/CoderSetup.py
@@ -323,7 +323,7 @@ def formatSDDevice():
pythonexe = os.path.dirname(sys.argv[0]) + "/../MacOS/python"
open( logfile, 'w' ).close()
- command = "osascript -e 'do shell script \"" + pythonexe + " -u formatsdcard.py really " + str( sdCardDev ) + " > " + logfile + " \" with administrator privileges'"
+ command = "osascript -e 'do shell script \"\\\"" + pythonexe + "\\\" -u formatsdcard.py really " + str( sdCardDev ) + " > " + logfile + " \" with administrator privileges'"
print( "SYSTEM: " + command )
#os.system( command )
diff --git a/installer/macosx/formatsdcard.py b/installer/macosx/formatsdcard.py
index 9706d2de..a0a03e36 100644
--- a/installer/macosx/formatsdcard.py
+++ b/installer/macosx/formatsdcard.py
@@ -50,7 +50,7 @@
filesize = os.path.getsize( filepath )
progresssize = 0
- command = 'dd bs=2m if=' + filepath + ' of=/dev/rdisk' + str( sdCardDev )
+ command = 'dd bs=2m if="' + filepath + '" of=/dev/rdisk' + str( sdCardDev )
print( "FORMATTING: " + command )
proc = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
diff --git a/installer/stock_raspbian/coder_bootstrap_install.sh b/installer/stock_raspbian/coder_bootstrap_install.sh
new file mode 100755
index 00000000..15042e6f
--- /dev/null
+++ b/installer/stock_raspbian/coder_bootstrap_install.sh
@@ -0,0 +1,36 @@
+
+echo "### Set up coder account."
+adduser --system --group coder
+echo ""
+
+
+echo "### Fetch the latest coder tree and install in /home/coder/coder-dist"
+su -s/bin/bash coder <<'EOF'
+cd /home/coder
+git clone https://github.com/googlecreativelab/coder.git coder-dist
+EOF
+echo ""
+
+echo "### Changing directory to raspian install scripts."
+echo "### /home/coder/coder-dist/installer/stock_raspbian/scripts"
+cd /home/coder/coder-dist/installer/stock_raspbian/scripts
+echo ""
+
+cat </etc/resolv.conf
+echo ""
+
+echo "Resetting wifi and network defaults."
+cp ../../../raspbian-addons/etc/network/interfaces /etc/network/interfaces
+cp ../../../raspbian-addons/etc/network/interfaces.reset /etc/network/interfaces.reset
+chown root:root /etc/network/interfaces
+chown root:root /etc/network/interfaces.reset
+chmod 664 /etc/network/interfaces
+chmod 664 /etc/network/interfaces.reset
+cp ../../../raspbian-addons/etc/wpa_supplicant/wpa_supplicant.conf.reset /etc/wpa_supplicant/wpa_supplicant.conf
+chown root:wpaconfig /etc/wpa_supplicant/wpa_supplicant.conf
+chmod 660 /etc/wpa_supplicant/wpa_supplicant.conf
+echo ""
+
+echo "Clearing system log files."
+rm /var/log/messages
+rm /var/log/syslog
+rm /var/log/wtmp
+touch /var/log/wtmp
+chmod 644 /var/log/wtmp
+rm /var/log/dmesg*
+rm /var/log/debug
+touch /var/log/debug
+rm /var/log/btmp
+touch /var/log/btmp
+chmod 644 /var/log/btmp
+rm /var/log/auth.log
+touch /var/log/auth.log
+chown root:adm /var/log/auth.log
+chmod 640 /var/log/auth.log
+touch /var/log/user.log
+chown root:adm /var/log/user.log
+chmod 640 /var/log/user.log
+echo ""
+
+# Reset pi password to raspberry
+echo "Choose the default pi passwd (normally this should be raspberry)"
+passwd pi
+
+echo ""
+echo "Done!"
+echo ""
+
diff --git a/installer/stock_raspbian/scripts/coder_system_setup.sh b/installer/stock_raspbian/scripts/coder_system_setup.sh
new file mode 100755
index 00000000..1477b423
--- /dev/null
+++ b/installer/stock_raspbian/scripts/coder_system_setup.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+echo "### Setting up the coder account."
+adduser --system --group coder
+echo ""
+
+
+echo "### Downloading the Coder git repo to /home/coder/coder-dist."
+su -s/bin/bash coder <<'EOF'
+cd /home/coder
+git clone https://github.com/googlecreativelab/coder.git coder-dist
+EOF
+echo ""
+
+
diff --git a/installer/stock_raspbian/scripts/grant_coder_sudo.sh b/installer/stock_raspbian/scripts/grant_coder_sudo.sh
new file mode 100755
index 00000000..84b232e5
--- /dev/null
+++ b/installer/stock_raspbian/scripts/grant_coder_sudo.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# Allows the coder user to run a limited number of scripts as the root user.
+# This is used for changing the pi password and wireless settings, and for
+# rebooting the device from the Coder UI.
+
+echo "### Granting sudo access to coder for scripts in /home/coder/coder-dist/coder-base/sudo_scripts/"
+bash -c "echo 'coder ALL= NOPASSWD: /home/coder/coder-dist/coder-base/sudo_scripts/*' >>/etc/sudoers"
+echo "### A line has been added to /etc/sudoers:"
+echo "coder ALL= NOPASSWD: /home/coder/coder-dist/coder-base/sudo_scripts/*"
+echo ""
diff --git a/installer/stock_raspbian/scripts/install_all_coder.sh b/installer/stock_raspbian/scripts/install_all_coder.sh
new file mode 100755
index 00000000..0676b86a
--- /dev/null
+++ b/installer/stock_raspbian/scripts/install_all_coder.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+cat <
+
+
+
+
+ Coder Server at %h.local
+
+
+ _http._tcp
+ 80
+
+
diff --git a/raspbian-addons/etc/hostname b/raspbian-addons/etc/hostname
new file mode 100644
index 00000000..972bf968
--- /dev/null
+++ b/raspbian-addons/etc/hostname
@@ -0,0 +1 @@
+coder
diff --git a/raspbian-addons/etc/hosts b/raspbian-addons/etc/hosts
new file mode 100644
index 00000000..c5997eaa
--- /dev/null
+++ b/raspbian-addons/etc/hosts
@@ -0,0 +1,8 @@
+127.0.0.1 localhost
+::1 localhost ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+
+127.0.1.1 coder
diff --git a/raspbian-addons/etc/init.d/generate-ssh-hostkeys b/raspbian-addons/etc/init.d/generate-ssh-hostkeys
index 04ba4368..fe8d9cc2 100755
--- a/raspbian-addons/etc/init.d/generate-ssh-hostkeys
+++ b/raspbian-addons/etc/init.d/generate-ssh-hostkeys
@@ -16,21 +16,24 @@ logger="logger -t $prog"
rsa_key="/etc/ssh/ssh_host_rsa_key"
dsa_key="/etc/ssh/ssh_host_dsa_key"
+ecdsa_key="/etc/ssh/ssh_host_ecdsa_key"
# Exit if the hostkeys already exist
-if [ -f $rsa_key -a -f $dsa_key ]; then
+if [ -f $rsa_key -a -f $dsa_key -a -f $ecdsa_key ]; then
exit
fi
# Generate the ssh host keys
[ -f $rsa_key ] || ssh-keygen -f $rsa_key -t rsa -C 'host' -N ''
[ -f $dsa_key ] || ssh-keygen -f $dsa_key -t dsa -C 'host' -N ''
+[ -f $ecdsa_key ] || ssh-keygen -f $ecdsa_key -t ecdsa -C 'host' -N ''
# Output the public keys to the console
# This allows user to get host keys securely through console log
echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" | $logger
ssh-keygen -l -f $rsa_key.pub | $logger
ssh-keygen -l -f $dsa_key.pub | $logger
+ssh-keygen -l -f $ecdsa_key.pub | $logger
echo "------END SSH HOST KEY FINGERPRINTS------" | $logger
diff --git a/raspbian-addons/etc/init.d/isc-dhcp-server b/raspbian-addons/etc/init.d/isc-dhcp-server
index 59d88e2b..290b74f1 100755
--- a/raspbian-addons/etc/init.d/isc-dhcp-server
+++ b/raspbian-addons/etc/init.d/isc-dhcp-server
@@ -8,15 +8,15 @@
# Required-Stop: $remote_fs $network $syslog
# Should-Start: $local_fs slapd $named
# Should-Stop: $local_fs slapd
-# Default-Start:
-# Default-Stop:
+# Default-Start:
+# Default-Stop:
# Short-Description: DHCP server
# Description: Dynamic Host Configuration Protocol Server
### END INIT INFO
##commented out only launching from wpa-supplicant
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
+# ORIG-Start: 2 3 4 5
+# ORIG-Stop: 0 1 6
PATH=/sbin:/bin:/usr/sbin:/usr/bin
diff --git a/raspbian-addons/etc/init.d/pull-coder-reset b/raspbian-addons/etc/init.d/pull-coder-reset
index eb398c58..09990a77 100755
--- a/raspbian-addons/etc/init.d/pull-coder-reset
+++ b/raspbian-addons/etc/init.d/pull-coder-reset
@@ -19,6 +19,8 @@ source_wpa_conf="/etc/wpa_supplicant/wpa_supplicant.conf.reset"
dest_wpa_conf="/etc/wpa_supplicant/wpa_supplicant.conf"
source_device_json="/home/coder/coder-dist/coder-base/device.json.reset"
dest_device_json="/home/coder/coder-dist/coder-base/device.json"
+source_net_interfaces="/etc/network/interfaces.reset"
+dest_net_interfaces="/etc/network/interfaces"
# copy from source to dest if source exists
if [ -f $reset_file ]; then
@@ -26,6 +28,10 @@ if [ -f $reset_file ]; then
cp $source_wpa_conf $dest_wpa_conf
chown root:wpaconfig $dest_wpa_conf
chmod 660 $dest_wpa_conf
+ echo "-----RESET NETWORK INTERFACES-----" | $logger
+ cp $source_net_interfaces $dest_net_interfaces
+ chown root:root $dest_net_interfaces
+ chmod 664 $dest_net_interfaces
echo "-----RESET DEVICE.JSON-----" | $logger
cp $source_device_json $dest_device_json
chown coder $dest_device_json
diff --git a/raspbian-addons/etc/init.d/pull-hostname b/raspbian-addons/etc/init.d/pull-hostname
index 23db23fc..8d0ee36f 100755
--- a/raspbian-addons/etc/init.d/pull-hostname
+++ b/raspbian-addons/etc/init.d/pull-hostname
@@ -14,23 +14,29 @@
prog=$(basename $0)
logger="logger -t $prog"
-source_conf="/boot/coder_settings/hostname.txt"
-dest_conf="/etc/hostname"
+hostname_conf="/boot/coder_settings/hostname.txt"
+hostname_dest_conf="/etc/hostname"
+hosts_conf="/boot/coder_settings/hosts.txt"
+hosts_dest_conf="/etc/hosts"
# copy from source to dest if source exists
-if [ -f $source_conf ]; then
- echo "-----IMPORTING WPA_SUPPLICANT.CONF FROM SD-----" | $logger
- cp $source_conf $dest_conf
- chown root:root $dest_conf
- chmod 644 $dest_conf
+if [ -f $hostname_conf ]; then
+ echo "-----IMPORTING HOSTNAME FROM SD-----" | $logger
+ cp $hostname_conf $hostname_dest_conf
+ chown root:root $hostname_dest_conf
+ chmod 644 $hostname_dest_conf
HOSTNAME="$(cat /etc/hostname)"
- hostname "$HOSTNAME"
-
- # Should we delete or re-import every time?
- # Opting to import every time.
- # rm -f $source_conf
+ hostname "$HOSTNAME"
+fi
+
+# copy from source to dest if source exists
+if [ -f $hosts_conf ]; then
+ echo "-----IMPORTING HOSTS FROM SD-----" | $logger
+ cp $hosts_conf $hosts_dest_conf
+ chown root:root $hosts_dest_conf
+ chmod 644 $hosts_dest_conf
fi
diff --git a/raspbian-addons/etc/init.d/pull-net-interfaces b/raspbian-addons/etc/init.d/pull-net-interfaces
new file mode 100755
index 00000000..b239cebf
--- /dev/null
+++ b/raspbian-addons/etc/init.d/pull-net-interfaces
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides: pull-net-interfaces
+# Required-Start:
+# Required-Stop:
+# Should-Start:
+# Should-Stop:
+# Default-Start: S
+# Default-Stop:
+# Description: import /etc/network/interfaces from boot partition if it exists
+### END INIT INFO
+
+prog=$(basename $0)
+logger="logger -t $prog"
+
+source_conf="/boot/coder_settings/net_interfaces.txt"
+dest_conf="/etc/network/interfaces"
+
+# copy from source to dest if source exists
+if [ -f $source_conf ]; then
+ echo "-----IMPORTING NET INTERFACES FROM SD-----" | $logger
+ cp $source_conf $dest_conf
+ chown root:root $dest_conf
+ chmod 664 $dest_conf
+ rm -f $source_conf
+fi
+
+
+
diff --git a/raspbian-addons/etc/modprobe.d/8192cu.conf b/raspbian-addons/etc/modprobe.d/8192cu.conf
new file mode 100644
index 00000000..bac98b93
--- /dev/null
+++ b/raspbian-addons/etc/modprobe.d/8192cu.conf
@@ -0,0 +1 @@
+options 8192cu rtw_power_mgnt=0 rtw_enusbss=0 rtw_ips_mode=1
diff --git a/raspbian-addons/etc/modprobe.d/raspi-blacklist.conf b/raspbian-addons/etc/modprobe.d/raspi-blacklist.conf
new file mode 100644
index 00000000..61c637eb
--- /dev/null
+++ b/raspbian-addons/etc/modprobe.d/raspi-blacklist.conf
@@ -0,0 +1,4 @@
+# blacklist spi and i2c by default (many users don't need them)
+
+#blacklist spi-bcm2708
+#blacklist i2c-bcm2708
diff --git a/raspbian-addons/etc/modules b/raspbian-addons/etc/modules
new file mode 100644
index 00000000..40224950
--- /dev/null
+++ b/raspbian-addons/etc/modules
@@ -0,0 +1,18 @@
+# /etc/modules: kernel modules to load at boot time.
+#
+# This file contains the names of kernel modules that should be loaded
+# at boot time, one per line. Lines beginning with "#" are ignored.
+# Parameters can be specified after the module name.
+
+# Sound
+snd-bcm2835
+
+# SPI
+spi-bcm2708
+spi-dev
+
+# I2C
+i2c-bcm2708
+i2c-dev
+
+
diff --git a/raspbian-addons/etc/network/interfaces b/raspbian-addons/etc/network/interfaces
index a0c985d3..b66eebad 100644
--- a/raspbian-addons/etc/network/interfaces
+++ b/raspbian-addons/etc/network/interfaces
@@ -1,8 +1,26 @@
auto lo
iface lo inet loopback
+
+###
+# Set up ethernet to use dhcp
+###
iface eth0 inet dhcp
+###
+# Or comment the above and uncomment these
+# lines for a static IP address
+###
+#auto eth0
+#iface eth0 inet static
+# address 192.168.1.10
+# netmask 255.255.255.0
+# network 192.168.1.0
+# broadcast 192.168.1.255
+# gateway 192.168.1.1
+#
+
+
allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
diff --git a/raspbian-addons/etc/network/interfaces.reset b/raspbian-addons/etc/network/interfaces.reset
new file mode 100644
index 00000000..b66eebad
--- /dev/null
+++ b/raspbian-addons/etc/network/interfaces.reset
@@ -0,0 +1,36 @@
+auto lo
+
+iface lo inet loopback
+
+###
+# Set up ethernet to use dhcp
+###
+iface eth0 inet dhcp
+
+###
+# Or comment the above and uncomment these
+# lines for a static IP address
+###
+#auto eth0
+#iface eth0 inet static
+# address 192.168.1.10
+# netmask 255.255.255.0
+# network 192.168.1.0
+# broadcast 192.168.1.255
+# gateway 192.168.1.1
+#
+
+
+allow-hotplug wlan0
+iface wlan0 inet manual
+wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
+iface default inet dhcp
+
+iface staticadhoc inet static
+ address 192.168.0.1
+ netmask 255.255.255.0
+ network 192.168.0.0
+ broadcast 192.168.0.255
+ post-up /etc/init.d/isc-dhcp-server start
+ pre-down /etc/init.d/isc-dhcp-server stop
+
diff --git a/raspbian-addons/etc/redis/redis.conf b/raspbian-addons/etc/redis/redis.conf
new file mode 100644
index 00000000..2d5b35a4
--- /dev/null
+++ b/raspbian-addons/etc/redis/redis.conf
@@ -0,0 +1,492 @@
+# Redis configuration file example
+
+# Note on units: when memory size is needed, it is possible to specifiy
+# it in the usual form of 1k 5GB 4M and so forth:
+#
+# 1k => 1000 bytes
+# 1kb => 1024 bytes
+# 1m => 1000000 bytes
+# 1mb => 1024*1024 bytes
+# 1g => 1000000000 bytes
+# 1gb => 1024*1024*1024 bytes
+#
+# units are case insensitive so 1GB 1Gb 1gB are all the same.
+
+# By default Redis does not run as a daemon. Use 'yes' if you need it.
+# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
+daemonize yes
+
+# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
+# default. You can specify a custom pid file location here.
+pidfile /var/run/redis/redis-server.pid
+
+# Accept connections on the specified port, default is 6379.
+# If port 0 is specified Redis will not listen on a TCP socket.
+port 6379
+
+# If you want you can bind a single interface, if the bind option is not
+# specified all the interfaces will listen for incoming connections.
+#
+bind 127.0.0.1
+
+# Specify the path for the unix socket that will be used to listen for
+# incoming connections. There is no default, so Redis will not listen
+# on a unix socket when not specified.
+#
+# unixsocket /var/run/redis/redis.sock
+# unixsocketperm 755
+
+# Close the connection after a client is idle for N seconds (0 to disable)
+timeout 0
+
+# Set server verbosity to 'debug'
+# it can be one of:
+# debug (a lot of information, useful for development/testing)
+# verbose (many rarely useful info, but not a mess like the debug level)
+# notice (moderately verbose, what you want in production probably)
+# warning (only very important / critical messages are logged)
+loglevel notice
+
+# Specify the log file name. Also 'stdout' can be used to force
+# Redis to log on the standard output. Note that if you use standard
+# output for logging but daemonize, logs will be sent to /dev/null
+logfile /var/log/redis/redis-server.log
+
+# To enable logging to the system logger, just set 'syslog-enabled' to yes,
+# and optionally update the other syslog parameters to suit your needs.
+# syslog-enabled no
+
+# Specify the syslog identity.
+# syslog-ident redis
+
+# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
+# syslog-facility local0
+
+# Set the number of databases. The default database is DB 0, you can select
+# a different one on a per-connection basis using SELECT where
+# dbid is a number between 0 and 'databases'-1
+databases 16
+
+################################ SNAPSHOTTING #################################
+#
+# Save the DB on disk:
+#
+# save
+#
+# Will save the DB if both the given number of seconds and the given
+# number of write operations against the DB occurred.
+#
+# In the example below the behaviour will be to save:
+# after 900 sec (15 min) if at least 1 key changed
+# after 300 sec (5 min) if at least 10 keys changed
+# after 60 sec if at least 10000 keys changed
+#
+# Note: you can disable saving at all commenting all the "save" lines.
+
+save 900 1
+save 300 10
+save 60 10000
+
+# Compress string objects using LZF when dump .rdb databases?
+# For default that's set to 'yes' as it's almost always a win.
+# If you want to save some CPU in the saving child set it to 'no' but
+# the dataset will likely be bigger if you have compressible values or keys.
+rdbcompression yes
+
+# The filename where to dump the DB
+dbfilename dump.rdb
+
+# The working directory.
+#
+# The DB will be written inside this directory, with the filename specified
+# above using the 'dbfilename' configuration directive.
+#
+# Also the Append Only File will be created inside this directory.
+#
+# Note that you must specify a directory here, not a file name.
+dir /var/lib/redis
+
+################################# REPLICATION #################################
+
+# Master-Slave replication. Use slaveof to make a Redis instance a copy of
+# another Redis server. Note that the configuration is local to the slave
+# so for example it is possible to configure the slave to save the DB with a
+# different interval, or to listen to another port, and so on.
+#
+# slaveof
+
+# If the master is password protected (using the "requirepass" configuration
+# directive below) it is possible to tell the slave to authenticate before
+# starting the replication synchronization process, otherwise the master will
+# refuse the slave request.
+#
+# masterauth
+
+# When a slave lost the connection with the master, or when the replication
+# is still in progress, the slave can act in two different ways:
+#
+# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will
+# still reply to client requests, possibly with out of data data, or the
+# data set may just be empty if this is the first synchronization.
+#
+# 2) if slave-serve-stale data is set to 'no' the slave will reply with
+# an error "SYNC with master in progress" to all the kind of commands
+# but to INFO and SLAVEOF.
+#
+slave-serve-stale-data yes
+
+# Slaves send PINGs to server in a predefined interval. It's possible to change
+# this interval with the repl_ping_slave_period option. The default value is 10
+# seconds.
+#
+# repl-ping-slave-period 10
+
+# The following option sets a timeout for both Bulk transfer I/O timeout and
+# master data or ping response timeout. The default value is 60 seconds.
+#
+# It is important to make sure that this value is greater than the value
+# specified for repl-ping-slave-period otherwise a timeout will be detected
+# every time there is low traffic between the master and the slave.
+#
+# repl-timeout 60
+
+################################## SECURITY ###################################
+
+# Require clients to issue AUTH before processing any other
+# commands. This might be useful in environments in which you do not trust
+# others with access to the host running redis-server.
+#
+# This should stay commented out for backward compatibility and because most
+# people do not need auth (e.g. they run their own servers).
+#
+# Warning: since Redis is pretty fast an outside user can try up to
+# 150k passwords per second against a good box. This means that you should
+# use a very strong password otherwise it will be very easy to break.
+#
+# requirepass foobared
+
+# Command renaming.
+#
+# It is possilbe to change the name of dangerous commands in a shared
+# environment. For instance the CONFIG command may be renamed into something
+# of hard to guess so that it will be still available for internal-use
+# tools but not available for general clients.
+#
+# Example:
+#
+# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
+#
+# It is also possilbe to completely kill a command renaming it into
+# an empty string:
+#
+# rename-command CONFIG ""
+
+################################### LIMITS ####################################
+
+# Set the max number of connected clients at the same time. By default there
+# is no limit, and it's up to the number of file descriptors the Redis process
+# is able to open. The special value '0' means no limits.
+# Once the limit is reached Redis will close all the new connections sending
+# an error 'max number of clients reached'.
+#
+# maxclients 128
+
+# Don't use more memory than the specified amount of bytes.
+# When the memory limit is reached Redis will try to remove keys
+# accordingly to the eviction policy selected (see maxmemmory-policy).
+#
+# If Redis can't remove keys according to the policy, or if the policy is
+# set to 'noeviction', Redis will start to reply with errors to commands
+# that would use more memory, like SET, LPUSH, and so on, and will continue
+# to reply to read-only commands like GET.
+#
+# This option is usually useful when using Redis as an LRU cache, or to set
+# an hard memory limit for an instance (using the 'noeviction' policy).
+#
+# WARNING: If you have slaves attached to an instance with maxmemory on,
+# the size of the output buffers needed to feed the slaves are subtracted
+# from the used memory count, so that network problems / resyncs will
+# not trigger a loop where keys are evicted, and in turn the output
+# buffer of slaves is full with DELs of keys evicted triggering the deletion
+# of more keys, and so forth until the database is completely emptied.
+#
+# In short... if you have slaves attached it is suggested that you set a lower
+# limit for maxmemory so that there is some free RAM on the system for slave
+# output buffers (but this is not needed if the policy is 'noeviction').
+#
+# maxmemory
+
+# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
+# is reached? You can select among five behavior:
+#
+# volatile-lru -> remove the key with an expire set using an LRU algorithm
+# allkeys-lru -> remove any key accordingly to the LRU algorithm
+# volatile-random -> remove a random key with an expire set
+# allkeys->random -> remove a random key, any key
+# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
+# noeviction -> don't expire at all, just return an error on write operations
+#
+# Note: with all the kind of policies, Redis will return an error on write
+# operations, when there are not suitable keys for eviction.
+#
+# At the date of writing this commands are: set setnx setex append
+# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
+# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
+# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
+# getset mset msetnx exec sort
+#
+# The default is:
+#
+# maxmemory-policy volatile-lru
+
+# LRU and minimal TTL algorithms are not precise algorithms but approximated
+# algorithms (in order to save memory), so you can select as well the sample
+# size to check. For instance for default Redis will check three keys and
+# pick the one that was used less recently, you can change the sample size
+# using the following configuration directive.
+#
+# maxmemory-samples 3
+
+############################## APPEND ONLY MODE ###############################
+
+# By default Redis asynchronously dumps the dataset on disk. If you can live
+# with the idea that the latest records will be lost if something like a crash
+# happens this is the preferred way to run Redis. If instead you care a lot
+# about your data and don't want to that a single record can get lost you should
+# enable the append only mode: when this mode is enabled Redis will append
+# every write operation received in the file appendonly.aof. This file will
+# be read on startup in order to rebuild the full dataset in memory.
+#
+# Note that you can have both the async dumps and the append only file if you
+# like (you have to comment the "save" statements above to disable the dumps).
+# Still if append only mode is enabled Redis will load the data from the
+# log file at startup ignoring the dump.rdb file.
+#
+# IMPORTANT: Check the BGREWRITEAOF to check how to rewrite the append
+# log file in background when it gets too big.
+
+appendonly yes
+
+# The name of the append only file (default: "appendonly.aof")
+# appendfilename appendonly.aof
+
+# The fsync() call tells the Operating System to actually write data on disk
+# instead to wait for more data in the output buffer. Some OS will really flush
+# data on disk, some other OS will just try to do it ASAP.
+#
+# Redis supports three different modes:
+#
+# no: don't fsync, just let the OS flush the data when it wants. Faster.
+# always: fsync after every write to the append only log . Slow, Safest.
+# everysec: fsync only if one second passed since the last fsync. Compromise.
+#
+# The default is "everysec" that's usually the right compromise between
+# speed and data safety. It's up to you to understand if you can relax this to
+# "no" that will will let the operating system flush the output buffer when
+# it wants, for better performances (but if you can live with the idea of
+# some data loss consider the default persistence mode that's snapshotting),
+# or on the contrary, use "always" that's very slow but a bit safer than
+# everysec.
+#
+# If unsure, use "everysec".
+
+# appendfsync always
+appendfsync everysec
+# appendfsync no
+
+# When the AOF fsync policy is set to always or everysec, and a background
+# saving process (a background save or AOF log background rewriting) is
+# performing a lot of I/O against the disk, in some Linux configurations
+# Redis may block too long on the fsync() call. Note that there is no fix for
+# this currently, as even performing fsync in a different thread will block
+# our synchronous write(2) call.
+#
+# In order to mitigate this problem it's possible to use the following option
+# that will prevent fsync() from being called in the main process while a
+# BGSAVE or BGREWRITEAOF is in progress.
+#
+# This means that while another child is saving the durability of Redis is
+# the same as "appendfsync none", that in pratical terms means that it is
+# possible to lost up to 30 seconds of log in the worst scenario (with the
+# default Linux settings).
+#
+# If you have latency problems turn this to "yes". Otherwise leave it as
+# "no" that is the safest pick from the point of view of durability.
+no-appendfsync-on-rewrite no
+
+# Automatic rewrite of the append only file.
+# Redis is able to automatically rewrite the log file implicitly calling
+# BGREWRITEAOF when the AOF log size will growth by the specified percentage.
+#
+# This is how it works: Redis remembers the size of the AOF file after the
+# latest rewrite (or if no rewrite happened since the restart, the size of
+# the AOF at startup is used).
+#
+# This base size is compared to the current size. If the current size is
+# bigger than the specified percentage, the rewrite is triggered. Also
+# you need to specify a minimal size for the AOF file to be rewritten, this
+# is useful to avoid rewriting the AOF file even if the percentage increase
+# is reached but it is still pretty small.
+#
+# Specify a precentage of zero in order to disable the automatic AOF
+# rewrite feature.
+
+auto-aof-rewrite-percentage 100
+auto-aof-rewrite-min-size 64mb
+
+################################## SLOW LOG ###################################
+
+# The Redis Slow Log is a system to log queries that exceeded a specified
+# execution time. The execution time does not include the I/O operations
+# like talking with the client, sending the reply and so forth,
+# but just the time needed to actually execute the command (this is the only
+# stage of command execution where the thread is blocked and can not serve
+# other requests in the meantime).
+#
+# You can configure the slow log with two parameters: one tells Redis
+# what is the execution time, in microseconds, to exceed in order for the
+# command to get logged, and the other parameter is the length of the
+# slow log. When a new command is logged the oldest one is removed from the
+# queue of logged commands.
+
+# The following time is expressed in microseconds, so 1000000 is equivalent
+# to one second. Note that a negative number disables the slow log, while
+# a value of zero forces the logging of every command.
+slowlog-log-slower-than 10000
+
+# There is no limit to this length. Just be aware that it will consume memory.
+# You can reclaim memory used by the slow log with SLOWLOG RESET.
+slowlog-max-len 128
+
+################################ VIRTUAL MEMORY ###############################
+
+### WARNING! Virtual Memory is deprecated in Redis 2.4
+### The use of Virtual Memory is strongly discouraged.
+
+# Virtual Memory allows Redis to work with datasets bigger than the actual
+# amount of RAM needed to hold the whole dataset in memory.
+# In order to do so very used keys are taken in memory while the other keys
+# are swapped into a swap file, similarly to what operating systems do
+# with memory pages.
+#
+# To enable VM just set 'vm-enabled' to yes, and set the following three
+# VM parameters accordingly to your needs.
+
+vm-enabled no
+# vm-enabled yes
+
+# This is the path of the Redis swap file. As you can guess, swap files
+# can't be shared by different Redis instances, so make sure to use a swap
+# file for every redis process you are running. Redis will complain if the
+# swap file is already in use.
+#
+# The best kind of storage for the Redis swap file (that's accessed at random)
+# is a Solid State Disk (SSD).
+#
+# *** WARNING *** if you are using a shared hosting the default of putting
+# the swap file under /tmp is not secure. Create a dir with access granted
+# only to Redis user and configure Redis to create the swap file there.
+vm-swap-file /var/lib/redis/redis.swap
+
+# vm-max-memory configures the VM to use at max the specified amount of
+# RAM. Everything that deos not fit will be swapped on disk *if* possible, that
+# is, if there is still enough contiguous space in the swap file.
+#
+# With vm-max-memory 0 the system will swap everything it can. Not a good
+# default, just specify the max amount of RAM you can in bytes, but it's
+# better to leave some margin. For instance specify an amount of RAM
+# that's more or less between 60 and 80% of your free RAM.
+vm-max-memory 0
+
+# Redis swap files is split into pages. An object can be saved using multiple
+# contiguous pages, but pages can't be shared between different objects.
+# So if your page is too big, small objects swapped out on disk will waste
+# a lot of space. If you page is too small, there is less space in the swap
+# file (assuming you configured the same number of total swap file pages).
+#
+# If you use a lot of small objects, use a page size of 64 or 32 bytes.
+# If you use a lot of big objects, use a bigger page size.
+# If unsure, use the default :)
+vm-page-size 32
+
+# Number of total memory pages in the swap file.
+# Given that the page table (a bitmap of free/used pages) is taken in memory,
+# every 8 pages on disk will consume 1 byte of RAM.
+#
+# The total swap size is vm-page-size * vm-pages
+#
+# With the default of 32-bytes memory pages and 134217728 pages Redis will
+# use a 4 GB swap file, that will use 16 MB of RAM for the page table.
+#
+# It's better to use the smallest acceptable value for your application,
+# but the default is large in order to work in most conditions.
+vm-pages 134217728
+
+# Max number of VM I/O threads running at the same time.
+# This threads are used to read/write data from/to swap file, since they
+# also encode and decode objects from disk to memory or the reverse, a bigger
+# number of threads can help with big objects even if they can't help with
+# I/O itself as the physical device may not be able to couple with many
+# reads/writes operations at the same time.
+#
+# The special value of 0 turn off threaded I/O and enables the blocking
+# Virtual Memory implementation.
+vm-max-threads 4
+
+############################### ADVANCED CONFIG ###############################
+
+# Hashes are encoded in a special way (much more memory efficient) when they
+# have at max a given numer of elements, and the biggest element does not
+# exceed a given threshold. You can configure this limits with the following
+# configuration directives.
+hash-max-zipmap-entries 512
+hash-max-zipmap-value 64
+
+# Similarly to hashes, small lists are also encoded in a special way in order
+# to save a lot of space. The special representation is only used when
+# you are under the following limits:
+list-max-ziplist-entries 512
+list-max-ziplist-value 64
+
+# Sets have a special encoding in just one case: when a set is composed
+# of just strings that happens to be integers in radix 10 in the range
+# of 64 bit signed integers.
+# The following configuration setting sets the limit in the size of the
+# set in order to use this special memory saving encoding.
+set-max-intset-entries 512
+
+# Similarly to hashes and lists, sorted sets are also specially encoded in
+# order to save a lot of space. This encoding is only used when the length and
+# elements of a sorted set are below the following limits:
+zset-max-ziplist-entries 128
+zset-max-ziplist-value 64
+
+# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
+# order to help rehashing the main Redis hash table (the one mapping top-level
+# keys to values). The hash table implementation redis uses (see dict.c)
+# performs a lazy rehashing: the more operation you run into an hash table
+# that is rhashing, the more rehashing "steps" are performed, so if the
+# server is idle the rehashing is never complete and some more memory is used
+# by the hash table.
+#
+# The default is to use this millisecond 10 times every second in order to
+# active rehashing the main dictionaries, freeing memory when possible.
+#
+# If unsure:
+# use "activerehashing no" if you have hard latency requirements and it is
+# not a good thing in your environment that Redis can reply form time to time
+# to queries with 2 milliseconds delay.
+#
+# use "activerehashing yes" if you don't have such hard requirements but
+# want to free memory asap when possible.
+activerehashing yes
+
+################################## INCLUDES ###################################
+
+# Include one or more other config files here. This is useful if you
+# have a standard template that goes to all redis server but also need
+# to customize a few per-server settings. Include files can include
+# other files, so use this wisely.
+#
+# include /path/to/local.conf
+# include /path/to/other.conf
diff --git a/raspbian-addons/etc/ssh/sshd_config b/raspbian-addons/etc/ssh/sshd_config
deleted file mode 100644
index 5458c7e9..00000000
--- a/raspbian-addons/etc/ssh/sshd_config
+++ /dev/null
@@ -1,87 +0,0 @@
-# Package generated configuration file
-# See the sshd_config(5) manpage for details
-
-# What ports, IPs and protocols we listen for
-Port 22
-# Use these options to restrict which interfaces/protocols sshd will bind to
-#ListenAddress ::
-#ListenAddress 0.0.0.0
-Protocol 2
-# HostKeys for protocol version 2
-HostKey /etc/ssh/ssh_host_rsa_key
-HostKey /etc/ssh/ssh_host_dsa_key
-#HostKey /etc/ssh/ssh_host_ecdsa_key
-#Privilege Separation is turned on for security
-UsePrivilegeSeparation yes
-
-# Lifetime and size of ephemeral version 1 server key
-KeyRegenerationInterval 3600
-ServerKeyBits 768
-
-# Logging
-SyslogFacility AUTH
-LogLevel INFO
-
-# Authentication:
-LoginGraceTime 120
-PermitRootLogin yes
-StrictModes yes
-
-RSAAuthentication yes
-PubkeyAuthentication yes
-#AuthorizedKeysFile %h/.ssh/authorized_keys
-
-# Don't read the user's ~/.rhosts and ~/.shosts files
-IgnoreRhosts yes
-# For this to work you will also need host keys in /etc/ssh_known_hosts
-RhostsRSAAuthentication no
-# similar for protocol version 2
-HostbasedAuthentication no
-# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
-#IgnoreUserKnownHosts yes
-
-# To enable empty passwords, change to yes (NOT RECOMMENDED)
-PermitEmptyPasswords no
-
-# Change to yes to enable challenge-response passwords (beware issues with
-# some PAM modules and threads)
-ChallengeResponseAuthentication no
-
-# Change to no to disable tunnelled clear text passwords
-#PasswordAuthentication yes
-
-# Kerberos options
-#KerberosAuthentication no
-#KerberosGetAFSToken no
-#KerberosOrLocalPasswd yes
-#KerberosTicketCleanup yes
-
-# GSSAPI options
-#GSSAPIAuthentication no
-#GSSAPICleanupCredentials yes
-
-X11Forwarding yes
-X11DisplayOffset 10
-PrintMotd no
-PrintLastLog yes
-TCPKeepAlive yes
-#UseLogin no
-
-#MaxStartups 10:30:60
-#Banner /etc/issue.net
-
-# Allow client to pass locale environment variables
-AcceptEnv LANG LC_*
-
-Subsystem sftp /usr/lib/openssh/sftp-server
-
-# Set this to 'yes' to enable PAM authentication, account processing,
-# and session processing. If this is enabled, PAM authentication will
-# be allowed through the ChallengeResponseAuthentication and
-# PasswordAuthentication. Depending on your PAM configuration,
-# PAM authentication via ChallengeResponseAuthentication may bypass
-# the setting of "PermitRootLogin without-password".
-# If you just want the PAM account and session checks to run without
-# PAM authentication, then enable this but set PasswordAuthentication
-# and ChallengeResponseAuthentication to 'no'.
-UsePAM yes
diff --git a/raspbian-addons/etc/udev/rules.d/10-gpio.rules b/raspbian-addons/etc/udev/rules.d/10-gpio.rules
new file mode 100644
index 00000000..fea8859d
--- /dev/null
+++ b/raspbian-addons/etc/udev/rules.d/10-gpio.rules
@@ -0,0 +1,5 @@
+# Give the GPIO group access to /sys/class/gpio*
+
+SUBSYSTEM=="gpio", KERNEL!="gpio[0-9]*", ACTION=="add", PROGRAM="/bin/bash -c 'chown -R root:gpio $sys/class/gpio ; chmod 220 $sys/class/gpio/{export,unexport}'"
+
+SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/bash -c 'chmod -f 755 $sys$devpath ; chmod -f 660 $sys$devpath/{active_low,direction,edge,uevent,value} ; chown -Rf root:gpio $sys/$devpath'"
diff --git a/raspbian-addons/home/coder/coder-dist/coder-base/package.json b/raspbian-addons/home/coder/coder-dist/coder-base/package.json
index 74625ac1..80ae62b8 100644
--- a/raspbian-addons/home/coder/coder-dist/coder-base/package.json
+++ b/raspbian-addons/home/coder/coder-dist/coder-base/package.json
@@ -1,18 +1,20 @@
{
- "name": "coder-base",
- "description": "kid-friendly web programming environment for pi",
- "version": "0.0.1",
- "private": true,
- "dependencies": {
- "express": "3.1.0",
- "redis": "0.8.2",
- "mustache": "0.7.2",
- "consolidate": "0.8.0",
- "socket.io": "0.9.13",
- "express-params": "0.0.3",
- "bcrypt": "0.7.4",
- "connect": "*",
- "cookie": "*",
- "gpio": "*"
- }
+ "name": "coder-base",
+ "description": "A simple way to make cool web things with Raspberry Pi",
+ "version": "0.0.7",
+ "private": true,
+ "dependencies": {
+ "express": "3.1.0",
+ "redis": "0.8.2",
+ "mustache": "0.7.2",
+ "consolidate": "0.8.0",
+ "socket.io": "0.9.13",
+ "express-params": "0.0.3",
+ "bcrypt": "0.7.4",
+ "connect": "2.14.3",
+ "cookie": "0.1.1",
+ "gpio": "git://github.com/jmstriegel/GpiO.git",
+ "i2c": "*",
+ "pi-spi": "*"
+ }
}
diff --git a/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_apscan b/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_apscan
index bc4d17cf..acc643c9 100755
--- a/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_apscan
+++ b/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_apscan
@@ -1,2 +1,2 @@
#!/bin/sh
-/sbin/wpa_cli scan
+/sbin/wpa_cli ap_scan 2
diff --git a/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_scan b/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_scan
index acc643c9..bc4d17cf 100755
--- a/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_scan
+++ b/raspbian-addons/home/coder/coder-dist/coder-base/sudo_scripts/wpa_cli_scan
@@ -1,2 +1,2 @@
#!/bin/sh
-/sbin/wpa_cli ap_scan 2
+/sbin/wpa_cli scan