Skip to content

Instantly share code, notes, and snippets.

@jinman
Created September 15, 2016 23:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jinman/1dfae868bb18e950b0c031c80158e942 to your computer and use it in GitHub Desktop.
Save jinman/1dfae868bb18e950b0c031c80158e942 to your computer and use it in GitHub Desktop.
LambdaToNannyCheckinCheckout
/**
* This is a sample Lambda function that records nanny check in/out activities
* in DynamoDB and sends SMS messages to the phone number on file.
*
* This function creates a DynamoDB table 'button_nanny_checkin' the first time
* it runs. The table uses button's DSN as hash key and timestamp as range key.
* Each entry has an activity message.
*
* If phone number is set, an SMS message about this check in/out activity will
* be sent.
*
* Permissions required for this function to run are the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1472255738000",
"Effect": "Allow",
"Action": [
"dynamodb:CreateTable",
"dynamodb:DescribeTable",
"dynamodb:PutItem",
"dynamodb:Query"
],
"Resource": [
"arn:aws:dynamodb:*:*:table/button_nanny_checkin"
]
},
{
"Sid": "Stmt1472255738001",
"Effect": "Allow",
"Action": [
"sns:Publish"
],
"Resource": [
"*"
]
}
]
}
*/
const AWS = require('aws-sdk');
const SNS = new AWS.SNS({ apiVersion: '2010-03-31' });
const dynamodb = new AWS.DynamoDB();
const TABLE_NAME = "button_nanny_checkin"; // DynamoDB table name
const PHONE_NUMBER = null; // config your phone number for SMS or null
function getButtonAttributes(event, callback) {
Iot.describeThing({ thingName: 'IotButton_' + event.serialNumber }, callback);
}
function sendSMS(event, phoneNumber, callback) {
if (phoneNumber !== null) {
console.log(`Sending SMS to ${phoneNumber}`);
const params = {
PhoneNumber: phoneNumber,
Message: getMessage(event)
};
// result will go to function callback
SNS.publish(params, callback);
} else {
callback(null);
}
}
/**
* Checks whether the table exists and is active by calling DescribeTable.
*/
function createTableIfNeeded(tableName, callback) {
dynamodb.describeTable({ TableName: tableName }, (err, data) => {
if (err) {
if (err.code === 'ResourceNotFoundException') {
createTable(tableName, (err, data) => {
if (err) {
callback(err);
} else {
callback('Table has been created but is not ready.');
}
});
} else {
callback(err);
}
} else {
// table already created
if (data.Table.TableStatus !== "ACTIVE") {
callback(`Table ${tableName} is not ready with status ${data.Table.TableStatus}.`);
} else {
callback(null, data);
}
}
});
}
/**
* The table uses button's DSN as hash key and timestamp as range key.
* Each entry has an activity message.
*/
function createTable(tableName, callback) {
const param = {
TableName: tableName,
KeySchema: [
{ AttributeName: "dsn", KeyType: "HASH" },
{ AttributeName: "timestamp", KeyType: "RANGE" }
],
AttributeDefinitions: [
{ AttributeName: "dsn", AttributeType: "S" },
{ AttributeName: "timestamp", AttributeType: "N" }
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 5
}
};
dynamodb.createTable(param, callback);
}
/**
* Saves nanny check in/out activity into DynamoDB
*/
function saveEvent(event, callback) {
const params = {
TableName: TABLE_NAME,
Item: {
dsn: { S: event.serialNumber },
timestamp: { N: new Date().getTime().toString() },
message: { S: getMessage(event) }
}
};
dynamodb.putItem(params, callback);
}
/**
* The following JSON template shows what is sent as the payload:
{
"serialNumber": "GXXXXXXXXXXXXXXXXX",
"batteryVoltage": "xxmV",
"clickType": "SINGLE" | "DOUBLE" | "LONG"
}
*
* A "LONG" clickType is sent if the first press lasts longer than 1.5 seconds.
* "SINGLE" and "DOUBLE" clickType payloads are sent for short clicks.
*
* For more documentation, follow the link below.
* http://docs.aws.amazon.com/iot/latest/developerguide/iot-lambda-rule.html
*/
function getMessage(event) {
if (event.clickType === 'SINGLE') {
return 'Your nanny checked in.';
}
if (event.clickType === 'DOUBLE') {
return 'Your nanny checked out.';
}
if (event.clickType === 'LONG') {
return 'Your nanny needs help!';
}
return 'Unknown event: ' + event.clickType;
}
exports.handler = (event, context, callback) => {
console.log('Received event:', event.clickType);
// checks whether the table is ready
createTableIfNeeded(TABLE_NAME, (err, data) => {
if (err) {
console.log("Something wrong with table: " + TABLE_NAME, err);
context.done("Failed to access DynamoDB table.");
return;
}
// writes event into Dynamo
saveEvent(event, (err, data) => {
if (err) {
console.log("Failed to save event: " + JSON.stringify(event), err);
context.done("Failed to save event.");
} else {
console.log("Successfully recorded event.");
// and sends an SMS message if configured
sendSMS(event, PHONE_NUMBER, (err, data) => {
if (err) {
console.log("Failed to send SMS");
context.done(err);
} else {
context.done(null, event);
}
});
}
});
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment