master
Alban 4 years ago
parent 8d25fbdfb4
commit 66141472e1

3
.gitignore vendored

@ -0,0 +1,3 @@
.idea/
./node_modules/
./package-lock.json

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>esp-iot</title>
<!-- PWA support -->
<link rel="manifest" href="/manifest.json"/>
<meta name="theme-color" content="#2F3BA2"/>
<meta name="description" content="short your url"/>
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0'/>
<link rel="stylesheet" type="text/css" href="/sym/dark.css"/>
<script>
const wsurl = 'ws://192.168.1.101:8081'
const socket = new WebSocket(wsurl)
socket.onopen = () => {
socket.send('new session')
}
socket.onerror = (error) => {
console.log(`WebSocket error: ${error}`)
}
socket.onclose = error => show_error('session timed out (refresh)')
socket.onmessage = (e) => {
console.log(e.data)
let msg = e.data.split(";", 2)
if (msg[0] === 'error') show_error(msg[1])
else if (msg[0] === 'led1') {
document.getElementById('led1_title').innerText = `LED 1 (currently ${msg[1]}):`
}
}
function copy_url() {
document.getElementById('surl-input').select()
document.execCommand("copy")
}
function lurl_submit() {
socket.send('long_url;'+document.getElementById('longurl-input').value)
}
function show_error(msg) {
let error_style = document.getElementById('error-box').style
document.getElementById('error-message').innerText = msg
error_style.display = "block"
error_style.animation = "slide-from-left alternate 0.2s"
setTimeout(to => {error_style.animation = ""}, 200)
}
</script>
</head>
<body>
<div class="title">ESP-IoT</div>
<div class="title2" id="led1_title">LED 1:</div>
<div class="btn-green"
style="width: 10rem; position:relative; margin-top: 1rem; margin-left: calc(50% - 5rem);"
onclick="socket.send('led1;on')">
<div class="btn-text">on</div>
</div>
<div class="btn-red"
style="width: 10rem; position:relative; margin-top: 1rem; margin-left: calc(50% - 5rem);"
onclick="socket.send('led1;off')">
<div class="btn-text">off</div>
</div>
<div id="error-box" class="error">
<div onclick="this.parentNode.style.display = 'none'" style="position: absolute; top:5px; right: 5px;" class="sym_btn-invisible">
<img class="icon" src="./sym/ic_close_white_24px.svg">
</div>
<div id="error-message" class="btn-text">
</div>
</div>
</body>
</html>

@ -0,0 +1,9 @@
{
"name": "ESP IoT",
"short_name": "ESP-IoT",
"start_url": "/",
"display": "standalone",
"background_color": "#14181b",
"theme_color": "#00BCD4",
"orientation": "portrait-primary"
}

@ -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;
}
}

@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 265 B

@ -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 {
}
}

@ -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
}
Loading…
Cancel
Save