diff --git a/README.md b/README.md index 97371f2..61196dc 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,53 @@ # SmartThings Connected Wired Security System using a NodeMCU ESP8266 -This project will help you connect SmartThings to wired contact sensors (for doors and windows) and motion sensors that you may already have pre-wired in your home from a built-in home security system. There are three components to the project: +This project will help you connect wired contact sensors and motion sensors from an old wired home alarm system (such as +Honeywell, ADT, Interlogix, etc) to Samsung SmartThings. Convert your old wired alarm system into an internet connected + Smart Alarm! + +We use an inexpensive NodeMCU ESP82660 wifi enabled development board to connect our wired alarm system sensors to the +SmartThings. + +There are three components to the project: 1. a SmartThings Device Handler for contact sensors and motion sensors -2. a SmartThings SmartApp that receives HTTP POST messages +2. a SmartThings SmartApp that interfaces with the Wifi-connected device in your home 3. Lua code for the NodeMCU device that connects your wired system to the cloud +## Update June 17 2017 +#### Release 1.6: Easy OAuth and Pre-Loaded Kits Available for Pre-order! + +**Easy OAuth:** Many people who contacted me for help were having trouble with the OAuth flow. In the latest [1.6 release](https://github.com/heythisisnate/nodemcu-smartthings/releases/tag/1.6) +this is now much simpler! You no longer have to manually do the OAuth step. Just open your browser, copy and paste your OAuth + Client ID and Secret when prompted, and the application handles the rest. + +**Pre-loaded Kits for Sale!** I'm working hard to make it as easy as possible for anyone to connect their wired alarm system + to SmartThings, so I've decided to begin selling all-inclusive DIY kits with this software pre-loaded! With one of my DIY kits, + there's no flashing or code to modify. Simply wire your sensors and alarm following the online instructions and open up + your web browser to configure. + + ### [Now Accepting Pre-orders](https://nodemcu-smartthings.com/collections/wired-alarm-system-smartthings-connection-kits-and-accessories) for first shipment in August 2017 + + + + #### [Wired Alarm System Complete DIY Kit](https://nodemcu-smartthings.com/collections/wired-alarm-system-smartthings-connection-kits-and-accessories/products/wired-alarm-system-complete-kit) + * For connecting up to 5 sensors and one siren or alarm + * Software is pre-loaded! Just wire it up and configure with your web browser. + * Includes NodeMCU board, NodeMCU base, relay for siren, and jumper wires + * Includes email support to help you get up and running + * **Pre-order now** for first shipment in August 2017 + + + + #### [Wired Alarm System Add-on DIY Kit](https://nodemcu-smartthings.com/collections/wired-alarm-system-smartthings-connection-kits-and-accessories/products/wired-alarm-system-add-on-kit) + * For connecting up to 6 sensors (no siren) + * Software is pre-loaded! Just wire it up and configure with your web browser. + * Includes NodeMCU board, NodeMCU base and jumper wires + * Includes email support to help you get up and running + * **Pre-order now** for first shipment in August 2017 + + #### [Donate to this Project!](https://nodemcu-smartthings.com/products/donate-to-this-project) + * If you've loved this open-source project, donate any amount to support it! + ### Background The house I live in was built in the early 90s and came with a built-in home security system. I'm not interested in using the outdated alarm system panel, but I wanted to connect the contact sensors in my doors and the motion sensor in my house to SmartThings. I learned about the NodeMCU ESP8266, a small, cheap, programmable development board that has WiFi built in. I set out to connect my door and motion sensors to the NodeMCU and program it to update SmartThings every time a change is detected. @@ -29,12 +71,25 @@ _Update 2:_ One user reported that he had success with [this board](https://www. ## Updates -##### v1.5 / 2017-04-07 +### v1.6 / 2017-06-17 + +_Feature:_ Easy OAuth. The application handles the OAuth flow automatically now. Just point your browser +to `http://:8100/oauth`. See the updated README for details. + +_Feature:_ Authorize multiple alarms with the SmartApp. + +_Bug Fix:_ Strobe output did not work due to copy/paste bug. + +_Bug Fix:_ Fix error in SmartApp when you only have motion sensors authorized. + +**IMPORTANT: Read the [1.6 upgrade notes](https://github.com/heythisisnate/nodemcu-smartthings-sensors/releases/tag/1.6) if you're upgrading from an earlier version.** + +### v1.5 / 2017-04-07 _Feature:_ Connect a wired siren and/or strobe. Integrates seamlessly with the Smart Home Monitor app. -**IMPORTANT: Read the [upgrade notes](https://github.com/heythisisnate/nodemcu-smartthings-sensors/releases/tag/1.5) if you're upgrading from an earlier version.** +**IMPORTANT: Read the [1.5 upgrade notes](https://github.com/heythisisnate/nodemcu-smartthings-sensors/releases/tag/1.5) if you're upgrading from an earlier version.** -##### v1.3 / 2017-03-29 +### v1.3 / 2017-03-29 _Feature:_ Blink the onboard LED on successful communication with SmartThings. To enable set `blink_led = true` in `variables.lua` @@ -42,11 +97,11 @@ _Feature:_ Blink the onboard LED on successful communication with SmartThings. T _Feature:_ **Reliable polling**. Configure by setting `poll_interval` in `variables.lua` to a number to indicate the number of seconds between polling for sensors that may have gotten out of sync. -##### v1.2 / 2017-03-06 +### v1.2 / 2017-03-06 _Feature:_ Reports the status of each sensor upon startup. -##### v1.1 / 2017-02-18 +### v1.1 / 2017-02-18 _Feature:_ Support for wired smoke detectors. @@ -103,46 +158,7 @@ The SmartApp receives data from your NodeMCU device, and updates the status of y 1. Make note of the OAuth Client ID and Client Secret, you'll need these later. 1. Click Publish -> For Me -### 5. Generate an OAuth token - -The OAuth token is used to sign HTTP requests from the NodeMCU to the SmartApp you just created. [SmartThings has documentation of this process here.](http://docs.smartthings.com/en/latest/smartapp-web-services-developers-guide/authorization.html). We'll be going through the OAuth flow manually to capture the token which can then be saved on the NodeMCU. - -1. Copy and paste the below web address into your browser and replace `YOUR-SMARTAPP-CLIENT-ID` with the OAuth Client ID from the SmartApp created eariler. - - ``` - https://graph.api.smartthings.com/oauth/authorize?response_type=code&client_id=YOUR-SMARTAPP-CLIENT-ID&scope=app&redirect_uri=http://localhost:3000/auth - ``` - -1. You'll see a page like this allowing you to authorize the devices you set up earlier: - - ![](screenshots/Authorization2017-02-05-21-59-54.png) - -1. Once you click Authorize, you'll be redirect to http://localhost:3000/auth which will error. That's ok! It wasn't supposed to work. All you need is the code out of the URL parameter: - - ![](screenshots/localhost2017-02-05-22-24-28.png) - -1. Now that you've got the code, it's time to make a POST request to get the access token. For this I like to use [Advanced REST Client Chrome app](https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo?hl=en-US). You can use any tool that can create a POST request with form parameters. Just fill in [the fields](http://docs.smartthings.com/en/latest/smartapp-web-services-developers-guide/authorization.html#get-access-token) as shown: - - ![](screenshots/ARC2017-02-05-22-09-03.png) - -1. Click Send, and with any luck, you'll get a successful response back that contains your access token: - - ![](screenshots/ARC2017-02-05-22-11-09.png) - - Copy this access token into the `credentials.lua` file. - -1. Finally, get your SmartApp endpoint by doing a GET request to `https://graph.api.smartthings.com/api/smartapps/endpoints`, signing the request with an `Authorization` header and your token: - - ![](screenshots/ARC2017-02-05-22-52-45.png) - -1. Click send and make note of the url data returned: - - ![](screenshots/ARC2017-02-0522-53-21.png) - - Copy the `base_url` field from here into the `apiHost` variable in the variables file - Copy the `url` field into the `apiEndpoint` variable in the variables file - -### 6. Flash the NodeMCU Lua firmware +### 5. Flash the NodeMCU Lua firmware #### Drivers @@ -182,12 +198,23 @@ good [documentation here](https://nodemcu.readthedocs.io/en/master/en/flash/) in ![](screenshots/ESPlorer2017-02-06-22-58-30.png) -1. Once connected, it's time to upload the code. Click Open in Esplorer and open each of the lua files on your computer and click "Save to ESP". Alternatively, you can use the Upload button to upload them all at once. [This documentation](http://esp8266.ru/download/esp8266-doc/Getting%20Started%20with%20the%20ESPlorer%20IDE%20-%20Rui%20Santos.pdf) was also very helpful in learning how to interact with the device using Esplorer. -1. After all the code is uploaded, click the Reset button to restart the device. It should boot up, connect to your WiFi and output a message for each configured sensor, like this: +1. Once connected, it's time to upload the code. Click the Upload button in ESplorer and navigate to the `lua` directory. Highlight all the `lua` and `html` files and click Open to upload them to the device: + + ![](screenshots/ESP2017-06-17-14-37-45.png) + +1. After all the code is uploaded, toggle the RTS button on then off to restart the device. It should boot up, connect to your WiFi and output a link to begin the OAuth flow. +1. Copy and paste the OAuth link URL into your web browser and begin the OAuth flow. You'll need the OAuth Client ID and Secret from the SmartApp. +1. After you enter the Client ID and Secret, you'll see a page like this allowing you to authorize the devices you set up earlier: + + ![](screenshots/Authorization2017-02-05-21-59-54.png) + +1. Authorize all the NodeMCU connected devices. When prompted, reboot the device by toggling the RTS button in ESPlorer on and off. + +1. Now your device should be working. You will see a debug message on boot for each configured sensor, like this: ![](screenshots/ESPlorer2017-02-06-23-02-30.png) -1. Now let's test it out! The first sensor in this example is configured on pin 6 (labled D6). Take a wire and connect one end to pin D6 and the other end to the ground (GND). This completes the circuit, setting the pin low or 0, indicating that the contact sensor is closed. Hopefully it worked and you should see a success message in the Esplorer terminal, and when you open your SmartThings app you should see that the door is closed. Now remove the wire and watch it set to open. +1. Let's test it out! The first sensor in this example is configured on pin 6 (labled D6). Take a wire and connect one end to pin D6 and the other end to the ground (GND). This completes the circuit, setting the pin low or 0, indicating that the contact sensor is closed. Hopefully it worked and you should see a success message in the Esplorer terminal, and when you open your SmartThings app you should see that the door is closed. Now remove the wire and watch it set to open. 1. _Note about the pins:_ I found that some of the pins don't work very well when normally _low_. It took a lot of trial and error to figure out that pins D1, D2, D6 and D7 worked reliabliy for me. I had problems with D3, D9 and D10. Your mileage may vary. ### 7. Connect your switches at the alarm panel @@ -211,7 +238,6 @@ signal from the ESP8266 board. These instructions are written for the relay link 1. Make sure the black (-) wire of the siren/strobe is connected to the ground (-) on the alarm panel. 1. Connect the COM on the relay to the 12V aux power out (+) on the alarm panel with a jumper wire. - ## Problems or Questions Please [open an issue](https://github.com/heythisisnate/nodemcu-smartthings-sensors/issues) if you run into problems or have feature requests. You can also [join the discussion on SmartThings community](https://community.smartthings.com/t/connect-wired-alarm-system-sensors-to-smartthings-with-a-nodemcu-esp8266/76010) diff --git a/SmartThings/cloud-sensor.groovy b/SmartThings/cloud-sensor.groovy index d4cd07e..79ad49e 100644 --- a/SmartThings/cloud-sensor.groovy +++ b/SmartThings/cloud-sensor.groovy @@ -32,7 +32,7 @@ preferences { input "contactSensors", "capability.contactSensor", title: "Contact sensors", multiple:true, required:false input "motionSensors", "capability.motionSensor", title: "Motion sensors", multiple:true, required:false input "smokeDetectors", "capability.smokeDetector", title: "Smoke detectors", multiple:true, required:false - input "alarm", "capability.alarm", title: "Alarm", required:false + input "alarms", "capability.alarm", title: "Alarms", multiple: true, required:false } } @@ -52,7 +52,7 @@ mappings { def handle_event() { def event = request.JSON - def allSensors = (contactSensors?:[] + motionSensors?:[] + smokeDetectors?:[]) - null + def allSensors = (contactSensors?:[]) + (motionSensors?:[]) + (smokeDetectors?:[]) - null def device = allSensors.find { event.sensor_id == it.id } @@ -74,7 +74,10 @@ def handle_event() { def sync() { def sync_data = request.JSON - if (sync_data.device_id == alarm.id) { + def alarm = alarms.find { + sync_data.device_id == it.id + } + if (alarm) { alarm.sync(sync_data.ip, sync_data.port as String, sync_data.mac) } return [ "success": true ] diff --git a/lua/alarm.lua b/lua/alarm.lua index 5e72be4..f7a80ca 100644 --- a/lua/alarm.lua +++ b/lua/alarm.lua @@ -62,61 +62,3 @@ function alarmState() if strobe then return "strobe" end return "off" end - --- 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 device_id - local action - - device_id, action = string.match(requestObject.path, '^/([%w-]+)/(%w+)') - - if 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() }) - if blink_led then blinkLed() end - else - response_code = "404 NOT FOUND" - response_body = [[{"status":"error","message":"No device with ID ]] .. device_id .. [[ is configured"}]] - end - - local response = {} - response[1] = "HTTP/1.1 " .. response_code .. "\r\n" - response[2] = "Server: NodeMCU on ESP8266\r\n" - response[3] = "Content-Type: application/json\r\n" - response[4] = "Content-Length: " .. string.len(response_body) .. "\r\n\r\n" - response[5] = response_body - - -- 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 - -if alarm and alarm.httpPort and alarm.deviceId and (alarm.sirenPin or alarm.strobePin) then - alarmListener = net.createServer(net.TCP) - alarmListener:listen(alarm.httpPort, function(connection) - connection:on("receive", processRequest) - end) - - print("Listening for Alarm commands on HTTP port " .. alarm.httpPort) -end diff --git a/lua/application.lua b/lua/application.lua index 54d9b67..e76013d 100644 --- a/lua/application.lua +++ b/lua/application.lua @@ -1,13 +1,9 @@ --- --- SETUP --- - -require "variables" -- set up application variables globalHeaders = "Host: " .. apiHost .. "\r\n" globalHeaders = globalHeaders .. "Authorization: Bearer " .. auth_token .. "\r\n" globalHeaders = globalHeaders .. "Content-Type: application/json\r\n" + requestQueue = {} if blink_led then @@ -15,18 +11,6 @@ if blink_led then gpio.mode(led_pin, gpio.OUTPUT) end --- --- GLOBAL FUNCTIONS --- - --- Blink the onboard LED -function blinkLed() - gpio.write(led_pin, gpio.LOW) - tmr.create():alarm(100, tmr.ALARM_SINGLE, function() - gpio.write(led_pin, gpio.HIGH) - end) -end - -- Inserts a request to the end of the queue function queueRequest(endpoint, requestData) table.insert(requestQueue, {endpoint, requestData}) @@ -37,25 +21,30 @@ function doNextRequest() local requestData = requestQueue[1] if requestData then - local endpoint = requestData[1] - local payload = cjson.encode(requestData[2]) - -- set http headers - local headers = globalHeaders .. "Content-Length: " .. string.len(payload) .. "\r\n" - - -- do the POST to SmartThings - http.post( - apiHost .. apiEndpoint .. endpoint, - headers, - payload, - function(code, data) - if code == 201 then - print("Success: " .. payload) - table.remove(requestQueue, 1) -- remove from the queue when successful - if blink_led then blinkLed() end - elseif code > 201 then - print("Error " .. code .. " posting " .. payload .. ", retrying") - end - end) + local endpoint = requestData[1] + local payload = cjson.encode(requestData[2]) + local url = apiHost .. apiEndpoint .. endpoint + + -- set http headers + local headers = globalHeaders .. "Content-Length: " .. string.len(payload) .. "\r\n" + + -- do the POST to SmartThings + http.post( + url, + headers, + payload, + function(code) + if code == 201 then + print("Success: " .. payload) + table.remove(requestQueue, 1) -- remove from the queue when successful + if blink_led then blinkLed() end + elseif code > 201 then + print("Error " .. code .. " posting " .. payload .. ", retrying") + end + payload = nil + end) + + headers, requestData, url, endpoint = nil end end @@ -103,7 +92,7 @@ end -- In case of a HTTP failure, re-insert the request data back into the first position so it will -- retry on the next cycle. -- This throttles the HTTP calls to SmartThings in an attempt to prevent timeouts -tmr.create():alarm(1000, tmr.ALARM_AUTO, doNextRequest) +tmr.create():alarm(1500, tmr.ALARM_AUTO, doNextRequest) -- Poll sensors periodically if configured if poll_interval and poll_interval > 0 then diff --git a/lua/boot.lua b/lua/boot.lua new file mode 100644 index 0000000..ae2d3ac --- /dev/null +++ b/lua/boot.lua @@ -0,0 +1,12 @@ +-- +-- SETUP +-- + +require "variables" +require "common" +require "server" +require "oauth" + +if auth_token then + getApiEndpointAndStart() +end diff --git a/lua/common.lua b/lua/common.lua new file mode 100644 index 0000000..96f3e5f --- /dev/null +++ b/lua/common.lua @@ -0,0 +1,21 @@ +-- +-- COMMON GLOBAL FUNCTIONS +-- + +-- Blink the onboard LED +function blinkLed() + gpio.write(led_pin, gpio.LOW) + tmr.create():alarm(100, tmr.ALARM_SINGLE, function() + gpio.write(led_pin, gpio.HIGH) + end) +end + +function writeCredentials() + if file.open('credentials.lua', 'w+') then + file.writeline("wifi_ssid = \"" .. wifi_ssid .. "\"") + file.writeline("wifi_password = \"" .. wifi_password .. "\"") + file.writeline("auth_token = \"" .. auth_token .. "\"") + file.close() + end + print("wrote credentials.lua") +end diff --git a/lua/credentials.lua.example b/lua/credentials.lua.example index 9a88bdb..1596c94 100644 --- a/lua/credentials.lua.example +++ b/lua/credentials.lua.example @@ -1,3 +1,2 @@ wifi_ssid = "myhouse" wifi_password = "mypass" -auth_token = "3333333-0000-aaaa-dddd-6666666666" diff --git a/lua/init.lua b/lua/init.lua index d71f15f..4f2ba68 100644 --- a/lua/init.lua +++ b/lua/init.lua @@ -6,8 +6,7 @@ function startup() else print("Running application...") file.close("init.lua") - -- the actual application is stored in 'application.lua' - dofile("application.lua") + require "boot" end end diff --git a/lua/oauth-complete.html b/lua/oauth-complete.html new file mode 100644 index 0000000..f9ecc1c --- /dev/null +++ b/lua/oauth-complete.html @@ -0,0 +1,18 @@ + + + + + + +
+

OAuth Complete!

+

Please reboot your device.

+
+ + diff --git a/lua/oauth.html b/lua/oauth.html new file mode 100644 index 0000000..5189923 --- /dev/null +++ b/lua/oauth.html @@ -0,0 +1,38 @@ + + + + + + +
+

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