External REST API client to Pi Gateway

Started by hrant, October 03, 2015, 01:18:31 PM

hrant

I see that Felix has added the ability to have none moteino nodes added to the gateway.
I'm looking for the reverse, ie  externally invoking node actions.
For example what would be the easiest way to let's say trigger 'Garage Open' through external clients.
I'm not concerned about security, as these would only be open to local LAN,

Would be nice to have some REST API like access to the gateway.

Thanks
H

Felix

A REST api was not really intended for the gateway model since that would require the gateway script itself to be a sort of web server to provide the API.
Instead it's entirely a socket driven model, which allows live updates. REST is connection-less and stateless, so you have to always do push and pulls everywhere to keep things in sync. Which is the #1 reason I even developed this gateway interface where I have to do none of that, and sockets would take care of all that work of instantly getting live data to everyone who cares.

hrant

Thanks Felix for taking time to respond.
Yes yes of course I do understand the model and the need of what the gateway is and I love it.
I was not implying to change that, basically all I was looking for is to add to it, a very simple perhaps even one endpoint would suffice that would mimic the terminal functionality that is in the gateway.

Send ID and Command, receive response
https:/pi/id=20&Cmd=OPN
for example to open the Garage door

This is not to replace full gateway functionality, but rather extend it, With it for example, I can open / close my Garage door using NFC tags, or voice commands from my phone instead of having to launch a browser, navigate to the node and issue the command.
I hope you can see the usefulness of such addition.

Perhaps if you can clue me in where to start, I can give it a shot.
Thanks
H


Felix

Well since the socket is proxied through the nginx webserver, any web traffic would also have to be done the same, or have some webpage accept a HTTP request and then open a socket connection to the script which then relays it to the node. Probably more complex than I'd wish.

hrant

#4
Excellent,

Many thanks for the guidance Felix.

Anyone who needs something like this, here's what i came up.
Create in /var/www/default
api.php or
api/index.php (my preference)

and paste this code
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Moteino Gateway API</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="/images/favicon.ico" type="image/x-icon">
  <script src="https://cdn.socket.io/socket.io-1.2.1.js"></script>
  <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
</head>
<body>
  <script type="text/javascript">
    $(function(){
      function getParameterByName(name) {
        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
        return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
      }
      var id = getParameterByName('id');
      var cmd = getParameterByName('cmd');

      var socket = io.connect('<?php echo $_SERVER['HTTP_HOST']; ?>', {'connect timeout': 1000}); //limit chrome xhr-polling fall back delay

      socket.on('connect', function(){
        console.log ( 'Socket Connected' );
        if (id > 0 && id < 256 && cmd.length > 0 && cmd.length < 4) {
          console.log ( 'Sending Command' );
          socket.emit("NODEMESSAGE", {nodeId:id, action:cmd});
        } else
          console.log ( 'Invalid id or cmd' );
        socket.disconnect();
        });

      socket.on('disconnect', function () {
        console.log ( 'Socket Disconnected' );
      });

      setTimeout(function(){socket.disconnect();},5000);

     });
  </script>
</body>
</html>


To use, just pass the id and command
Example to issue a Garage Open command, where the node id of the Garage is 10
Do the following. (pi is the gateway address in my case)

https://pi/api/?id=10&cmd=opn

Thanks again
H

hrant

I didn't like the previous one as much, as it was dependent on client supporting javascript.
This one is a server side.

Add the following line at the top of gateway.js
var api = require('./api.js');


And create api.js file at the same level as gateway.js and paste this code
// ---------------------------------------------------------------------------------------------
// RESTful API with nodejs
// References:
// http://ericbrandel.com/2013/01/27/even-easier-restful-api-with-node-js-and-express-framework/
// http://www.tutorialspoint.com/nodejs/nodejs_restful_api.htm
// ---------------------------------------------------------------------------------------------
var express = require('express')
var http = require('http')
var path = require('path');

var app = express();

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.use(express.logger('dev'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
    app.use(express.errorHandler());
});

app.get('/:id', function (req, res) {
    var id = req.params.id;
    var cmd = req.query.cmd;
    if (id && cmd)
    {
        if (id > 0 && id < 256 && cmd.length > 0 && cmd.length < 4)
        {
            console.log ( 'Sending API Command' );
            var msg = {nodeId:id, action:cmd};
            sendMessageToNode(msg);
        }
    } else
        console.log ( 'Invalid id or cmd' );

    res.contentType('application/json');
    res.write(JSON.stringify(msg));
    res.end();
})

http.createServer(app).listen(app.get('port'), function(){
    console.log("Express server listening on port " + app.get('port'));
});


Note, I did not add any authentication / authorization nor did I bother with https, as I only needed to support this within a secure subnet.
But it could easily be updated just like how the gateway.js does.

To api is running on port 3000 and the format is as follows.
http://pi:3000/10?cmd=opn

where 10 is the node id
and opn is the command


Felix

hrant, nice work.
As long as this is on a LAN and port 3000 is not exposed/forwarded from the outside you are golden.