diff options
-rw-r--r-- | IOT-clapSensor.ino | 266 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | index.h | 119 | ||||
-rw-r--r-- | memoryAccess.cpp | 77 | ||||
-rw-r--r-- | memoryAccess.h | 25 | ||||
-rw-r--r-- | settings.h | 146 | ||||
-rw-r--r-- | style.h | 3 | ||||
-rw-r--r-- | webPage/index.html | 127 | ||||
-rw-r--r-- | webPage/settings.html | 167 | ||||
-rw-r--r-- | webPage/style.css | 148 |
10 files changed, 1091 insertions, 1 deletions
diff --git a/IOT-clapSensor.ino b/IOT-clapSensor.ino new file mode 100644 index 0000000..a6f236e --- /dev/null +++ b/IOT-clapSensor.ino @@ -0,0 +1,266 @@ +#include <ESP8266WiFi.h> +#include <WiFiClient.h> +#include <ESP8266WebServer.h> +#include <ESP8266mDNS.h> +#include <EEPROM.h> +#include "memoryAccess.h" + +#include "style.h" // custom stylesheet, get it from stylesheet +#include "index.h" // Index page html, get it from MAIN_page +#include "settings.h" // Settings page html, get it from SETTINGS_page + +// User variables +const String MemoryAccessVersion = "cp-a1"; +const String deviceType = "clapSensor"; // This is what kind of device it is, crucial for the homeServer when determing which API to use +const int wifiConnectionTimeout = 5; // n*5 sec +char ssid[30]; +char password[30]; +char APssid[30]; +String deviceName; +String deviceLocation; +String deviceId; + +memoryAccess MemoryAccess; + +const int lampPin = 14; // Pin the appliance is connected to +const int sensorPin = 5; // Pin the sensor is connected to + +bool lampOn; // Is the appliance turned on? +bool sensorActive; // Should we care about sensor input? +bool buttonState; // Used to make button not flash indefinetly + +ESP8266WebServer server ( 80 ); // webServer on port 80 + +void setup ( void ) { + // Activate serial + Serial.begin ( 115200 ); + MemoryAccess.init(); + MemoryAccess.dump(); + + + if (MemoryAccess.readAscii("version")) { + deviceName = MemoryAccess.readAscii("deviceName"); + deviceLocation = MemoryAccess.readAscii("deviceLocation"); + deviceId = MemoryAccess.readAscii("deviceId"); + MemoryAccess.readAscii("SSID").toCharArray(ssid, 30); + MemoryAccess.readAscii("Password").toCharArray(password, 30); + (deviceType + "-" + deviceId).toCharArray(APssid, 30); + } else { + // SET TO DEFAULT VALUES + MemoryAccess.writeAscii("version", MemoryAccessVersion); + MemoryAccess.writeAscii("deviceName", ""); + MemoryAccess.writeAscii("deviceLocation", ""); + MemoryAccess.writeAscii("deviceGuid", uniqId()); + MemoryAccess.writeAscii("SSID", ""); + MemoryAccess.writeAscii("Password", ""); + MemoryAccess.commit(); + Serial.println("Have set default values, making watchdog boot us!"); + while (1); + } + + Serial.print("ssid: "); + Serial.println(ssid); + Serial.print("Password: "); + Serial.println(password); + Serial.print("deviceName: "); + Serial.println(deviceName); + Serial.print("deviceLocation: "); + Serial.println(deviceLocation); + Serial.print("deviceId: "); + Serial.println(deviceId); + + + // Setup pins and set default values + pinMode ( lampPin, OUTPUT ); + pinMode ( sensorPin, INPUT ); + lampOn = false; // Vil at denne skal bli husket, altså være samme etter boot + sensorActive = true; + digitalWrite(lampPin, lampOn); + digitalWrite(sensorPin, sensorActive); + + // Try to connect to WiFi + WiFi.begin ( ssid, password ); + + // Wait for connection + int connTime = 0; + while ( WiFi.status() != WL_CONNECTED ) { + if (connTime >= wifiConnectionTimeout) { break; } + delay ( 1000 ); + Serial.print ( "." ); + connTime++; + } + + Serial.println ( "" ); + + if (connTime >= wifiConnectionTimeout) { + WiFi.softAP(APssid); // add password here as second parameter, currently just a open hotspot + IPAddress myIP = WiFi.softAPIP(); + Serial.print("APssid: "); + Serial.println(APssid); + Serial.print("AP IP address: "); + Serial.println(myIP); + } else { + Serial.print ( "Connected to " ); + Serial.println ( ssid ); + Serial.print ( "IP address: " ); + Serial.println ( WiFi.localIP() ); + } + + if ( MDNS.begin ( "esp8266" ) ) { + Serial.println ( "MDNS responder started" ); + } + + // Gui + server.on ( "/", handleRoot ); + server.on ( "/settings", handleSettings ); + // Json + server.on ( "/j/", handleJson ); + server.on ( "/j", handleJson ); + // Others + server.on ( "/style.css", handleStylesheet); + server.on ( "/inline", []() { + server.send ( 200, "text/plain", "this works as well" ); + } ); + server.onNotFound ( handleNotFound ); + server.begin(); + Serial.println ( "HTTP server started" ); +} + +void loop ( void ) { + server.handleClient(); + + if (sensorActive) { + bool currButtonState = digitalRead(sensorPin); + if (currButtonState == true && buttonState == false) { + setLamp("TOGGLE"); + + } + buttonState = currButtonState; + } + +} + +String uniqId() { + return "AAAAAAAAAA"; +} + +void setLamp(String action) { + if (action == "ON") { + lampOn = true; + } else if (action == "OFF") { + lampOn = false; + } else if (action == "TOGGLE") { + lampOn = !lampOn; + } + digitalWrite(lampPin, lampOn); +} + +void setSensor(String action) { + if (action == "ON") { + sensorActive = true; + } else if (action == "OFF") { + sensorActive = false; + } +} + +void handleRoot() { + String htmlPage = MAIN_page; + htmlPage.replace("{{NAME}}", deviceName); + htmlPage.replace("{{Location}}", deviceLocation); + server.send ( 200, "text/html", htmlPage); +} + +void handleJson() { + // escape dobbel tøddler med en \ slik \" for å ha dobbel tøddler i en string + + for (int i = 0; i < server.args(); i++) { + String argKey = server.argName(i); + String argVal = server.arg(i); + + if (argKey == "lamp") { + if (argVal == "1" || argVal == "true") { setLamp("ON"); } + else { setLamp("OFF"); } + } else if (argKey == "sens") { + if (argVal == "1" || argVal == "true") { setSensor("ON"); } + else { setSensor("OFF"); } + } + } + + String jsonAnswer = "{"; + jsonAnswer += "\"deviceName\":\""; + jsonAnswer += deviceName; + jsonAnswer += "\",\"deviceId\":\""; + jsonAnswer += deviceId; + jsonAnswer += "\",\"deviceType\":\""; + jsonAnswer += deviceType; + jsonAnswer += "\",\"deviceLocation\":\""; + jsonAnswer += deviceLocation; + jsonAnswer += "\",\"lampOn\":\""; + jsonAnswer += (String)lampOn; + jsonAnswer += "\",\"sensorOn\":\""; + jsonAnswer += (String)sensorActive; + jsonAnswer += "\"}"; + server.send ( 200, "text/html", jsonAnswer); +} + +void handleSettings() { + + String type, _deviceName, _deviceLocation, _ssid, _password; + for (int i = 0; i < server.args(); i++) { + String argKey = server.argName(i); + String argVal = server.arg(i); + + if (argKey == "txtDeviceName") { + _deviceName = argVal; + } else if (argKey == "txtDeviceLocation") { + _deviceLocation = argVal; + } else if (argKey == "txtSSID") { + _ssid = argVal; + } else if (argKey == "txtPassword") { + _password = argVal; + } + } + + if (type == "settings") { + if (_deviceName != "") { + MemoryAccess.writeAscii("deviceName", _deviceName); + } else if (_deviceLocation != "") { + MemoryAccess.writeAscii("deviceLocation", _deviceLocation); + } else if (_ssid != "") { + MemoryAccess.writeAscii("SSID", _ssid); + } else if (_password != "") { + MemoryAccess.writeAscii("Password", _password); + } + MemoryAccess.commit(); + while (1); + } + + String htmlResponse = SETTINGS_page; + htmlResponse.replace("{{NAME}}", deviceName); + htmlResponse.replace("{{SUCCESSMSG}}", ""); + htmlResponse.replace("{{DEVICENAME}}", deviceName); + htmlResponse.replace("{{DEVICELOCATION}}", deviceLocation); + server.send( 200, "text/html", htmlResponse ); +} + +void handleStylesheet() { + String style = stylesheet; + server.send ( 200, "text/css", style); +} + +void handleNotFound() { + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += ( server.method() == HTTP_GET ) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + + for ( uint8_t i = 0; i < server.args(); i++ ) { + message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n"; + } + + server.send ( 404, "text/plain", message ); +} @@ -1 +1,13 @@ -# IOT-ClapSensor
\ No newline at end of file +# IOT-ClapSensor + +### Features +- Enable/Disable lamp through web app +- Enable/Disable possibility to use physical button to toggle lamp through web app +- Live update of stat in web app +- It will act as an AP if it can´t connect to the saved network +- It will save user preferences in the EEPROM + +### TODO +- Gjør OTA mulig +- Sett opp kryptering (Tror bare det kommer til å bli passord for å endre instillingene) +- Få klappesensoren til å virke som ønsket @@ -0,0 +1,119 @@ +const char MAIN_page[] PROGMEM = R"=====( + <!DOCTYPE html> + <html> + + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + + <title>{{NAME}}</title> + + <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> + <link rel="stylesheet" href="./style.css"> + </head> + + + <body> + + <nav class="navbar navbar-dark bg-dark"> + <a class="navbar-brand" href="/">IOT Device</a> + <a class="navbar-toggler navbar-toggler-right" href="/settings"> + ⚙ + </a> + </nav> + + <div class="container-fluid pt-3"> + + <div class="card card-inverse bg-dark text-white"> + <div class="card-body"> + + <div class="card-title"> + <h3> + {{NAME}} + <span class="text-align:right;"> + <label class="switch float-right"> + <input type="checkbox" id="lampActive"> + <span class="slider round"></span> + </label> + </span> + </h3> + </div> + <h6 class="card-subtitle mb-2 text-muted">{{Location}}</h6> + + <hr /> + <div class="toggle-button toggle-button--tuli"> + <input id="sensorActive" type="checkbox"> + <label for="sensorActive">Clap sensor</label> + <div class="toggle-button__icon"></div> + </div> + + </div> + </div> + + </div> + + + <script type="text/javascript"> + document.getElementById("lampActive").addEventListener("change", changLampState); + document.getElementById("sensorActive").addEventListener("change", changSensorState); + setInterval(getStates, 1000); + + function ajax_request(adress, callback_function) { + /* A simple ajax request wrapper + Doesn´t return anything else than the callback */ + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + callback_function(this.responseText); + } + }; + xhttp.open("POST", adress, true); + xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhttp.send(); + } + + function changLampState() { + var boxState = document.getElementById("lampActive").checked; + if (boxState) { + ajax_request("/j/lamp/1", function(e) { + console.log(e); + }); + } else { + ajax_request("/j/lamp/0", function(e) { + console.log(e); + }); + } + } + + function changSensorState() { + var sensorState = document.getElementById("sensorActive").checked; + if (sensorState) { + ajax_request("/j/sens/1", function(e) { + console.log(e); + }); + } else { + ajax_request("/j/sens/0", function(e) { + console.log(e); + }); + } + } + + function getStates() { + ajax_request("/j/", function(e) { + result = JSON.parse(e); + + var lampOn = false; + var sensorOn = false; + if (result['lampOn'] == 1) { lampOn = true; } + if (result['sensorOn'] == 1) { sensorOn = true; } + + document.getElementById("lampActive").checked = lampOn; + document.getElementById("sensorActive").checked = sensorOn; + }); + } + </script> + </body> + + </html> + +)====="; diff --git a/memoryAccess.cpp b/memoryAccess.cpp new file mode 100644 index 0000000..2887b74 --- /dev/null +++ b/memoryAccess.cpp @@ -0,0 +1,77 @@ +/* This class is made spesifically for the IOT-clapSensor + ADRESSES + version and others {0, 9} + deviceGuid {10, 19} + deviceName {20, 69} + deviceLocation {70, 119} + SSID {120, 269} + Password {270, 219} +*/ +#include "Arduino.h" +#include "memoryAccess.h" +#include <EEPROM.h> + +void memoryAccess::init() { + EEPROM.begin(512); +} + +struct memoryAdress memoryAccess::getAdress(String varName) { + memoryAdress tmp; + if ((String) "version" == varName) { tmp = {0, 4}; } + else if ((String) "deviceId" == varName) { tmp = {10, 19}; } + else if ((String) "deviceName" == varName) { tmp = {20, 69}; } + else if ((String) "deviceLocation" == varName) { tmp = {70, 119}; } + else if ((String) "SSID" == varName) { tmp = {120, 269}; } + else if ((String) "Password" == varName) { tmp = {270, 219}; } + else { Serial.println("UNKNOWN MEMNAME"); } + return tmp; +} + +bool memoryAccess::writeAscii(String varName, String data) { + /* Write ascii string to EEPROM */ + struct memoryAdress memAdress = this->getAdress(varName); + int LString = data.length() - 1; + int LMemory = memAdress.endPos - memAdress.startPos; + + if (LString > LMemory) { return false; } + + int o = 0; + for (int i = memAdress.startPos; i <= memAdress.endPos; i++) { + byte Cchar = data.charAt(o); + if (EEPROM.read(i) != Char) { + EEPROM.write(i, Cchar); + } + o++; + } + return true; +} + +String memoryAccess::readAscii(String varName) { + /* Read ascii string from EEPROM */ + struct memoryAdress memAdress = this->getAdress(varName); + String buffer = ""; + + for (int i = memAdress.startPos; i <= memAdress.endPos; i++) { + char tmpByte = EEPROM.read(i); + if (tmpByte != '\0') { + buffer += tmpByte; + } + } + + return buffer; +} + +void memoryAccess::commit() { + EEPROM.commit(); +} + +void memoryAccess::dump() { + Serial.println(""); + Serial.println("EEPROM DUMP:"); + for (int i = 0; i < 512; i++) { + char cChar = EEPROM.read(i); + String cStr = String(cChar); + Serial.print("[" + String(i) + "]: "); + Serial.println(String(cStr)); + } +} diff --git a/memoryAccess.h b/memoryAccess.h new file mode 100644 index 0000000..048ae86 --- /dev/null +++ b/memoryAccess.h @@ -0,0 +1,25 @@ +/* + memoryAccess.h - Library for easy writing to the IOT-ClapSensor EEPROM +*/ +#ifndef memoryAccess_h +#define memoryAccess_h + +#include "Arduino.h" + +class memoryAccess { + public: + void init(); + bool writeAscii(String varName, String data); + String readAscii(String varName); + void commit(); + void dump(); + private: + struct memoryAdress getAdress(String varName); +}; + +struct memoryAdress { + int startPos; + int endPos; +}; + +#endif diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..af1b7f6 --- /dev/null +++ b/settings.h @@ -0,0 +1,146 @@ +const char SETTINGS_page[] PROGMEM = R"=====( + <!DOCTYPE html> + <html> + + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + + <title>{{NAME}}</title> + + <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> + <link rel="stylesheet" href="./style.css"> + </head> + + + <body> + + <nav class="navbar navbar-dark bg-dark"> + <a class="navbar-brand" href="/">IOT Device</a> + <a class="navbar-toggler navbar-toggler-right" href="/settings"> + ⚙ + </a> + </nav> + + <div class="container-fluid pt-3 pb-3"> + + <div class="alert alert-danger" role="alert"> + <b>Warning:</b> The password-protection does not work yet! Make sure to don´t expose the device. + </div> + + <div class="card card-inverse bg-dark text-white mt-3"> + <div class="card-body"> + + <div class="card-title"> + <h3>Password</h3> + </div> + + <div class="container"> + <form> + + <h5 class="small text-muted">To change any settings, you must enter the correct password"</h5> + <div class="form-group row"> + + <div class="col-sm-12"> + <input type="password" class="form-control" id="name" placeholder="Device password" readonly> + </div> + </div> + + </form> + </div> + + </div> + </div> + + <div class="card card-inverse bg-dark text-white mt-3"> + <div class="card-body"> + + <div class="card-title"> + <h3>Settings</h3> + </div> + + <div class="container"> + <form> + + <h5 class="small text-muted">Device settings:</h5> + <div class="form-group row"> + <label for="inputEmail3" class="col-sm-2 col-form-label">Device name</label> + <div class="col-sm-10"> + <input type="name" class="form-control" id="name" placeholder="Rooflight"> + </div> + </div> + + <div class="form-group row"> + <label for="inputEmail3" class="col-sm-2 col-form-label">Location</label> + <div class="col-sm-10"> + <input type="name" class="form-control" id="name" placeholder="Bathroom"> + </div> + </div> + + <h5 class="small text-muted">Wifi settings:</h5> + <div class="form-group row"> + <label for="inputEmail3" class="col-sm-2 col-form-label">SSID</label> + <div class="col-sm-10"> + <input type="name" class="form-control" id="name" placeholder="Name of router"> + </div> + </div> + + <div class="form-group row"> + <label for="inputEmail3" class="col-sm-2 col-form-label">Pasword</label> + <div class="col-sm-10"> + <input type="name" class="form-control" id="name" placeholder="Passphrase (If open, write nothing)"> + </div> + </div> + + <div class="form-group row"> + <div class="col-sm-10"> + + <button type="submit" class="btn btn-primary">Save</button> + <button type="submit" class="btn btn-muted">Cancel</button> + <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you save the settings.</span> + </div> + </div> + + </form> + </div> + + </div> + </div> + + <div class="card card-inverse bg-dark text-white mt-3"> + <div class="card-body"> + + <div class="card-title"> + <h3>Version & OTA</h3> + </div> + + <div class="container"> + <form> + <h5>Device type: <b>clapSensor</b></h5> + <h5>Current version: <b>1.0.0</b></h5> + <hr /> + <div class="form-group row"> + <label for="inputEmail3" class="col-sm-2 col-form-label">Update file:</label> + <div class="col-sm-10"> + <input type="file" class="form-control-file" id="exampleFormControlFile1"> + </div> + </div> + + <div class="form-group row"> + <div class="col-sm-10"> + <button type="submit" class="btn btn-danger">Flash new version</button> + <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you flash a new version.</span> + </div> + </div> + + </form> + </div> + + </div> + </div> + </div> + + </body> + + </html> +)====="; @@ -0,0 +1,3 @@ +const char stylesheet[] PROGMEM = R"=====( + body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;background-color:#222}.switch{position:relative;display:inline-block;width:60px;height:34px}.switch input{display:none}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slider:before{position:absolute;content:"";height:26px;width:26px;left:4px;bottom:4px;background-color:#fff;-webkit-transition:.4s;transition:.4s}input:checked+.slider{background-color:#2196F3}input:focus+.slider{box-shadow:0 0 1px #2196F3}input:checked+.slider:before{-webkit-transform:translateX(26px);-ms-transform:translateX(26px);transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.toggle-button{position:relative;display:inline-block;color:#fff}.toggle-button label{display:inline-block;text-transform:uppercase;cursor:pointer;text-align:left}.toggle-button input{display:none}.toggle-button__icon{cursor:pointer;pointer-events:none}.toggle-button__icon:after,.toggle-button__icon:before{content:"";position:absolute;top:45%;left:35%;transition:.2s ease-out}.toggle-button--tuli label{line-height:20px;text-indent:30px}.toggle-button--tuli input[type=checkbox]:checked~.toggle-button__icon{background:#fff}.toggle-button--tuli input[type=checkbox]:checked~.toggle-button__icon:after,.toggle-button--tuli input[type=checkbox]:checked~.toggle-button__icon:before{opacity:1}.toggle-button--tuli .toggle-button__icon{position:absolute;top:0;left:0;width:20px;height:20px;transition:all .2s;border:2px solid #fff;border-radius:1px;box-shadow:0 1px 0 rgba(0,0,0,.1);text-shadow:0 1px 0 rgba(0,0,0,.1)}.toggle-button--tuli .toggle-button__icon:after,.toggle-button--tuli .toggle-button__icon:before{left:2px;width:12px;height:2px;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.1);top:35%;background:#61B136;opacity:0;transform-origin:left center}.toggle-button--tuli .toggle-button__icon:before{transform:translate(0,0) rotate(45deg) scale(.6,1)}.toggle-button--tuli .toggle-button__icon:after{transform:translate(4px,6px) rotate(-45deg)}.toggle-button--tuli:hover input[type=checkbox]:not(:checked)~.toggle-button__icon{box-shadow:0 1px 3px rgba(0,0,0,.2);text-shadow:0 1px 3px rgba(0,0,0,.2)} +)====="; diff --git a/webPage/index.html b/webPage/index.html new file mode 100644 index 0000000..c80ee61 --- /dev/null +++ b/webPage/index.html @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> + + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + + <title>{{NAME}}</title> + + <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> + <link rel="stylesheet" href="./style.css"> + </head> + + + <body> + + <nav class="navbar navbar-dark bg-dark"> + <a class="navbar-brand" href="/">IOT Device</a> + <a class="navbar-toggler navbar-toggler-right" href="/settings"> + ⚙ + </a> + </nav> + + <div class="container-fluid pt-3"> + + <div class="card card-inverse bg-dark text-white"> + <div class="card-body"> + + <div class="card-title"> + <h3> + {{NAME}} + <span class="text-align:right;"> + <label class="switch float-right"> + <input type="checkbox" id="lampActive"> + <span class="slider round"></span> + </label> + </span> + </h3> + </div> + <h6 class="card-subtitle mb-2 text-muted">{{Location}}</h6> + + <hr /> + <div class="toggle-button toggle-button--tuli"> + <input id="sensorActive" type="checkbox"> + <label for="sensorActive">Clap sensor</label> + <div class="toggle-button__icon"></div> + </div> + + </div> + </div> + + </div> + + + <script type="text/javascript"> + var pollingActive = true; // Prevent states from being updated while trying to change them. + document.getElementById("lampActive").addEventListener("change", changLampState); + document.getElementById("sensorActive").addEventListener("change", changSensorState); + setInterval(getStates, 1000); + + function ajax_request(adress, callback_function) { + /* A simple ajax request wrapper + Doesn´t return anything else than the callback */ + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + callback_function(this.responseText); + } + }; + xhttp.open("POST", adress, true); + xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xhttp.send(); + } + + function changLampState() { + var boxState = document.getElementById("lampActive").checked; + if (boxState) { + pollingActive = false; + ajax_request("/j?lamp=1", function(e) { + pollingActive = true; + console.log(e); + }); + } else { + pollingActive = false; + ajax_request("/j?lamp=0", function(e) { + pollingActive = true; + console.log(e); + }); + } + } + + function changSensorState() { + var sensorState = document.getElementById("sensorActive").checked; + if (sensorState) { + pollingActive = false; + ajax_request("/j?sens=1", function(e) { + pollingActive = true; + console.log(e); + }); + } else { + pollingActive = false; + ajax_request("/j?sens=0", function(e) { + pollingActive = true; + console.log(e); + }); + } + } + + function getStates() { + if (pollingActive) { + ajax_request("/j/", function(e) { + result = JSON.parse(e); + + var lampOn = false; + var sensorOn = false; + if (result['lampOn'] == 1) { lampOn = true; } + if (result['sensorOn'] == 1) { sensorOn = true; } + + document.getElementById("lampActive").checked = lampOn; + document.getElementById("sensorActive").checked = sensorOn; + }); + } + } + </script> + </body> + +</html> diff --git a/webPage/settings.html b/webPage/settings.html new file mode 100644 index 0000000..cdda75a --- /dev/null +++ b/webPage/settings.html @@ -0,0 +1,167 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + + <title>{{NAME}}</title> + + <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> + <link rel="stylesheet" href="./style.css"> +</head> + + +<body> + + <nav class="navbar navbar-dark bg-dark"> + <a class="navbar-brand" href="/">IOT Device</a> + <a class="navbar-toggler navbar-toggler-right" href="/settings"> + ⚙ + </a> + </nav> + + <div class="container-fluid pt-3 pb-3"> + + <div id="success-box" class="alert alert-success" role="alert"> + {{SUCCESSMSG}} + </div> + + <div class="alert alert-danger" role="alert"> + <b>Warning:</b> The password-protection does not work yet! Make sure to don´t expose the device. + </div> + + <div class="card card-inverse bg-dark text-white mt-3"> + <div class="card-body"> + + <div class="card-title"> + <h3>Password</h3> + </div> + + <div class="container"> + <form> + + <h5 class="small text-muted">To change any settings, you must enter the correct password"</h5> + <div class="form-group row"> + + <div class="col-sm-12"> + <input type="password" class="form-control" id="" placeholder="Device password" readonly> + </div> + </div> + + </form> + </div> + + </div> + </div> + + <div class="card card-inverse bg-dark text-white mt-3"> + <div class="card-body"> + + <div class="card-title"> + <h3>Settings</h3> + </div> + + <div class="container"> + <form method="post"> + + <input type="hidden" name="type" value="settings"> + + <h5 class="small text-muted">Device settings:</h5> + <div class="form-group row"> + <label for="lblDeviceName" class="col-sm-2 col-form-label">Device name</label> + <div class="col-sm-10"> + <input type="text" class="form-control" name="txtDeviceName" id="txtDeviceName" placeholder="Ex. Rooflight"> + </div> + </div> + + <div class="form-group row"> + <label for="lblDeviceLocation" class="col-sm-2 col-form-label">Location</label> + <div class="col-sm-10"> + <input type="text" class="form-control" name="txtDeviceLocation" id="txtDeviceLocation" placeholder="Ex. Bathroom"> + </div> + </div> + + <h5 class="small text-muted">Wifi settings:</h5> + <div class="form-group row"> + <label for="lblSSID" class="col-sm-2 col-form-label">SSID</label> + <div class="col-sm-10"> + <input type="name" class="form-control" name="txtSSID" id="txtSSID" placeholder="Name of router"> + </div> + </div> + + <div class="form-group row"> + <label for="lblPassword" class="col-sm-2 col-form-label">Pasword</label> + <div class="col-sm-10"> + <input type="name" class="form-control" name="txtPassword" id="txtPassword" placeholder="Passphrase (If open, write nothing)"> + </div> + </div> + + <div class="form-group row"> + <div class="col-sm-10"> + + <button type="submit" class="btn btn-primary">Save</button> + <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you save the settings.</span> + <br /><span class="small text-muted"> <b> Note:</b> Empty or unchanged fields will not be updated.</span> + </div> + </div> + + </form> + </div> + + </div> + </div> + + <div class="card card-inverse bg-dark text-white mt-3"> + <div class="card-body"> + + <div class="card-title"> + <h3>Version & OTA</h3> + </div> + + <div class="container"> + <form> + <h5>Device type: <b>clapSensor</b></h5> + <h5>Current version: <b>1.0.0</b></h5> + <hr /> + <div class="form-group row"> + <label for="inputEmail3" class="col-sm-2 col-form-label">Update file:</label> + <div class="col-sm-10"> + <input type="file" class="form-control-file" id="exampleFormControlFile1"> + </div> + </div> + + <div class="form-group row"> + <div class="col-sm-10"> + <button type="submit" class="btn btn-danger">Flash new version</button> + <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you flash a new version.</span> + </div> + </div> + + </form> + </div> + + </div> + </div> + </div> + + + <script type="text/javascript"> + var successAlert = document.getElementById('success-box'); + + updatePlaceholder("txtDeviceName", "{{DEVICENAME}}"); + updatePlaceholder("txtDeviceLocation", "{{DEVICELOCATION}}");; + + if (successAlert.innerHtml == "") { + successAlert.style.display = "none"; + } + + function updatePlaceholder(elemId, textToInsert) { + var field = document.getElementById(elemId); + if (textToInsert != "") { field.placeholder = "Curr; " + textToInsert } + } + + </script> +</body> + +</html> diff --git a/webPage/style.css b/webPage/style.css new file mode 100644 index 0000000..63663d5 --- /dev/null +++ b/webPage/style.css @@ -0,0 +1,148 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-size: 1rem; + font-weight: normal; + line-height: 1.5; + background-color: #222; +} + +/* SWITCH */ + .switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input {display:none;} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} +/* ./SWITCH */ + +.toggle-button { + position: relative; + display: inline-block; + color: #fff; +} + + .toggle-button label { + display: inline-block; + text-transform: uppercase; + cursor: pointer; + text-align: left; +} + +.toggle-button input { + display: none; +} + + .toggle-button__icon { + cursor: pointer; + pointer-events: none; +} + +.toggle-button__icon:before, .toggle-button__icon:after { + content: ""; + position: absolute; + top: 45%; + left: 35%; + transition: 0.2s ease-out; +} + +.toggle-button--tuli label { + line-height: 20px; + text-indent: 30px; +} + +.toggle-button--tuli input[type=checkbox]:checked ~ .toggle-button__icon { + background: #fff; +} + +.toggle-button--tuli input[type=checkbox]:checked ~ .toggle-button__icon:before, .toggle-button--tuli input[type=checkbox]:checked ~ .toggle-button__icon:after { + opacity: 1; +} + +.toggle-button--tuli .toggle-button__icon { + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + transition: all 0.2s; + border: 2px solid #fff; + border-radius: 1px; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); +} + +.toggle-button--tuli .toggle-button__icon:before, .toggle-button--tuli .toggle-button__icon:after { + top: 5px; + left: 2px; + width: 12px; + height: 2px; + border-radius: 3px; + background: #fff; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); + top: 35%; + background: #61B136; + opacity: 0; + transform-origin: left center; +} + +.toggle-button--tuli .toggle-button__icon:before { + transform: translate(0, 0) rotate(45deg) scale(0.6, 1); +} + +.toggle-button--tuli .toggle-button__icon:after { + transform: translate(4px, 6px) rotate(-45deg); +} + +.toggle-button--tuli:hover input[type=checkbox]:not(:checked) ~ .toggle-button__icon { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} |