N00b trying to understand simple app

28 views
Skip to first unread message

Marcio Valenzuela

unread,
Apr 18, 2017, 8:42:02 PM4/18/17
to nodejs
Im working with this article:

and in it they use this node.js app which serves as a control for light switches.  Im trying to understand this code in order to modify it for my purposes.  Here is the code and below is my understanding of it:

1. creates an array
2. reads data from the saveState.json file
3. creates a Switch object with default sw as id, off as state and switch as name and creates a toggle function where if on then off and vv. I dont understand the this.setState bit?
4. some quirk
5. gets a switch
6. writes the switch array back to json
7. configures the server api route...i was wondering how we went from folder/app.js to ip/API/switches/
8. how do i toggle the switch on and off?

require('dotenv').config();

const PythonShell = require('python-shell');
const fs = require('fs');
const express = require('express');
const bodyParser= require('body-parser');
const path = require('path')
const app = express();


// 1.Switch states held in memory
const switches = [];

// 2.Read state from saveState.json, populate switches array
var readableStream = fs.createReadStream('saveState.json');
var data = ''

readableStream.on('data', function(chunk) {
    data+=chunk;
});

readableStream.on('end', function() {
  var parsed = JSON.parse(data);

  for (i=0;i<parsed.switches.length;i++){
    switches.push(new Switch(parsed.switches[i]))
  }
});




// 3.Switch Model
// Expects an object:{
  // id:"sw" + number,
  // state: "on" or "off",
  // name: any name you want to display. Defaults to "switch"
// }

function Switch(switchValues){
  this.id = switchValues.id || "sw"
  this.state = switchValues.state || "off"
  this.name = switchValues.name || "switch"
  this.toggle = function(){
    if(this.state === "on"){
      this.setState("off")
    } 
    else{
      this.setState("on");
    }
  }
  this.setState = function(state){
    var str = state === "on" ? onString(this.id[2]) : offString(this.id[2]);
    PythonShell.run(str, function (err) {
      if (!process.env.DEV){
        if (err) throw err;
      } 
    });
    this.state = state
  }
  // Invokes setState on init to set the switch to its last recalled state.
  this.setState(this.state);
}    

// 4.needed due to a quirk with PythonShell
function onString(number){
  return './public/python/sw' + number + '_on.py'
}
function offString(number){
  return './public/python/sw' + number + '_off.py'
}




// 5.Switch Lookup
function getSwitch(string){
  return switches.filter(function(element){
    return element.id === string;
  })[0]
}

// 6.Updates saveState.json
function saveState (){
  var formattedState = {
    switches: switches
  }
  fs.writeFile('./saveState.json', JSON.stringify(formattedState) )
}


//7.Server Configuration
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static(__dirname + '/public'));

// If you have a frontend, drop it in the Public folder with an entry point of index.html
app.get('/', function(req, res){
  res.sendFile('index');
})

// Switch Routes for API
app.get('/api/switches', function(req, res){
  res.send(switches);
})

app.get('/api/switches/:id', function(req, res){
  var found = getSwitch(req.params.id);
  res.json(found);
})

app.post('/api/switches/:id', function(req, res){

// 8.For now, uses a simple password query in the url string. 
// Example: POST to localhost:8000/API/switches/sw1?password=test
  if (req.query.password === process.env.PASS){
    var foundSwitch = getSwitch(req.params.id);
    
    // Optional On / Off command. If not included, defaults to a toggle.

    if(!(req.query.command === "on" || req.query.command === "off")){
      foundSwitch.toggle();
    }
    else {
      foundSwitch.setState(req.query.command)
    }

    saveState();
    console.log("postSwitch "+JSON.stringify(foundSwitch));
    res.json(foundSwitch);
  }
  else {
    console.log("invalid password")
    res.send("try again")
  }
  
})

app.listen(process.env.PORT, function(){
 console.log('Listening on port ' + process.env.PORT);
})


The way to use it according to the author is that I post a request to that server as:


This prints out the json stored in the saveState.json file which is:

{"switches":[{"id":"sw1","state":"off","name":"Kyle's Lamp"}]}

where the python file for OFF for example is:

# Edit line 6 to match your chosen GPIO pin

import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(23, GPIO.OUT)


and for ON its just a change to GPIO.IN

Thanks!

tpx1

unread,
Apr 20, 2017, 3:18:05 PM4/20/17
to nod...@googlegroups.com
Hi,

the "function Switch" is a build plan for an object, that has a method
setState. This method will be defined in the constructor method of the
Switch, because you will use variables, which are hide from the outside,
by defining them in the constructor.

By here there is no need to do it so, because there are no such
mechanism. Thats why it is better to use the prototype object, to define
these. The benefit is, that the parser won't create multiple copies of
the same function like he will in this example. You save memory and
speed your application a little bit up.

function Switch() {
...
}

Switch.prototype.setState(state) {
...
}

Switch.prototype.toggle() {
...
}

What does the setState?

The first line of the setState is a shortcut for an if statement.

var str = state === "on" ? onString(this.id[2]) : offString(this.id[2]);

I use ES6, so I will link the syntax below:

let str;
if (state === "on") {
str = onString(this.id[2])
} else {
str = offString(this.id[2])
}

https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/let


This is a little bit confusing, because the initial string is "sw".
These are two characters. If you ask the character on the second
position, you get an undefined for the default id value.

s - 0
w - 1

A better way is to check the string in the constructor method, split
them and handle the id as an integer.

The on and off strings could be merged to one function like

function getPyString(id, mode) {
return './public/python/sw' + id + '_' + mode + '.py';
}

Back to set State. On str you have now the file to a python script,
which will be called. This is realy ugly, because if there is an error,
you will crash the server. Do not throw errors, if you're not using
Promises. The callback is called by the runtime, when the shell script
is finished and with no try/catch. That means an unhandled error.

> PythonShell.run(str, function (err) {
> if (!process.env.DEV){
> if (err) throw err;
> }
> });
> this.state = state

Ok, next. The writeFile should have a callback. Something could went
wrong and then no data will be stored.

Number 7: You have the to difference between your local file system and
the uri which will be created for your application server.

Express is a framework which handles your web application.
http://expressjs.com/
There you define routes to a functionality. If you read

/api/switches

that means that you create a route like

http://[ip or domain]/api/switches

your local file system is not affected by this.

You can toggle the lights by sending a post request to an light. This is
defined by:

app.post('/api/switches/:id', function(req, res) {

});

The req object is the object, which stores the information of your
request. http://expressjs.com/en/4x/api.html#req

And req.query means, that the get param is meaned.
Send a post request like this to switch your light on.

http://[Server:Port]/apiswitches/sw1/?password=test&command=on

I hope that clarifies the code a little bit. There are many things that
can improve the code, but I think this should be sufficient for this time.

Thomas
> --
> Job board: http://jobs.nodejs.org/
> New group rules:
> https://gist.github.com/othiym23/9886289#file-moderation-policy-md
> Old group rules:
> https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> ---
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to nodejs+un...@googlegroups.com
> <mailto:nodejs+un...@googlegroups.com>.
> To post to this group, send email to nod...@googlegroups.com
> <mailto:nod...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/nodejs/92b0b89d-9841-43a8-9ff6-b2d9e527ac5a%40googlegroups.com
> <https://groups.google.com/d/msgid/nodejs/92b0b89d-9841-43a8-9ff6-b2d9e527ac5a%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages