diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4e259ca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/
+./node_modules/
+./package-lock.json
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..04e5fe3
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,71 @@
+
+
+
+
+ esp-iot
+
+
+
+
+
+
+
+
+
+
+
+ ESP-IoT
+ LED 1:
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/manifest.json b/public/manifest.json
new file mode 100644
index 0000000..70b9a12
--- /dev/null
+++ b/public/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "ESP IoT",
+ "short_name": "ESP-IoT",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#14181b",
+ "theme_color": "#00BCD4",
+ "orientation": "portrait-primary"
+}
\ No newline at end of file
diff --git a/public/sym/dark.css b/public/sym/dark.css
new file mode 100644
index 0000000..596de62
--- /dev/null
+++ b/public/sym/dark.css
@@ -0,0 +1,250 @@
+html{
+ width: 100%;
+ height: 100%;
+ margin: 0px;
+ background-color: #14181b;
+}
+body{
+ position: relative;
+ width: 100%;
+ padding-top: 20px;
+ padding-bottom: 130px;
+ min-height: calc(100% - 150px);
+ max-width: 70rem;
+ min-width: 25rem;
+ margin: auto;
+ background-color: #1f262b;
+}
+table{
+ position: relative;
+ margin: auto;
+ width: calc(100% - 50px);
+ max-width: 600px;
+ background-color: #1f262b;
+ box-shadow: 3px 3px 10px #333;
+ border: 2px solid #fff;
+ border-collapse: separate;
+ border-spacing: 2px;
+ border-radius: 10px;
+ color: #fff;
+ font-size: 15pt;
+ font-family:"Roboto", regular, sans-serif;
+}
+tr{
+ margin: auto;
+ border: 1px solid #fff;
+}
+th{
+ margin: auto;
+ border: 0px solid #aaa;
+ background-color: #546E7A;
+ padding: 20px;
+ border-radius: 8px;
+}
+td{
+ margin: auto;
+ border: 0px solid #aaa;
+ background-color: #37474F ;
+ padding: 10px;
+ border-radius: 8px;
+}
+td.left,th.left{
+ border-radius: 8px 0px 0px 8px;
+}
+td.right,th.right{
+ border-radius: 0px 8px 8px 0px;
+}
+td.mid,th.mid{
+ border-radius: 0px 0px 0px 0px;
+}
+
+
+div.input-box {
+ position: absolute;
+ left: calc(50% - 14rem);
+ top: calc(50% - 7rem);
+ width: 28rem;
+ height: 14rem;
+ background-color: #14181b00;
+ border-radius: 8px;
+ border: 0px solid #fff;
+}
+div.input-field {
+ position: relative;
+ margin-left: 5%;
+ margin-top: 20px;
+ width: 90%;
+ height: 2.5rem;
+ background-color: #14181b;
+ border-radius: 1.25rem;
+ border: 1px solid #fff;
+}
+.input {
+ position: absolute;
+ left: 1rem;
+ top: calc(50% - 1rem);
+ width: calc(100% - 2rem);
+ height: 2rem;
+ text-align: center;
+ color:#fff;
+ font-size: 15pt;
+ font-family:Arial, "lucida console", sans-serif;
+ border: 0px solid #fff;
+ background-color: transparent;
+ outline: 0 none;
+}
+input[id="longurl-input"]::-webkit-input-placeholder {
+ color: #ffffff;
+}
+.btn-blue{
+ cursor: pointer;
+ height: 2.5rem;
+ background-color: #00BCD4;
+ box-shadow: 3px 3px 10px #333;
+ border-radius: 1.25rem;
+}
+.btn-green{
+ cursor: pointer;
+ height: 2.5rem;
+ background-color: #009688;
+ box-shadow: 3px 3px 10px #333;
+ border-radius: 1.25rem;
+}
+.btn-red{
+ cursor: pointer;
+ height: 2.5rem;
+ background-color: #E53935;
+ box-shadow: 3px 3px 10px #333;
+ border-radius: 1.25rem;
+}
+.btn-text {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 1.4rem;
+ color:#fff;
+ font-family:Arial, "lucida console", sans-serif;
+}
+.sym_btn-red{
+ cursor: pointer;
+ height: 2.5rem;
+ width: 2.5rem;
+ background-color: #E53935;
+ border-radius: 1.25rem;
+ box-shadow: 3px 3px 10px #333;
+ float: left;
+}
+.sym_btn-blue{
+ cursor: pointer;
+ height: 2.5rem;
+ width: 2.5rem;
+ background-color: #1E88E5;
+ border-radius: 1.25rem;
+ box-shadow: 3px 3px 10px #333;
+ float: left;
+}
+.sym_btn-invisible{
+ cursor: pointer;
+ height: 2.5rem;
+ width: 2.5rem;
+ border-radius: 1.25rem;
+ float: left;
+}
+img.icon {
+ margin-top: 8px;
+ margin-left: 8px;
+}
+.title {
+ text-align: center;
+ width: 100%;
+ color:#fff;
+ font-size: 30pt;
+ font-family:"Roboto", bold, sans-serif;
+}
+.title2 {
+ margin-top: 20px;
+ text-align: center;
+ width: 100%;
+ color:#fff;
+ font-size: 20pt;
+ font-family:"Roboto", bold, sans-serif;
+}
+.box-dark{
+ position: relative;
+ margin: auto;
+ width: calc(100% - 50px);
+ max-width: 600px;
+ background-color: #1f262b;
+ box-shadow: 3px 3px 10px #333;
+ border: 2px solid #fff;
+ border-radius: 10px;
+ color: #fff;
+ font-size: 15pt;
+ font-family:"Roboto", regular, sans-serif;
+}
+.error{
+ position: absolute;
+ bottom: 1rem;
+ left: 1rem;
+ height: 10rem;
+ width: 16rem;
+ background-color: #E53935;
+ border-radius: 15px;
+}
+
+
+#short-btn {
+ position: absolute;
+ width: 10rem;
+ margin-left: calc(50% - 5rem);
+ margin-top: 20px;
+}
+#copy-btn {
+ position: absolute;
+ width: 10rem;
+ margin-left: calc(50% - 5rem);
+ margin-top: 1rem;
+}
+#surl-popup{
+ position: absolute;
+ width: 30rem;
+ height: 14rem;
+ left: calc(50% - 15rem);
+ top: calc(50% - 7rem);
+ display: none;
+ animation: slide-from-top alternate 0.4s;
+}
+#error-box{
+ display: none;
+}
+
+
+@keyframes slide-from-top{
+ from{top:20%;opacity: 0}
+ to{top:calc(50% - 7rem);opacity: 1}
+}
+@keyframes slide-from-left{
+ from{left:-30rem;opacity: 0}
+ to{left:1rem;opacity: 1}
+}
+
+
+
+@media (max-width: 35rem){
+ div.input-box {
+ left: 5%;
+ width: 90%;
+ }
+ .error{
+ bottom: 1rem;
+ left: 1rem;
+ height: 8rem;
+ width: calc(100% - 2rem);
+ }
+ #surl-popup{
+ width: calc(100% - 2rem);
+ height: 14rem;
+ left: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/public/sym/ic_close_white_24px.svg b/public/sym/ic_close_white_24px.svg
new file mode 100644
index 0000000..5704800
--- /dev/null
+++ b/public/sym/ic_close_white_24px.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/public/sym/script.js b/public/sym/script.js
new file mode 100644
index 0000000..445706e
--- /dev/null
+++ b/public/sym/script.js
@@ -0,0 +1,32 @@
+function selected(id){
+ var element = document.getElementById(id);
+ element.style.boxShadow = '1px 1px 2px #999';
+ element.style.backgroundColor = "#ffffff";
+
+ var element = document.getElementById(id+'-input');
+ element.style.color = "#000000";
+}
+function deselected(id){
+ var element = document.getElementById(id);
+ element.style.boxShadow = '0px 0px 0px #999';
+ element.style.backgroundColor = "#14181b";
+
+ var element = document.getElementById(id+'-input');
+ element.style.color = "#ffffff";
+}
+
+function visible(id, status){
+ if(status == "on"){
+ document.getElementById(id).style.display = 'block';
+ }
+ else{
+ document.getElementById(id).style.display = 'none';
+ }
+}
+
+function session_end() {
+ if (confirm("Session timed out! Please login.")) {
+ window.location.replace('https://adb.sh/robo-remote/login.php');
+ } else {
+ }
+}
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..4325633
--- /dev/null
+++ b/server.js
@@ -0,0 +1,113 @@
+const http = require('http')
+const url = require('url')
+const fs = require('fs')
+const ws = require('ws')
+const redis = require('redis')
+const mime = require('mime')
+const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest
+
+const host = 'http://127.0.0.1:8080/'
+const outpath = ['sym', '', 'manifest.json', 'admin', 'stats', 'analytics']
+
+let led1_status = 'off';
+
+//redis client
+const redis_cli = redis.createClient({
+ host: '127.0.0.1',
+ port: 6379
+})
+redis_cli.on("error", function (error) {
+ console.error(error)
+})
+
+//HTTP server
+http.createServer(function (req, res) {
+ const q = url.parse(req.url, true);
+ let filename = "./public" + q.pathname;
+ let path_split = q.pathname.split("/", 3);
+ if (path_split[path_split.length - 1] === "") filename += "/index.html";
+ let file_type = mime.getType(filename)
+ if (path_split[1] === "connect") {
+ res.writeHead(200, {'Content-Type': file_type});
+ res.write("");
+ return res.end();
+ }
+ fs.readFile(filename, function(err, data) {
+ if (err) {
+ res.writeHead(404, {'Content-Type': "text/html"});
+ return res.end("404 Not Found");
+ }
+ res.writeHead(200, {'Content-Type': file_type});
+ res.write(data);
+ return res.end();
+ })
+}).listen(8080);
+
+//WS server
+const wss = new ws.Server({
+ port: 8081,
+ perMessageDeflate: {
+ zlibDeflateOptions: {
+ // See zlib defaults.
+ chunkSize: 1024,
+ memLevel: 7,
+ level: 3
+ },
+ zlibInflateOptions: {
+ chunkSize: 10 * 1024
+ },
+ // Other options settable:
+ clientNoContextTakeover: true, // Defaults to negotiated value.
+ serverNoContextTakeover: true, // Defaults to negotiated value.
+ serverMaxWindowBits: 10, // Defaults to negotiated value.
+ // Below options specified as default values.
+ concurrencyLimit: 10, // Limits zlib concurrency for perf.
+ threshold: 1024 // Size (in bytes) below which messages
+ // should not be compressed.
+ }
+});
+
+//WS handler
+wss.on('connection', ws => {
+ ws.on('message', message => {
+ console.log(`Received message => ${message}`)
+ let msg = message.split(";", 2)
+ if (msg[0] === 'led1') {
+ if (msg[1] === '') ws.send('error;try again')
+ else if (msg[1] === 'on' || msg[1] === "off"){
+ let xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ wss.clients.forEach(clients => {
+ clients.send(message)
+ })
+ led1_status = msg[1]
+ }
+ }
+ xhr.open('GET', `http://192.168.1.210/${msg[1]}`, true);
+ xhr.timeout = 1000;
+ xhr.ontimeout = function(e) {ws.send('error;device "led1" is offline')}
+ xhr.send();
+ setTimeout(to => {if (xhr.readyState !== 4) ws.send('error;device "led1" took too long to reach');xhr.abort()}, 1500)
+ }else ws.send('error;try again')
+ }
+ })
+ ws.send('websocket connected')
+ ws.send(`led1;${led1_status}`)
+})
+
+//random key
+function get_key(length) {
+ let forbidden = false; let output = ''
+ let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ do{
+ for (let i = 0; i < length; i++ )
+ output += characters.charAt(Math.floor(Math.random() * characters.length))
+ for (let i = 0; i < output.length; i++) if (output === outpath[i]) forbidden = true
+ redis_cli.hget("surl;"+output, "url", function(err, obj) {
+ if(err) console.log(err)
+ if(obj) forbidden = true
+ })
+ } while (forbidden)
+ return output
+}