├── .npmignore
├── lib
├── getSystemStatus.js
├── getSecurityImpl.js
├── getUuid.js
├── mdnsServer.js
├── subEvents.js
├── elasticSearch.js
├── sendActivity.js
├── wrapMqttMessage.js
├── getSocketId.js
├── clearCache.js
├── sendConfigActivity.js
├── getPhone.js
├── createReadStream.js
├── cacheDevice.js
├── getYo.js
├── util.js
├── createActivity.js
├── parentConnection.js
├── splunk.js
├── authDevice.js
├── updatePresence.js
├── getParentConnection.js
├── getThrottles.js
├── airbrakeErrors.js
├── subscribe.js
├── redisSplunk.js
├── sendSms.js
├── sendYo.js
├── createSocketEmitter.js
├── setupMqttClient.js
├── getLocalDevices.js
├── bindSocket.js
├── database.js
├── claimDevice.js
├── sendPushNotification.js
├── setupGatewayConfig.js
├── updateFromClient.js
├── logData.js
├── getEvents.js
├── unregister.js
├── whoAmI.js
├── redis.js
├── logEvent.js
├── getDevices.js
├── getData.js
├── updateSocketId.js
├── coapServer.js
├── simpleAuth.js
├── updateDevice.js
├── proxyListener.js
├── register.js
├── coapRouter.js
├── httpServer.js
├── sendMessage.js
├── mqttServer.js
├── setupCoapRoutes.js
├── setupHttpRoutes.js
└── socketLogic.js
├── .gitignore
├── app.yml
├── public
├── index.html
├── jsconsole.html
└── localjsconsole.html
├── docker
├── supervisor.conf
└── config.js.docker
├── app.json
├── newrelic.js
├── Dockerfile
├── .jshintrc
├── LICENSE
├── demo.html
├── .travis.yml
├── package.json
├── config.js.sample
├── config.js
├── server.js
└── readme.md
/.npmignore:
--------------------------------------------------------------------------------
1 | !./config.js
2 |
--------------------------------------------------------------------------------
/lib/getSystemStatus.js:
--------------------------------------------------------------------------------
1 | module.exports = function(callback) {
2 | var status = {'meshblu':'online'};
3 | callback(status);
4 | }
5 |
--------------------------------------------------------------------------------
/lib/getSecurityImpl.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 |
3 | var securityModuleName = config.securityImpl || './../lib/simpleAuth';
4 |
5 | module.exports = require(securityModuleName);
6 |
--------------------------------------------------------------------------------
/lib/getUuid.js:
--------------------------------------------------------------------------------
1 | module.exports = function(socket, callback) {
2 | if(socket.uuid){
3 | return callback(null, socket.uuid);
4 | }else{
5 | return callback(new Error('uuid not found for socket' + socket), null);
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Npm install files
2 | node_modules
3 | npm-debug.log
4 |
5 | # Config files
6 | config.js
7 |
8 | # Log file
9 | skynet.txt
10 |
11 | # Swap and meta data files
12 | .DS_Store
13 | *.swp
14 | *.swo
15 |
16 | #NEDB files
17 | *.db
18 |
--------------------------------------------------------------------------------
/lib/mdnsServer.js:
--------------------------------------------------------------------------------
1 | var mdns = require('mdns');
2 |
3 | var mdnsServer = function(config) {
4 | return mdns.createAdvertisement(
5 | mdns.tcp('meshblu'),
6 | parseInt(config.port, 10)
7 | );
8 | }
9 |
10 | module.exports = mdnsServer;
11 |
--------------------------------------------------------------------------------
/lib/subEvents.js:
--------------------------------------------------------------------------------
1 | var events = require('events');
2 |
3 | var subEvents = new events.EventEmitter();
4 |
5 | //theoretically possible for every connected port to simultaenously subscribe to same topic
6 | subEvents.setMaxListeners(65000);
7 |
8 | module.exports = subEvents;
9 |
--------------------------------------------------------------------------------
/lib/elasticSearch.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 | var _ = require('lodash');
3 |
4 | if(config.elasticSearch && !_.isEmpty(config.elasticSearch.hosts)){
5 | var elasticsearch = require('elasticsearch');
6 | var client = new elasticsearch.Client({
7 | hosts: config.elasticSearch.hosts
8 | });
9 | module.exports = client;
10 | }
11 |
--------------------------------------------------------------------------------
/lib/sendActivity.js:
--------------------------------------------------------------------------------
1 | var config = require('../config');
2 | var socketEmitter = require('./createSocketEmitter')();
3 |
4 |
5 | function sendActivity(data){
6 | //TODO throttle
7 | if(config.broadcastActivity && data && data.ipAddress){
8 | socketEmitter(config.uuid + '_bc', 'message', data);
9 | }
10 | }
11 |
12 | module.exports = sendActivity;
13 |
--------------------------------------------------------------------------------
/app.yml:
--------------------------------------------------------------------------------
1 | name: Meshblu
2 | image: ubuntu-14-04-x64
3 | min_size: 1gb
4 | config:
5 | #cloud-config
6 | users:
7 | - name: meshblu
8 | groups: sudo
9 | shell: /bin/bash
10 | sudo: ['ALL=(ALL) NOPASSWD:ALL']
11 | packages:
12 | - git
13 | runcmd:
14 | - cd /home/meshblu && git clone git://github.com/octoblu/meshblu.git && cd meshblu && bash build/ubuntu/14.04/provision.sh
15 |
--------------------------------------------------------------------------------
/lib/wrapMqttMessage.js:
--------------------------------------------------------------------------------
1 | function wrapMqttMessage(topic, data){
2 | try{
3 | if(topic === 'tb'){
4 | if(typeof data !== 'string'){
5 | return JSON.stringify(data);
6 | }
7 | return data;
8 | }else{
9 | return JSON.stringify({topic: topic, data: data});
10 | }
11 | }catch(ex){
12 | console.error(ex);
13 | }
14 | }
15 |
16 | module.exports = wrapMqttMessage;
17 |
--------------------------------------------------------------------------------
/lib/getSocketId.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 |
3 | var devices = require('./database').devices;
4 |
5 | module.exports = function(uuid, callback) {
6 | devices.findOne({
7 | uuid: uuid
8 | }, function(err, devicedata) {
9 | if(err || !devicedata || devicedata.length < 1) {
10 | callback({});
11 | } else {
12 | callback(devicedata.socketid);
13 | }
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/lib/clearCache.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 | var redis = require('./redis');
3 |
4 |
5 | function clearCache(name){
6 |
7 | if(name && config.redis && config.redis.host){
8 | redis.del(name, function(){
9 | });
10 | }
11 |
12 | }
13 |
14 | function noop(){}
15 |
16 | if(config.redis && config.redis.host){
17 | module.exports = clearCache;
18 | }
19 | else{
20 | module.exports = noop;
21 | }
22 |
--------------------------------------------------------------------------------
/lib/sendConfigActivity.js:
--------------------------------------------------------------------------------
1 | var socketEmitter = require('./createSocketEmitter')();
2 | var whoAmI = require('./whoAmI');
3 |
4 | function sendConfigActivity(uuid, emitter){
5 | whoAmI(uuid, true, function(device) {
6 | if (emitter){
7 | emitter('config', device, device);
8 | } else {
9 | socketEmitter(uuid, 'config', device);
10 | }
11 | });
12 | }
13 |
14 | module.exports = sendConfigActivity;
15 |
--------------------------------------------------------------------------------
/lib/getPhone.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 |
3 | var devices = require('./database').devices;
4 |
5 | module.exports = function(phone, callback) {
6 | devices.findOne({
7 | phoneNumber: phone
8 | }, function(err, devicedata) {
9 | if(err || !devicedata || devicedata.length < 1) {
10 | callback('phone number not found');
11 | } else {
12 | callback(null, devicedata);
13 | }
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/lib/createReadStream.js:
--------------------------------------------------------------------------------
1 | var Readable = require('stream').Readable;
2 |
3 | function noop(){}
4 |
5 | function createReadStream(){
6 | var rs = new Readable();
7 |
8 | rs._read = noop;
9 |
10 | rs.pushMsg = function(msg){
11 | if(typeof msg === 'object'){
12 | rs.push(JSON.stringify(msg) + ',\n');
13 | }else{
14 | rs.push(msg + ',\n');
15 | }
16 | };
17 |
18 | return rs;
19 | }
20 |
21 | module.exports = createReadStream;
22 |
--------------------------------------------------------------------------------
/lib/cacheDevice.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 | var redis = require('./redis');
3 |
4 | var _ = require('lodash');
5 |
6 | function cacheDevice(device){
7 |
8 | if(device){
9 | var cloned = _.clone(device);
10 | redis.set('DEVICE_' + device.uuid, JSON.stringify(cloned),function(){
11 | });
12 | }
13 |
14 | }
15 |
16 | function noop(){}
17 |
18 | if(config.redis){
19 | module.exports = cacheDevice;
20 | }
21 | else{
22 | module.exports = noop;
23 | }
24 |
--------------------------------------------------------------------------------
/lib/getYo.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 |
3 | var devices = require('./database').devices;
4 |
5 | module.exports = function(yoUsername, callback) {
6 | var regex = new RegExp(["^",yoUsername,"$"].join(""),"i");
7 | devices.findOne({
8 | yoUser: regex
9 | }, function(err, devicedata) {
10 | if(err || !devicedata || devicedata.length < 1) {
11 | callback('Yo user not found');
12 | } else {
13 | callback(null, devicedata);
14 | }
15 | });
16 | };
17 |
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 | var r192 = new RegExp(/^192\.168\./);
2 | var r10 = new RegExp(/^10\./);
3 |
4 | function sameLAN(fromIp, toIp){
5 | if(!toIp || !fromIp){
6 | return false;
7 | }
8 |
9 | if(toIp === fromIp){
10 | return true;
11 | }
12 | else if(r10.test(fromIp) && r10.test(toIp)){
13 | return true;
14 | }
15 | else if(r192.test(fromIp) && r192.test(toIp)){
16 | return true;
17 | }
18 |
19 | return false;
20 |
21 | }
22 |
23 | module.exports = {
24 | sameLAN : sameLAN
25 | };
26 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 | Page Redirection
10 |
11 |
12 | If you are not redirected automatically, follow the link to example
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/jsconsole.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 | Page Redirection
10 |
11 |
12 | If you are not redirected automatically, follow the link to example
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docker/supervisor.conf:
--------------------------------------------------------------------------------
1 | [program:redis]
2 | command=/usr/bin/redis-server /etc/redis/redis.conf
3 | numprocs=1
4 | autostart=true
5 | autorestart=true
6 |
7 | [program:mongodb]
8 | command=/usr/bin/mongod --config /etc/mongodb.conf
9 | numprocs=1
10 | autostart=true
11 | autorestart=true
12 |
13 | [program:node]
14 | command=/usr/bin/node /var/www/server.js --http --coap
15 | numprocs=1
16 | directory=/var/www/
17 | stdout_logfile=/dev/fd/1
18 | stdout_logfile_maxbytes=0
19 | redirect_stderr=true
20 | autostart=true
21 | autorestart=true
22 |
--------------------------------------------------------------------------------
/lib/createActivity.js:
--------------------------------------------------------------------------------
1 |
2 | function getActivity(topic, ip, device, toDevice, messageData){
3 | var data = {ipAddress: ip};
4 | if(topic){
5 | data.topic = topic;
6 | }
7 | if(device && device.type){
8 | data.type = device.type;
9 | }
10 | if(toDevice && toDevice.ipAddress){
11 | data.toIpAddress = toDevice.ipAddress;
12 | }
13 | if(toDevice && toDevice.type){
14 | data.toType = toDevice.type;
15 | }
16 | if(messageData){
17 | data.message = messageData;
18 | }
19 | return data;
20 | }
21 |
22 | module.exports = getActivity;
23 |
--------------------------------------------------------------------------------
/lib/parentConnection.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var skynetClient = require('skynet'); //skynet npm client
4 |
5 | var parentConnection = function(config){
6 | if(!config.parentConnection.uuid){
7 | return;
8 | }
9 |
10 | var conn = skynetClient.createConnection(config.parentConnection);
11 | conn.on('notReady', function(data){
12 | console.log('Failed authenitication to parent cloud', data);
13 | });
14 |
15 | conn.on('ready', function(data){
16 | console.log('UUID authenticated for parent cloud connection.', data);
17 | });
18 |
19 | return conn;
20 | };
21 |
22 | module.exports = parentConnection;
23 |
--------------------------------------------------------------------------------
/lib/splunk.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 |
3 | if(config.splunk && config.splunk.indexObj){
4 | var splunk = require('splunk-sdk');
5 | var splunkService = new splunk.Service({
6 | host: config.splunk.host,
7 | port: config.splunk.port,
8 | scheme: config.splunk.protocol,
9 | username: config.splunk.user,
10 | password: config.splunk.password
11 | });
12 | var myindexes = splunkService.indexes();
13 | myindexes.fetch(function(err, myindexes) {
14 | if(myindexes){
15 | config.splunk.indexObj = myindexes.item(config.splunk.index);
16 | }
17 | });
18 |
19 | module.exports = splunkService;
20 | }
21 |
--------------------------------------------------------------------------------
/lib/authDevice.js:
--------------------------------------------------------------------------------
1 | // var devices = require('./database').collection('devices');
2 | var config = require('./../config');
3 | var devices = require('./database').devices;
4 |
5 | module.exports = function(uuid, token, callback) {
6 |
7 | if(!uuid && !token){
8 | return callback({'authenticate': false});
9 | }
10 |
11 | devices.findOne({
12 | uuid: uuid, token: token
13 | }, function(err, devicedata) {
14 | if(err || !devicedata || devicedata.length < 1) {
15 | return callback({'authenticate': false});
16 | } else {
17 | return callback({'authenticate': true, device: devicedata});
18 | }
19 | });
20 |
21 | };
22 |
--------------------------------------------------------------------------------
/lib/updatePresence.js:
--------------------------------------------------------------------------------
1 | var bindSocket = require('./bindSocket');
2 | var config = require('./../config');
3 |
4 | var devices = require('./database').devices;
5 |
6 | module.exports = function(socket) {
7 | devices.update({
8 | socketid: socket
9 | }, {
10 | $set: {online: false}
11 | }, function(err, saved) {
12 | if(err || saved === 0 || (saved && !saved.updatedExisting)) {
13 | return;
14 | } else {
15 | //disconnect any bound sockets asynchronously
16 | try{
17 | bindSocket.disconnect(socket);
18 | } catch(e){
19 | console.error(e);
20 | }
21 |
22 | return;
23 | }
24 | });
25 |
26 | };
27 |
--------------------------------------------------------------------------------
/lib/getParentConnection.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var config = require('../config');
4 | var skynetClient = require('skynet'); //skynet npm client
5 |
6 | var parentConnection = null;
7 |
8 | if(config.parentConnection && config.parentConnection.uuid){
9 |
10 | parentConnection = skynetClient.createConnection(config.parentConnection);
11 | parentConnection.on('notReady', function(data){
12 | console.log('Failed authenitication to parent cloud', data);
13 | });
14 |
15 | parentConnection.on('ready', function(data){
16 | console.log('UUID authenticated for parent cloud connection.', data);
17 | });
18 |
19 | }
20 |
21 | module.exports = parentConnection;
22 |
--------------------------------------------------------------------------------
/lib/getThrottles.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var tokenthrottle = require("tokenthrottle");
4 | var config = require('../config');
5 |
6 | config.rateLimits = config.rateLimits || {};
7 | // rate per second
8 | var throttles = {
9 | connection : tokenthrottle({rate: config.rateLimits.connection || 3}),
10 | message : tokenthrottle({rate: config.rateLimits.message || 10}),
11 | data : tokenthrottle({rate: config.rateLimits.data || 10}),
12 | query : tokenthrottle({rate: config.rateLimits.query || 2}),
13 | whoami : tokenthrottle({rate: config.rateLimits.whoami || 10}),
14 | unthrottledIps : config.rateLimits.unthrottledIps || []
15 | };
16 |
17 | module.exports = throttles;
--------------------------------------------------------------------------------
/lib/airbrakeErrors.js:
--------------------------------------------------------------------------------
1 | var airbrake = require('airbrake').createClient(process.env.AIRBRAKE_KEY);
2 |
3 | var handleExceptions = function() {
4 | var origConsoleError = console.error;
5 | airbrake.log = console.log;
6 |
7 | console.error = function(err) {
8 | if (err instanceof Error) {
9 | origConsoleError(err.message, err.stack);
10 | airbrake.notify(err);
11 | } else {
12 | origConsoleError.apply(this, arguments);
13 | airbrake.notify({error: arguments});
14 | }
15 | }
16 |
17 | process.on("uncaughtException", function(error) {
18 | console.error(error);
19 | });
20 | }
21 |
22 | module.exports = {
23 | handleExceptions: handleExceptions
24 | };
25 |
--------------------------------------------------------------------------------
/lib/subscribe.js:
--------------------------------------------------------------------------------
1 | var moment = require('moment');
2 | var config = require('./../config');
3 |
4 | var events = require('./database').events;
5 |
6 | module.exports = function(uuid) {
7 |
8 | var newTimestamp = new Date().getTime();
9 | // var cursor = events.find({
10 | // $or: [{fromUuid: uuid}, {uuid:uuid}, { devices: {$in: [uuid, "all", "*"]}}],
11 | // timestamp: { $gt : moment(newTimestamp).toISOString() }
12 | // }, {}, {tailable:true, timeout:false});
13 |
14 | var cursor = events.find({
15 | $or: [{fromUuid: uuid}, {uuid:uuid}],
16 | timestamp: { $gt : moment(newTimestamp).toISOString() }
17 | }, {}, {tailable:true, timeout:false});
18 |
19 | return cursor;
20 |
21 | };
22 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Meshblu",
3 | "description": "Meshblu is an open source machine-to-machine instant messaging network and API. ",
4 | "website": "http://meshblu.org",
5 | "success_url": "/",
6 | "addons": ["mongohq"],
7 | "env": {
8 | "NODE_ENV": "production",
9 | "PORT": "80",
10 | "RATE_LIMITS_MESSAGE" : "10",
11 | "RATE_LIMITS_DATA" : "10",
12 | "RATE_LIMITS_CONNECTION" : "2",
13 | "RATE_LIMITS_QUERY" : "2",
14 | "RATE_LIMITS_WHOAMI" : "10",
15 | "RATE_LIMITS_UNTHROTTLED_IPS" : ""
16 | },
17 | "repository": "https://github.com/octoblu/meshblu",
18 | "keywords": ["M2M", "IoT", "Communication", "arduino", "skynet", "meshblu"]
19 | }
--------------------------------------------------------------------------------
/newrelic.js:
--------------------------------------------------------------------------------
1 | /**
2 | * New Relic agent configuration.
3 | *
4 | * See lib/config.defaults.js in the agent distribution for a more complete
5 | * description of configuration variables and their potential values.
6 | */
7 | exports.config = {
8 | /**
9 | * Array of application names.
10 | */
11 | app_name : ['Meshblu'],
12 | /**
13 | * Your New Relic license key.
14 | */
15 | license_key : 'e70989cafd562b1ba49653bcf0629fea323c8b4d',
16 | logging : {
17 | /**
18 | * Level at which to log. 'trace' is most useful to New Relic when diagnosing
19 | * issues with the agent, 'info' and higher will impose the least overhead on
20 | * production applications.
21 | */
22 | level : 'info'
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/lib/redisSplunk.js:
--------------------------------------------------------------------------------
1 | var redis = require('redis');
2 | var config = require('./../config');
3 |
4 | var redisSplunk;
5 |
6 | var RedisSplunk = function(options){
7 | var self, client;
8 |
9 | self = this;
10 | client = redis.createClient(options.port, options.host);
11 | client.auth(options.password, console.error);
12 |
13 | self.log = function(data, callback){
14 | client.rpush('splunk', JSON.stringify(data), callback);
15 | };
16 | };
17 |
18 | RedisSplunk.log = function(data, callback){
19 | if(!config.redis) {
20 | return callback();
21 | }
22 |
23 | if(!redisSplunk) {
24 | redisSplunk = new RedisSplunk(config.redis);
25 | }
26 |
27 | redisSplunk.log(data, callback);
28 | };
29 |
30 | module.exports = RedisSplunk;
31 |
--------------------------------------------------------------------------------
/lib/sendSms.js:
--------------------------------------------------------------------------------
1 | var request = require('request');
2 | var config = require('./../config');
3 |
4 | var devices = require('./database').devices;
5 |
6 | module.exports = function(uuid, message, callback) {
7 | devices.findOne({
8 | uuid: uuid
9 | }, function(err, devicedata) {
10 | if(err || !devicedata || devicedata.length < 1) {
11 | callback({});
12 | } else {
13 | request.post('https://' + devicedata.plivoAuthId + ':' + devicedata.plivoAuthToken + '@api.plivo.com/v1/Account/' + devicedata.plivoAuthId + '/Message/',
14 | {json: {'src': '17144625921', 'dst': devicedata.phoneNumber, 'text': message}}
15 | , function (error, response, body) {
16 | body.uuid = uuid
17 | callback(body);
18 | });
19 | }
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/lib/sendYo.js:
--------------------------------------------------------------------------------
1 | var request = require('request');
2 | var config = require('./../config');
3 |
4 | var devices = require('./database').devices;
5 |
6 | module.exports = function(uuid, callback) {
7 | devices.findOne({
8 | uuid: uuid
9 | }, function(err, devicedata) {
10 | if(err || !devicedata || devicedata.length < 1) {
11 | callback({});
12 | } else {
13 | request.post('http://api.justyo.co/yo/',
14 | {json: {'api_token': config.yo.token, 'username': devicedata.yoUser}}
15 | , function (error, response, body) {
16 | if (error) {
17 | console.error(error);
18 | callback();
19 | return;
20 | }
21 | body.uuid = uuid
22 | callback(body);
23 | });
24 | }
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/lib/createSocketEmitter.js:
--------------------------------------------------------------------------------
1 | var config = require('../config');
2 | var redis = require('./redis');
3 | var subEvents = require('./subEvents');
4 |
5 | var redisIoEmitter;
6 |
7 | if(config.redis && config.redis.host){
8 | redisIoEmitter = require('socket.io-emitter')(redis.client);
9 | }
10 |
11 | module.exports = function(io, ios){
12 | if(redisIoEmitter){
13 | return function(channel, topic, data){
14 | redisIoEmitter.in(channel).emit(topic, data);
15 | };
16 | }else if(io){
17 | return function(channel, topic, data){
18 | io.sockets.in(channel).emit(topic, data);
19 | if(ios){
20 | ios.sockets.in(channel).emit(topic, data);
21 | }
22 | //for local http streaming:
23 | subEvents.emit(channel, topic, data);
24 | };
25 | }else{
26 | return function(){};
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/lib/setupMqttClient.js:
--------------------------------------------------------------------------------
1 | var mqtt = require('mqtt');
2 |
3 | function setupMqttClient(skynet, config){
4 |
5 | var client;
6 |
7 | // Handle MQTT Messages
8 | try{
9 |
10 | var mqttConfig = config.mqtt || {};
11 | var mqttsettings = {
12 | keepalive: 1000, // seconds
13 | protocolId: 'MQIsdp',
14 | protocolVersion: 3,
15 | //clientId: 'skynet',
16 | username: 'skynet',
17 | password: process.env.MQTT_PASS || mqttConfig.skynetPass
18 | };
19 | var mqttPort = process.env.MQTT_PORT || mqttConfig.port || 1833;
20 | var mqttHost = process.env.MQTT_HOST || mqttConfig.host || 'localhost';
21 | client = mqtt.createClient(mqttPort, mqttHost, mqttsettings);
22 | } catch(e){
23 | console.error('no mqtt server found', e);
24 | }
25 |
26 | return client;
27 |
28 | };
29 |
30 | module.exports = setupMqttClient;
31 |
--------------------------------------------------------------------------------
/lib/getLocalDevices.js:
--------------------------------------------------------------------------------
1 | var getDevices = require('./getDevices');
2 |
3 | var r192 = new RegExp(/^192\.168\./);
4 | var r10 = new RegExp(/^10\./);
5 |
6 | module.exports = function(fromDevice, unclaimedOnly, callback){
7 | if(!fromDevice || !fromDevice.ipAddress){
8 | callback({error: {
9 | message: "No ipAddress on device",
10 | code: 404
11 | }
12 | });
13 | }
14 |
15 | var ip = fromDevice.ipAddress;
16 | var query = {};
17 |
18 | if(r192.test(ip)){
19 | query.ipAddress = r192;
20 | }
21 | else if(r10.test(ip)){
22 | query.ipAddress = r10;
23 | }
24 | else{
25 | query.ipAddress = ip;
26 | }
27 | //TODO 20-bit block 172.16.0.0 - 172.31.255.255
28 |
29 | if(unclaimedOnly){
30 | query.owner = { $exists: false };
31 | }
32 |
33 | query.type = { $ne: 'user' };
34 |
35 | getDevices(fromDevice, query, false, callback);
36 | };
37 |
--------------------------------------------------------------------------------
/docker/config.js.docker:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | databaseUrl: "mongodb://localhost:27017/skynet",
3 | port: 3000,
4 | /*tls: {
5 | sslPort: 443,
6 | cert: "/certs/server.crt",
7 | key: "/certs/server.key"
8 | },*/
9 | log: true,
10 | /*elasticSearch: {
11 | host: "localhost",
12 | port: "9200"
13 | },*/
14 | rateLimits: {
15 | message: 10, // 10 transactions per user per second
16 | data: 10, // 10 transactions per user per second
17 | connection: 2, // 2 transactions per IP per second
18 | query: 2, // 2 transactions per user per second
19 | whoami: 10 // 10 transactions per user per second
20 | },
21 | /*plivo: {
22 | authId: "abc",
23 | authToken: "123"
24 | },*/
25 | redis: {
26 | host: "localhost",
27 | port: "6379",
28 | password: "localhost"
29 | },
30 | coap: {
31 | port: 5683,
32 | host: "localhost"
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/lib/bindSocket.js:
--------------------------------------------------------------------------------
1 | var redis = require('./redis');
2 | var config = require('../config');
3 |
4 | var TIMEOUT = config.bindTimeout || 3600;
5 |
6 | function connect(client, target, callback){
7 | if(config.redis){
8 | redis.setex('SOCKET_BIND_' + client, TIMEOUT, target);
9 | redis.setex('SOCKET_BIND_' + target, TIMEOUT, client, callback);
10 | }
11 | }
12 |
13 | function disconnect(client){
14 | if(config.redis){
15 | getTarget(client, function(err, target){
16 | if(target){
17 | redis.del('SOCKET_BIND_' + client);
18 | redis.del('SOCKET_BIND_' + target);
19 | }
20 | });
21 | }
22 | }
23 |
24 |
25 |
26 | function getTarget(client, callback){
27 | if(config.redis){
28 | redis.get('SOCKET_BIND_' + client, callback);
29 | }
30 | }
31 |
32 | if(config.redis){
33 | module.exports = {
34 | connect: connect,
35 | disconnect: disconnect,
36 | getTarget: getTarget
37 | };
38 | };
39 |
--------------------------------------------------------------------------------
/lib/database.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 | var path = require('path');
3 |
4 | if(config.mongo && config.mongo.databaseUrl){
5 |
6 | var mongojs = require('mongojs');
7 | var db = mongojs(config.mongo.databaseUrl);
8 | module.exports = {
9 | devices: db.collection('devices'),
10 | events: db.collection('events'),
11 | data: db.collection('data')
12 | };
13 |
14 | } else {
15 |
16 | var Datastore = require('nedb');
17 | var devices = new Datastore({
18 | filename: path.join(__dirname, '/../devices.db'),
19 | autoload: true }
20 | );
21 | var events = new Datastore({
22 | filename: path.join(__dirname, '/../events.db'),
23 | autoload: true }
24 | );
25 | var data = new Datastore({
26 | filename: path.join(__dirname, '/../data.db'),
27 | autoload: true }
28 | );
29 |
30 | module.exports = {
31 | devices: devices,
32 | events: events,
33 | data: data
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/lib/claimDevice.js:
--------------------------------------------------------------------------------
1 | var whoAmI = require('./whoAmI');
2 | var securityImpl = require('./getSecurityImpl');
3 | var config = require('./../config');
4 |
5 | var devices = require('./database').devices;
6 |
7 | module.exports = function(fromDevice, data, callback) {
8 |
9 | if(!fromDevice || !data || !data.uuid){
10 | callback('invalid from or to device');
11 | }
12 | else{
13 | whoAmI(data.uuid, false, function(toDevice){
14 | if(toDevice.error){
15 | callback('invalid device to claim');
16 | }
17 | else{
18 | if(securityImpl.canUpdate(fromDevice, toDevice, data)){
19 | var updateFields = {owner: fromDevice.uuid};
20 | if(data.name){
21 | updateFields.name = data.name;
22 | }
23 | devices.update({uuid: data.uuid}, {$set: updateFields }, callback);
24 | }
25 | else{
26 | callback('unauthorized');
27 | }
28 | }
29 | });
30 | }
31 |
32 | };
33 |
--------------------------------------------------------------------------------
/lib/sendPushNotification.js:
--------------------------------------------------------------------------------
1 | var request = require('request');
2 | var config = require('./../config');
3 |
4 | module.exports = function(device, message, callback) {
5 |
6 | // remove quotes around message string
7 | message = message.substring(1, message.length -1);
8 | request.post("https://" + config.urbanAirship.key + ":" + config.urbanAirship.secret + "@go.urbanairship.com/api/push/",
9 | { headers: {
10 | "Content-Type": "application/json",
11 | "Accept": "application/vnd.urbanairship+json; version=3;"
12 | },
13 | json:{
14 | "audience" : {
15 | "device_token" : device.pushID
16 | },
17 | "notification" : {
18 | "alert" : message
19 | },
20 | "device_types" : "all"
21 | }
22 | }, function (error, response, body) {
23 | if (error) {
24 | console.error(error);
25 | callback();
26 | return;
27 | }
28 | callback(body);
29 | });
30 |
31 | };
32 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:12.04
2 |
3 | MAINTAINER Skynet https://skynet.im/
4 |
5 | RUN apt-get update -y --fix-missing
6 | RUN apt-get install -y python-software-properties
7 | RUN apt-get install -y build-essential
8 | RUN add-apt-repository ppa:chris-lea/node.js
9 |
10 | RUN apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
11 | RUN echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" | tee -a /etc/apt/sources.list.d/10gen.list
12 | RUN apt-get update -y --fix-missing
13 | RUN apt-get -y install redis-server apt-utils supervisor nodejs
14 | RUN apt-get -y install -o apt::architecture=amd64 mongodb-10gen
15 |
16 | RUN sed -i 's/daemonize yes/daemonize no/g' /etc/redis/redis.conf
17 |
18 | ADD . /var/www
19 | RUN cd /var/www && npm install
20 | ADD ./docker/config.js.docker /var/www/config.js
21 | ADD ./docker/supervisor.conf /etc/supervisor/conf.d/supervisor.conf
22 | RUN mkdir /var/log/skynet
23 |
24 | EXPOSE 3000 5683
25 |
26 | CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf"]
27 |
--------------------------------------------------------------------------------
/lib/setupGatewayConfig.js:
--------------------------------------------------------------------------------
1 | var whoAmI = require('./whoAmI');
2 | var securityImpl = require('./getSecurityImpl');
3 |
4 | module.exports = function(clientEmitter){
5 |
6 | function configAck(fromDevice, data){
7 | whoAmI(data.uuid, false, function(check){
8 | if(!check.error && securityImpl.canSend(fromDevice, check)){
9 | clientEmitter('messageAck', fromDevice, data);
10 | }
11 | });
12 | }
13 |
14 | function config(fromDevice, data){
15 | whoAmI(data.uuid, true, function(check){
16 | if((check.type === 'gateway' || check.type === 'hub') && check.uuid === data.uuid && check.token === data.token){
17 | data.fromUuid = fromDevice.uuid;
18 | return clientEmitter('config', check, data);
19 | } else {
20 | data.error = {
21 | "message": "Gateway not found",
22 | "code": 404
23 | };
24 |
25 | return clientEmitter('messageAck', fromDevice, data);
26 | }
27 | });
28 | }
29 |
30 | return {
31 | config: config,
32 | configAck: configAck
33 | };
34 | };
35 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "angular",
4 | "after",
5 | "afterEach",
6 | "before",
7 | "beforeEach",
8 | "chai",
9 | "describe",
10 | "expect",
11 | "iit",
12 | "it",
13 | "runs",
14 | "sinon",
15 | "spyOn",
16 | "waits",
17 | "waitsFor",
18 | "xit",
19 | "xdescribe",
20 | "define",
21 | "module",
22 | "exports",
23 | "require",
24 | "window",
25 | "process",
26 | "console",
27 | "__dirname",
28 | "Buffer"
29 | ],
30 |
31 | "asi" : false,
32 | "bitwise" : true,
33 | "boss" : false,
34 | "browser" : true,
35 | "curly" : true,
36 | "debug": false,
37 | "devel": false,
38 | "eqeqeq": true,
39 | "evil": false,
40 | "expr": true,
41 | "forin": false,
42 | "immed": true,
43 | "latedef" : false,
44 | "laxbreak": false,
45 | "multistr": true,
46 | "newcap": true,
47 | "noarg": true,
48 | "noempty": false,
49 | "nonew": true,
50 | "onevar": false,
51 | "plusplus": false,
52 | "regexp": false,
53 | "strict": false,
54 | "globalstrict": true,
55 | "sub": false,
56 | "trailing" : true,
57 | "undef": true,
58 | "unused": "vars"
59 | }
--------------------------------------------------------------------------------
/lib/updateFromClient.js:
--------------------------------------------------------------------------------
1 | var whoAmI = require('./whoAmI');
2 | var logEvent = require('./logEvent');
3 | var securityImpl = require('./getSecurityImpl');
4 | var updateDevice = require('./updateDevice');
5 |
6 | function handleUpdate(fromDevice, data, fn){
7 | fn = fn || function(){};
8 |
9 | data.uuid = data.uuid || fromDevice.uuid;
10 |
11 | whoAmI(data.uuid, true, function(check){
12 |
13 | if(check.error){
14 | return fn(check);
15 | }
16 |
17 | if((data.token && check.token === data.token) || securityImpl.canUpdate(fromDevice, check, data)){
18 | // if(securityImpl.canUpdate(fromDevice, check, data)){
19 | updateDevice(data.uuid, data, function(results){
20 | results.fromUuid = fromDevice.uuid;
21 | results.from = fromDevice;
22 | logEvent(401, results);
23 |
24 | try{
25 | fn(results);
26 | } catch (e){
27 | console.error(e);
28 | }
29 |
30 | });
31 | }else{
32 | fn({error: {message: 'unauthorized', code: 401} });
33 | }
34 | });
35 |
36 | }
37 |
38 | module.exports = handleUpdate;
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Octoblu
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/lib/logData.js:
--------------------------------------------------------------------------------
1 | var moment = require('moment');
2 | var config = require('./../config');
3 |
4 | var data = require('./database').data;
5 |
6 | module.exports = function(params, callback) {
7 |
8 | // Loop through parameters to update device sensor data
9 | var updates = {};
10 | // updates.timestamp = new Date()
11 | updates.timestamp = moment(data.timestamp).toISOString();
12 |
13 | for (var param in params) {
14 | var parsed;
15 | try {
16 | parsed = JSON.parse(params[param]);
17 | } catch (e) {
18 | parsed = params[param];
19 | }
20 | // if(parsed)
21 | updates[param] = parsed.toString();
22 | }
23 |
24 | delete updates.token;
25 |
26 | data.insert(updates, function(err, saved) {
27 | // if(err || saved === 0) {
28 | if(err) {
29 | var regdata = {
30 | "error": {
31 | "message": "Device not registered",
32 | "code": 500
33 | }
34 | };
35 | // require('./logEvent')(400, regdata);
36 | callback(regdata);
37 | } else {
38 | updates._id.toString();
39 | delete updates._id;
40 |
41 | require('./logEvent')(700, updates);
42 | callback(updates);
43 | }
44 | });
45 |
46 | };
47 |
--------------------------------------------------------------------------------
/public/localjsconsole.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
30 |
31 |
32 |
33 |
34 | Connecting to Meshblu...
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/getEvents.js:
--------------------------------------------------------------------------------
1 | var config = require('./../config');
2 |
3 | var events = require('./database').events;
4 |
5 | module.exports = function(uuid, callback) {
6 |
7 | function processResults(err, eventdata){
8 | if(err || eventdata.length < 1) {
9 |
10 | var eventResp = {
11 | "error": {
12 | "message": "Events not found",
13 | "code": 404
14 | }
15 | };
16 | // require('./logEvent')(201, eventResp);
17 | callback(eventResp);
18 |
19 | } else {
20 |
21 | // remove tokens from results object
22 | for (var i=0;i
2 |
3 |
4 |
5 |
6 |
46 |
47 |
48 | Connecting to Meshblu!
49 | Subscribing to UUID:
50 | Listening for sensor data...
51 |
52 |
53 |