+
Get Started with NodeMCU SmartThings
+
Authorize this device to access your SmartThings account. Edit the NodeMCU SmartThings SmartApp in
+ the SmartThings web based IDE. Scroll down to the the OAuth section and copy the Client ID and Client Secret
+ here.
+
+
+
+
diff --git a/lua/oauth.lua b/lua/oauth.lua
new file mode 100644
index 0000000..389de86
--- /dev/null
+++ b/lua/oauth.lua
@@ -0,0 +1,41 @@
+if not auth_token then
+ print("**************** Welcome to NodeMCU SmartThings ****************")
+ print("**** ****")
+ print("**** To get started, open your browser to: ****")
+ print("**** http://" .. wifi.sta.getip() .. ":8100/oauth ****")
+ print("**** ****")
+end
+
+function oauthRequestCode(request)
+ oauth_client_id, oauth_client_secret = string.match(request, 'oauth_client_id=([%w-]+)&oauth_client_secret=([%w-]+)')
+ return "https://graph.api.smartthings.com/oauth/authorize?response_type=code&client_id=" .. oauth_client_id .. "&scope=app&redirect_uri=" .. oauthCallbackUrl()
+end
+
+function oauthCallbackUrl()
+ return "http://" .. wifi.sta.getip() .. ":8100/oauth/callback"
+end
+
+function saveOauthToken(oauth_json)
+ auth_token = string.match(oauth_json, '"access_token":"([%w-]+)"')
+ print("Your SmartThings OAuth token is: " .. auth_token)
+ writeCredentials()
+end
+
+function getApiEndpointAndStart()
+ local headers = "Host: https://graph.api.smartthings.com\r\n"
+ headers = headers .. "Authorization: Bearer " .. auth_token
+ headers = headers .. "\r\nAccept: application/json\r\n"
+
+ http.get('https://graph.api.smartthings.com/api/smartapps/endpoints',
+ headers,
+ function(code, data)
+ if code == 200 then
+ apiHost, apiEndpoint = string.match(data, '"base_url":"([^"]+)","url":"([^"]+)"')
+ print("Communicating with SmartThings at " .. apiHost .. apiEndpoint)
+ require "application"
+ else
+ print(code)
+ end
+ end
+ )
+end
diff --git a/lua/server.lua b/lua/server.lua
new file mode 100644
index 0000000..0ca29c9
--- /dev/null
+++ b/lua/server.lua
@@ -0,0 +1,96 @@
+-- Process an incoming HTTP request
+function processRequest(connection, request)
+
+ -- iterate through each line in the request payload and construct a table
+ local requestObject = {}
+ -- for line in string.gmatch(request, '[^\n]+') do print(line) end
+ requestObject.method, requestObject.path = string.match(request, '^(%u+) (/[%w%p]*)')
+ requestObject.host, requestObject.port = string.match(request, '[Hh][Oo][Ss][Tt]: ([%w%p]*):(%d+)')
+ print(requestObject.host .. ":" ..requestObject.port, requestObject.method, requestObject.path)
+
+ local response_body
+ local response_code
+ local content_type
+ local device_id
+ local action
+ local response = {}
+
+ device_id, action = string.match(requestObject.path, '^/([%w-]+)/(%w+)')
+
+ local function render()
+ table.insert(response, "HTTP/1.1 " .. response_code .. "\r\n")
+ table.insert(response, "Server: NodeMCU on ESP8266\r\n")
+ table.insert(response, "Content-Type: " .. content_type .. "\r\n")
+ table.insert(response, "Content-Length: " .. string.len(response_body) .. "\r\n")
+ table.insert(response, "\r\n")
+ table.insert(response, response_body)
+ end
+
+ local function redirect(redirectUrl)
+ table.insert(response, "HTTP/1.1 302 REDIRECT\r\n")
+ table.insert(response, "Server: NodeMCU on ESP8266\r\n")
+ table.insert(response, "Location: " .. redirectUrl .. "\r\n")
+ end
+
+ if requestObject.path == "/oauth" then
+ content_type = "text/html"
+ if requestObject.method == 'POST' then
+ redirect(oauthRequestCode(request))
+ else
+ response_code = "200 OK"
+ file.open('oauth.html')
+ response_body = file.read(1200)
+ file.close()
+ end
+ elseif string.match(requestObject.path, '^/oauth/callback') then
+ local auth_code = string.match(requestObject.path, 'code=(%w+)')
+ local payload = "grant_type=authorization_code&code=" .. auth_code .. "&client_id=" .. oauth_client_id .. "&client_secret=" .. oauth_client_secret .. "&redirect_uri=" .. oauthCallbackUrl()
+ response_code = "200 OK"
+ content_type = "text/html"
+ http.post(
+ 'https://graph.api.smartthings.com/oauth/token',
+ "Content-Type: application/x-www-form-urlencoded\r\nContent-Length: " .. string.len(payload) .. "\r\n",
+ payload,
+ function(code, data)
+ saveOauthToken(data)
+ end
+ )
+ file.open('oauth-complete.html')
+ response_body = file.read()
+ file.close()
+
+ elseif device_id == alarm.deviceId and requestObject.method == 'POST' then
+ alarmAction(action)
+ response_code = "200 OK"
+ response_body = cjson.encode({ device_id = device_id, alarm = alarmState() })
+ content_type = "application/json"
+ if blink_led then blinkLed() end
+ else
+ response_code = "404 NOT FOUND"
+ response_body = [[{"status":"error","message":"The URL or device id is incorrect."}]]
+ content_type = "application/json"
+ end
+
+ if #response == 0 then render() end
+
+ -- sends and removes the first element from the 'response' table
+ local function send(localSocket)
+ if #response > 0 then
+ localSocket:send(table.remove(response, 1))
+ else
+ localSocket:close()
+ response = nil
+ end
+ end
+
+ -- triggers the send() function again once the first chunk of data was sent
+ connection:on("sent", send)
+ send(connection)
+end
+
+httpListener = net.createServer(net.TCP)
+httpListener:listen(8100, function(connection)
+ connection:on("receive", processRequest)
+end)
+
+print("Listening for HTTP commands on port 8100")
diff --git a/lua/variables.lua.example b/lua/variables.lua.example
index 66e70d9..cbe8394 100644
--- a/lua/variables.lua.example
+++ b/lua/variables.lua.example
@@ -2,11 +2,6 @@
-- VARIABLES & OPTIONS
--
--- The `apiHost` and `apiEndpoint` can be obtained by doing an authenticated API call
--- to `https://graph.api.smartthings.com/api/smartapps/endpoints`. See the README for more details.
-apiHost = "https://graph-na02-useast1.api.smartthings.com"
-apiEndpoint = "/api/smartapps/installations/88888888-cccc-dddd-55555-44444444444"
-
-- Set `blink_led` to true to blink the blue onboard LED upon successful communication with SmartThings
-- This is useful for visual confirmation and testing, but I leave it off in production
-- The onboard LED is tied to pin D4 so you cannot use this pin for other things when enabled
diff --git a/screenshots/ESP2017-06-17-14-37-45.png b/screenshots/ESP2017-06-17-14-37-45.png
new file mode 100644
index 0000000..e0bb04a
Binary files /dev/null and b/screenshots/ESP2017-06-17-14-37-45.png differ
diff --git a/screenshots/add-on-kit.jpg b/screenshots/add-on-kit.jpg
new file mode 100644
index 0000000..b529fe2
Binary files /dev/null and b/screenshots/add-on-kit.jpg differ
diff --git a/screenshots/complete-kit.jpg b/screenshots/complete-kit.jpg
new file mode 100644
index 0000000..a7de0e8
Binary files /dev/null and b/screenshots/complete-kit.jpg differ