-
Notifications
You must be signed in to change notification settings - Fork 2
/
server.js
169 lines (155 loc) · 5.3 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
'use strict';
/* npm node.js modules */
const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const fs = require('fs');
const httpSignature = require('http-signature');
const prettyjson = require('prettyjson');
const rp = require('request-promise');
/* node.js implemented modules */
const apps = require('./lib/apps');
const stConfig = require('./lib/config');
const deviceprofiles = require('./lib/deviceprofiles');
const devices = require('./lib/devices');
const installedApps = require('./lib/installedapps');
const locations = require('./lib/locations');
const schedules = require('./lib/schedules');
const subscriptions = require('./lib/subscriptions');
/******************************************************/
const rooms = require('./lib/rooms');
const rule = require('./lib/rules');
const scenes = require('./lib/scenes');
/* SmartThings API */
const stApi = 'https://api.smartthings.com/v1';
const prettyjsonOptions = {};
const app = express();
app.use(bodyParser.json());
/**
* Entry point for callbacks by SmartThings.
* Every incoming call will have a lifecycle, which determines how the
* app should respond.
*
* All requests will have their HTTP signature verified to ensure the
* request is actually from SmartThings, except for the PING or CONFIRMATION lifecycle
* request (which occurs as the app is being created).
*
* @param {Object} req - HTTP requests
* @param {Object} response - App respondse
*/
app.post('/', function (req, response) {
// We don't yet have the public key during PING or CONFIRMATION (when the app is created),
// so no need to verify the signature. All other requests are verified.
if (req.body && (req.body.lifecycle === "PING" || req.body.lifecycle === "CONFIRMATION") || signatureIsVerified(req)) {
handleRequest(req, response);
} else {
response.status(401).send("Forbidden");
}
});
/**
* Verifies that the request is actually from SmartThings.
* @returns {boolean} true if verified
*/
function signatureIsVerified() {
return true;
}
/**
* Lifecycle handle requests.
* @param {Object} req - HTTP requests
* @param {Object} response - App respondse
*/
function handleRequest(req, response) {
let evt = req.body;
let lifecycle = evt.lifecycle;
let res;
console.log(`${lifecycle} lifecycle. Request body:`);
console.log(prettyjson.render(evt, prettyjsonOptions));
switch (lifecycle) {
// PING happens during app creation. Purpose is to verify app
// is alive and is who it says it is. Is deprecated.
case 'PING':
{
let chal = evt.pingData.challenge;
response.json({statusCode: 200, pingData: {challenge: chal}});
break;
}
// CONFIRMATION lifecycle phase occurs when a WebHook SmartApp is registered.
case 'CONFIRMATION' :
{
rp.get(evt.confirmationData.confirmationUrl);
response.json({statusCode: 200});
break;
}
// CONFIGURATION happens as user begins to install the app.
case 'CONFIGURATION':
{
res = stConfig.handle(evt.configurationData);
console.log(prettyjson.render({configurationData: res}, prettyjsonOptions));
response.json({statusCode: 200, configurationData: res});
break;
}
// INSTALL happens after a user finishes configuration, and installs the
// app.
case 'INSTALL':
{
let token = evt.installData.authToken;
let installedApp = evt.installData.installedApp;
/**
* TO DO
*/
response.json({statusCode: 200, installData: {}});
break;
}
// UPDATE happens when a user updates the configuration of an
// already-installed app.
case 'UPDATE':
{
let token = evt.updateData.authToken;
let installedApp = evt.updateData.installedApp;
/**
* TO DO
*/
response.json({statusCode: 200, updateData: {}});
break;
}
// UNINSTALL happens when a user uninstalls the app.
case 'UNINSTALL':
{
response.json({statusCode: 200, uninstallData: {}});
break;
}
// EVENT happens when any subscribed-to event or schedule executes.
case 'EVENT':
{
handleEvent(evt.eventData);
response.json({statusCode: 200, eventData: {}});
break;
}
default:
{
console.log(`Lifecycle ${lifecycle} not supported`);
}
}
}
/**
* Handles incoming EVENT lifecycle requests.
*
*
* @param {Object} eventData - The eventData associated with this event.
*/
function handleEvent(eventData) {
const eventType = eventData.events[0].eventType;
const token = eventData.authToken;
if ("TIMER_EVENT" === eventType) {
let timerEvent = eventData.events[0].timerEvent;
console.log(`Received timer event for schedule ${timerEvent.name} at ${timerEvent.time}`);
/**
* TO DO
*/
} else {
console.error(`This app only expects TIMER_EVENTs. Got ${eventType}`);
}
}
let server = app.listen(3000);
module.exports = server;
console.log('Open: http://127.0.0.1:3000');