Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* mLab

## Architecture
![Architecture](../images/sesame1.jpg)
![Architecture](images/sesame1.jpg)

## Jump Start

Expand Down Expand Up @@ -51,6 +51,8 @@ You can receive email and/or sms notifications about door issues. For that you n
5. As a event use one of two provided by sesame web app:
* **doorstuck** is used to notify you when door couldn't reach end position
* **maintenance** is used to notify you when door needs maintenance
* **lock** is used when the webapp sucessfully applies the software door lock as a result of the webapp's lock webhook
* **unlock** is used when the webapp sucessfully deactivates the software door lock status
6. As a action service choose anything you want. Eg you can use email service to get notifications via email or sms service to get them on your phone.
7. Create as many applets you want.
8. Go back to web app source code. Edit config.js file end enable ifttt module by setting flag **enabled** to true.
Expand Down
167 changes: 97 additions & 70 deletions api_v1/controllers/DoorOperationsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,50 @@ const DSHelper = require('./../helpers/DSHelper');
const Errors = require('../models/Errors/Errors');
const Ifttt = require('../helpers/Ifttt');
var timeout;
var locked = true;

exports.doorLock = function (req, res) {
locked=true;
console.info("Lock enabled");
Ifttt.notifyLock();
res.sendStatus(200);
};

exports.doorUnlock = function (req, res) {
locked=false;
console.info("Lock disabled");
Ifttt.notifyUnlock();
res.sendStatus(200);
};


exports.doorOperate = function (req, res) {
console.log("Received Door Operate command.");
clearTimeout(timeout);
DBHelper.insertOrUpdateConfig(DBConsts.lastDoorCommandKey, DBConsts.doorCommands.operate)
.then(() => {
return DSHelper.doorOperate(config.client_name);
})
.then( () => {
return DBHelper.addNewAction(Date.now(), DBConsts.statActions.operate);
}, (err) => {
console.error('Failed to perform "Door Operate" operation. %s', err);
if (config.lock_webhook && locked) {
console.log("Sesame is locked, ignoring Door Operate command");
res.sendStatus(406);
})
.then(() => {
console.log('Successfully performed "Door Operate" operation');
res.sendStatus(204);
})
.catch((error) => {
console.error('Failed to perform "Door Operate" operation. %s', error);
res.sendStatus(500);
});
}
else {
clearTimeout(timeout);
DBHelper.insertOrUpdateConfig(DBConsts.lastDoorCommandKey, DBConsts.doorCommands.operate)
.then(() => {
return DSHelper.doorOperate(config.client_name);
})
.then( () => {
return DBHelper.addNewAction(Date.now(), DBConsts.statActions.operate);
}, (err) => {
console.error('Failed to perform "Door Operate" operation. %s', err);
res.sendStatus(406);
})
.then(() => {
console.log('Successfully performed "Door Operate" operation');
res.sendStatus(204);
})
.catch((error) => {
console.error('Failed to perform "Door Operate" operation. %s', error);
res.sendStatus(500);
});
}
};

/**
Expand All @@ -42,61 +64,66 @@ exports.doorOperate = function (req, res) {
*
*/
exports.doorOpen = function (req, res) {

clearTimeout(timeout);

const fullUrl = req.protocol + '://' + req.get('host') + '/doors/open';
console.log("Door open command received.");
if (config.lock_webhook && locked) {
console.log("Sesame is locked, ignoring Door Open command");
res.sendStatus(406);
}
else {
clearTimeout(timeout);

const fullUrl = req.protocol + '://' + req.get('host') + '/doors/open';

DSHelper.getDoorOpenedOptoClickState(config.client_name)
.then((value) => {
if (value === false) {
console.log("Door already opened. Skipping.");
throw new Errors.AlreadyOpenedError();
}
return DSHelper.getDoorClosedOptoClickState(config.client_name);
})
.then((value) => {
if (value === true) {
console.log("Door not closed. Skipping.");
throw new Errors.InvalidDoorStateError();
}
return DBHelper.insertOrUpdateConfig(DBConsts.lastDoorCommandKey, DBConsts.doorCommands.open);
})
.then( () => {
return DSHelper.doorOperate(config.client_name);
})
.then( () => {
return DBHelper.addNewAction(Date.now(), DBConsts.statActions.open);
})
.then(() => {
return DSHelper.getDoorOpenCounterValue(config.client_name);
})
.then((counter) => {
setTimeout(doorTimeout, 10000);
Utils.sendJsonResponse(res, 200,
{
'links': [
{
'rel': 'reset',
'href': fullUrl + '/reset'
}
],
'count': counter
});
})
.catch((error) => {
if (error instanceof Errors.AlreadyOpenedError) {
res.sendStatus(405);
} else if (error instanceof Errors.InvalidDoorStateError) {
res.sendStatus(405);
}
else {
console.error('Failed to perform "Door Open" command. %s', error);
res.sendStatus(406);
}
});

DSHelper.getDoorOpenedOptoClickState(config.client_name)
.then((value) => {
if (value === false) {
console.log("Door already opened. Skipping.");
throw new Errors.AlreadyOpenedError();
}
return DSHelper.getDoorClosedOptoClickState(config.client_name);
})
.then((value) => {
if (value === true) {
console.log("Door not closed. Skipping.");
throw new Errors.InvalidDoorStateError();
}
return DBHelper.insertOrUpdateConfig(DBConsts.lastDoorCommandKey, DBConsts.doorCommands.open);
})
.then( () => {
return DSHelper.doorOperate(config.client_name);
})
.then( () => {
return DBHelper.addNewAction(Date.now(), DBConsts.statActions.open);
})
.then(() => {
return DSHelper.getDoorOpenCounterValue(config.client_name);
})
.then((counter) => {
setTimeout(doorTimeout, 10000);
Utils.sendJsonResponse(res, 200,
{
'links': [
{
'rel': 'reset',
'href': fullUrl + '/reset'
}
],
'count': counter
});
})
.catch((error) => {
if (error instanceof Errors.AlreadyOpenedError) {
res.sendStatus(405);
} else if (error instanceof Errors.InvalidDoorStateError) {
res.sendStatus(405);
}
else {
console.error('Failed to perform "Door Open" command. %s', error);
res.sendStatus(406);
}
});
}
};

/**
Expand Down
2 changes: 0 additions & 2 deletions api_v1/controllers/SesameController.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const DBConsts = require('../models/DBConsts');

const DEFAULT_PAGE_SIZE = 10;


//------------------------------------------------------------------------------
/**
* API Entry Point
Expand Down Expand Up @@ -213,7 +212,6 @@ exports.doorStatsReset = function (req, res) {
};



exports.doorLogs = function (req, res) {
const fullUrl = req.protocol + '://' + req.get('host') + '/doors/logs/';

Expand Down
35 changes: 35 additions & 0 deletions api_v1/helpers/Ifttt.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,41 @@ exports.notifyDoorStuck = function () {
);
};

exports.notifyLock = function () {
if (!ifttt || !ifttt.enabled) {
return;
}
request.post(
'https://maker.ifttt.com/trigger/lock/with/key/' + ifttt.ifttt_key,
{},
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
} else {
console.error("Unable to call IFTTT Maker channel");
}
}
);
};

exports.notifyUnlock = function () {
if (!ifttt || !ifttt.enabled) {
return;
}
request.post(
'https://maker.ifttt.com/trigger/unlock/with/key/' + ifttt.ifttt_key,
{},
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
} else {
console.error("Unable to call IFTTT Maker channel");
}
}
);
};


exports.notifyMaintenanceNeeded = function () {
if (!ifttt || !ifttt.enabled) {
return;
Expand Down
25 changes: 25 additions & 0 deletions api_v1/routes/SesameRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,31 @@ router.get('/', SesameController.entryPoint);
router.get('/doors', SesameController.doorEntryPoint);


/**
* @api {put} /doors/lock Lock Door
* @apiName DoorLock
* @apiGroup sesame
* @apiDescription applies a software lock, no door move operations will be actioned when locked
* @apiUse AuthorizationHeader
* @apiSuccess 204 NoContent
* @apiError 401 Unauthorized
* @apiError 406 Device Server issue
*/
router.put('/doors/lock', DoorOperationController.doorLock);

/**
* @api {put} /doors/unlock Unlock Door
* @apiName DoorUnlock
* @apiGroup sesame
* @apiDescription applies a software unlock, allow door move operations when unlocked
* @apiUse AuthorizationHeader
* @apiSuccess 204 NoContent
* @apiError 401 Unauthorized
* @apiError 406 Device Server issue
*/
router.put('/doors/unlock', DoorOperationController.doorUnlock);


/**
* @api {put} /doors/operate Door operate
* @apiName DoorOperate
Expand Down
11 changes: 8 additions & 3 deletions config.js.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
*
* CLIENT_NAME is the name of your sesame client connected to Device Server
*
* HOST is utl to your webapp instance, change it accordingly depending on your setup.
* HOST is url to your webapp instance, change it accordingly depending on your setup.
*
* lock_webhook enables the software lock of door operations is set to true.
*
* ifttt module will cause applets to be called if set to true and your ifttt key pasted into this config file
*
*/

Expand All @@ -24,12 +28,13 @@ module.exports = {
client_name : 'SesameDevice',
db_uri : 'mongodb://localhost/sesame',
host : '<host_url>',
lock_webhook : false,
modules: {
ifttt: {
enabled: false,
ifttt_key: '<iftt_key>'
ifttt_key: '<ifttt_key>'
}
}


};
};