Major overhaul

refactored frontend source files
added frontend build script
minor code optimizations
This commit is contained in:
Rohan Sircar 2020-08-02 15:55:48 +05:30
parent bda66ea315
commit 068703999a
73 changed files with 1336 additions and 40 deletions

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ website-source-copy
website-zipped
website-zipped_bak
.vscode
target
/data/
/data2/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +0,0 @@
{
"shour1": 16,
"fadePeriod1": 30,
"smin1": 0,
"ehour": 22,
"emin": 0
}

View File

@ -1,7 +0,0 @@
{
"shour2": 16,
"fadePeriod2": 30,
"smin2": 0,
"ehour": 22,
"emin": 0
}

View File

@ -1,7 +0,0 @@
{
"shour3": 16,
"fadePeriod3": 30,
"smin3": 0,
"ehour": 22,
"emin": 0
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
frontend-build.sh Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
rm -r frontend/target/
rm -r data/
rsync -avz --progress frontend/src/** frontend/target/
cd frontend/target/
find . \( \
-name '*.css' \
-o -name '*.html' \
-o -name '*.js' \
-o -name '*.jpg' \
-o -name '*.png' \
-o -name '*.ico' \
\) -exec pigz --force --verbose {} \;
cd ../..
rsync -avz --progress frontend/target/** data/

159
frontend/src/colors.css Normal file
View File

@ -0,0 +1,159 @@
/*------------------------------------
- COLOR ternary
------------------------------------*/
.alert-ternary {
color: #001b1e;
background-color: #65edff;
border-color: #51eaff;
}
.alert-ternary hr {
border-top-color: #38e7ff;
}
.alert-ternary .alert-link {
color: #000000;
}
.badge-ternary {
color: #fff;
background-color: #008799;
}
.badge-ternary[href]:hover,
.badge-ternary[href]:focus {
color: #fff;
background-color: #005965;
}
.bg-ternary {
background-color: #008799 !important;
}
a.bg-ternary:hover,
a.bg-ternary:focus,
button.bg-ternary:hover,
button.bg-ternary:focus {
background-color: #005965 !important;
}
.border-ternary {
border-color: #008799 !important;
}
.btn-ternary {
color: #fff;
background-color: #008799;
border-color: #008799;
}
.btn-ternary:hover {
color: #fff;
background-color: #006775;
border-color: #005965;
}
.btn-ternary:focus,
.btn-ternary.focus {
box-shadow: 0 0 0 0.2rem rgba(0, 135, 153, 0.5);
}
.btn-ternary.disabled,
.btn-ternary:disabled {
color: #fff;
background-color: #008799;
border-color: #008799;
}
.btn-ternary:not(:disabled):not(.disabled):active,
.btn-ternary:not(:disabled):not(.disabled).active,
.show>.btn-ternary.dropdown-toggle {
color: #fff;
background-color: #005965;
border-color: #004c56;
}
.btn-ternary:not(:disabled):not(.disabled):active:focus,
.btn-ternary:not(:disabled):not(.disabled).active:focus,
.show>.btn-ternary.dropdown-toggle:focus {
box-shadow: 0 0 0 0.2rem rgba(0, 135, 153, 0.5);
}
.btn-outline-ternary {
color: #008799;
background-color: transparent;
border-color: #008799;
}
.btn-outline-ternary:hover {
color: #fff;
background-color: #008799;
border-color: #008799;
}
.btn-outline-ternary:focus,
.btn-outline-ternary.focus {
box-shadow: 0 0 0 0.2rem rgba(0, 135, 153, 0.5);
}
.btn-outline-ternary.disabled,
.btn-outline-ternary:disabled {
color: #008799;
background-color: transparent;
}
.btn-outline-ternary:not(:disabled):not(.disabled):active,
.btn-outline-ternary:not(:disabled):not(.disabled).active,
.show>.btn-outline-ternary.dropdown-toggle {
color: #fff;
background-color: #008799;
border-color: #008799;
}
.btn-outline-ternary:not(:disabled):not(.disabled):active:focus,
.btn-outline-ternary:not(:disabled):not(.disabled).active:focus,
.show>.btn-outline-ternary.dropdown-toggle:focus {
box-shadow: 0 0 0 0.2rem rgba(0, 135, 153, 0.5);
}
.list-group-item-ternary {
color: #001b1e;
background-color: #51eaff;
}
.list-group-item-ternary.list-group-item-action:hover,
.list-group-item-ternary.list-group-item-action:focus {
color: #001b1e;
background-color: #38e7ff;
}
.list-group-item-ternary.list-group-item-action.active {
color: #fff;
background-color: #001b1e;
border-color: #001b1e;
}
.table-ternary,
.table-ternary>th,
.table-ternary>td {
background-color: #51eaff;
}
.table-hover .table-ternary:hover {
background-color: #38e7ff;
}
.table-hover .table-ternary:hover>td,
.table-hover .table-ternary:hover>th {
background-color: #38e7ff;
}
.text-ternary {
color: #008799 !important;
}
a.text-ternary:hover,
a.text-ternary:focus {
color: #005965 !important;
}

61
frontend/src/edit.html Normal file
View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<title>ESP8266 SPIFFS File Upload</title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<meta charset="utf-8">
<script src="loadMenu.js" defer></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.15.0/esm/popper.js" type="module"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sprintf/1.1.2/sprintf.min.js"></script>
<link href='main.css' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="colors.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-filestyle/2.1.0/bootstrap-filestyle.min.js"></script>
<script src="uploadButtonStyle.js" defer></script>
</head>
<body>
<div id="navigation"></div>
<div class="container shadow-lg">
<section class="py-5 mt-3">
<div class="container">
<div class="row">
<div class="col">
<h1 class="display-4 text-ternary">ESP8266 SPIFFS File Upload</h1>
<div class="row">
<div class="col-md-6 offset-md-3">
<p>Select a new file to upload to the ESP8266. Existing files will be replaced.</p>
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<input class="filestyle" data-btnClass="btn-ternary" type="file" data-dragdrop="true" name="data" id="upload-button" required>
<p class="form-text text-muted">You can drag and drop files as well</p>
</div>
<div class="form-group text-center">
<input class="btn btn-ternary" type="submit" value="Upload">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</body>
</html>

109
frontend/src/index.html Executable file
View File

@ -0,0 +1,109 @@
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<meta charset="utf-8">
<script src="loadMenu.js" defer></script>
<script src="main.js" defer></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.15.0/esm/popper.js" type="module"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sprintf/1.1.2/sprintf.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pretty-checkbox@3.0/dist/pretty-checkbox.min.css">
<link href='main.css' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="colors.css">
</head>
<body>
<!-- <iframe src="menu.html" width="90%"></iframe> -->
<div id="navigation"></div>
<div class="container">
<div class="shadow-lg p-3 mb-5 bg-white rounded">
<header>
<div class="container">
<div class="row">
<div class="col">
<div class="pt-5 mt-3">
<h2 class="display-4 text-ternary text-center"> Welcome </h2>
<p class="text-center">Welcome to the planted aquarium controller.</p>
</div>
</div>
</div>
</div>
</header>
<section>
<div class="container bg-ternary shadow-sm p-3 mb-2 rounded">
<div class="row mb-3">
<div class="col">
<h2 class="text-white text-center display-4">Settings at a glance</h2>
</div>
</div>
<div class="row" id="content-row">
<div class="col-md">
<section id="mySection "></section>
<div class="card mb-3 text-center card-form rounded mx-auto shadow-sm ">
<h2 class="card-header text-center text-ternary">Ballast #1</h2>
<div class="card-body rounded">
<div class="card-text" id="ballast1">
</div>
</div>
</div>
</div>
<div class="col-md">
<div class="card mb-3 text-center card-form rounded mx-auto shadow-sm">
<h2 class="card-header text-center text-ternary">Ballast #2</h2>
<div class="card-body rounded">
<div class="card-text" id="ballast2">
</div>
</div>
</div>
</div>
<div class="col-md">
<div class="card mb-3 text-center card-form rounded mx-auto shadow-sm">
<h2 class="card-header text-center text-ternary">Ballast #3</h2>
<div class="card-body rounded">
<div class="card-text" id="ballast3">
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section>
<div class="container">
<div class="row">
<div class="col">
<div class="pretty p-switch ">
<input type="checkbox" id="temperatures-switch" />
<div class="state p-primary">
<label>Show temperature readings</label>
</div>
</div>
<p id="temperature"></p>
<noscript>No javascript</noscript>
</div>
</div>
</div>
</section>
</div>
</div>
</body>
</html>

39
frontend/src/ledcontrol.html Executable file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>LED Control</title>
<link href='main.css' rel='stylesheet' type='text/css'>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<script src="WebSocket.js" type="text/javascript"></script>
</head>
<body>
<center>
<header>
<h1>LED Control</h1>
</header>
<div>
<table>
<tr>
<td style="width:14.4px; text-align: right">R: </td>
<td><input class="enabled" id="r" type="range" min="0" max="1023" step="1" oninput="sendRGB();" value="0"></td>
</tr>
<tr>
<td style="width:14.4px; text-align: right">G: </td>
<td><input class="enabled" id="g" type="range" min="0" max="1023" step="1" oninput="sendRGB();" value="0"></td>
</tr>
<tr>
<td style="width:14.4px; text-align: right">B: </td>
<td><input class="enabled" id="b" type="range" min="0" max="1023" step="1" oninput="sendRGB();" value="0"></td>
</tr>
</table>
<p style="margin:8px 0px"><button id="rainbow" class="button" style="background-color:#999" onclick="rainbowEffect();">Rainbow</button></p>
</div>
</center>
</body>
</html>

3
frontend/src/loadMenu.js Normal file
View File

@ -0,0 +1,3 @@
$(document).ready(function() {
$('#navigation').load('menu.html');
});

29
frontend/src/main.css Normal file
View File

@ -0,0 +1,29 @@
/* .navbar-light .nav-item.active .nav-link,
.navbar-light .nav-item:focus .nav-link,
.navbar-light .nav-item:hover .nav-link {
color: #ffffff;
} */
.navbar-dark .navbar-nav .nav-link {
color: rgba(255, 255, 255, .5);
color: #ffffff;
}
/* #form-section {
width: 30%;
margin: auto;
}
#manual-card {
width: 40%;
} */
@media only screen and (max-width: 900px) {
/* #form-section {
width: 90%;
}
#manual-card {
width: 90%;
} */
}

177
frontend/src/main.js Normal file
View File

@ -0,0 +1,177 @@
function incHeight() {
var el = document.getElementById('mySection');
var height = el.offsetHeight;
console.log("Height = " + height);
var newHeight = height + 250;
el.style.height = newHeight + 'px';
}
var header = document.querySelector('header');
var section = document.querySelector('section');
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
async function getData() {
//await the response of the fetch call
let response = await fetch('combinedSettingsFile.txt');
//proceed once the first promise is resolved.
let data = await response.json()
//proceed only when the second promise is resolved
return data;
}
async function getTemperatures() {
//await the response of the fetch call
let response = await fetch('temperatures.txt');
//proceed once the first promise is resolved.
let data = await response.json()
//proceed only when the second promise is resolved
return data;
}
async function getTemperatures2() {
//await the response of the fetch call
let response = await fetch('api/get/temperatures');
//proceed once the first promise is resolved.
let data = await response.json()
//proceed only when the second promise is resolved
return data;
}
getTemperatures()
.then(data => {
data.temperatures.forEach(function(temperature) {
console.log(temperature);
})
})
// .then(result => {
// //Here body is not ready yet, throw promise
// if (!result.ok) throw result;
// return result.json();
// })
// .then(result => {
// //Successful request processing
// console.log(result);
// }).catch(error => {
// //Here is still promise
// console.log(error);
// error.json().then((body) => {
// //Here is already the payload from API
// console.log(body);
// });
// })
setInterval(function() {
let temperaturesSwitch = document.getElementById('temperatures-switch');
if (temperaturesSwitch.checked) {
// getTemperatures2()
fetch('api/get/temperatures')
// .then(handleErrors)
.then(result => {
//Here body is not ready yet, throw promise
if (!result.ok) throw result;
return result.json();
})
.then(data => {
let p = document.getElementById('temperature');
if (data.status == 'failed') {
let errorMessage = sprintf('status = %s reason = %s', data.status, data.message)
p.textContent = errorMessage;
console.log(errorMessage);
} else {
p.textContent = "";
data.temperatures.forEach(function(temperature) {
console.log("Temperature = " + temperature);
temp = sprintf("%05.2f", temperature);
p.textContent += temp + ' ';
})
// let temp = sprintf("%.2f", 36.72);
// p.textContent += temp + ' ';
}
})
.catch(error => {
//Here is still promise
// console.log(error);
// if (error.status == 404) {
// console.log('error = ' + 404);
// }
// else {
// error.json().then((body) => {
//Here is already the payload from API
// let errorMessage = sprintf('status = %s reason = %s', body.status, body.message)
// console.log(errorMessage);
// });
// }
let p = document.getElementById('temperature');
p.textContent = 'Network Error';
})
}
},
2000
);
//call getData function
getData()
//.then(data => console.log(data))//log the data
.then(function showSettings(data) {
var ballasts = data['settings'];
console.log(ballasts);
for (let i = 0; i < 3; i++) {
// var myArticle = document.createElement('article');
// var myH2 = document.createElement('h3');
// var myPara1 = document.createElement('p');
// var myPara2 = document.createElement('p');
// var myPara3 = document.createElement('p');
// var myPara4 = document.createElement('p');
// var myPara5 = document.createElement('p');
// // var myList = document.createElement('ul');
// myH2.textContent = 'Ballast' + ' #' + (i + 1);
// myPara1.textContent = 'Start Hour: ' + ballasts[i].shour;
// myPara2.textContent = 'Start Minute: ' + ballasts[i].smin;
// myPara3.textContent = 'Fade Period: ' + ballasts[i].fadePeriod;
// myPara4.textContent = 'End Hour: ' + ballasts[i].ehour;
// myPara5.textContent = 'End Minute: ' + ballasts[i].emin;
// console.log(ballasts[i].shour);
// myArticle.appendChild(myH2);
// myArticle.appendChild(myPara1);
// myArticle.appendChild(myPara2);
// myArticle.appendChild(myPara3);
// myArticle.appendChild(myPara4);
// myArticle.appendChild(myPara5);
// // myArticle.appendChild(myList);
// section.appendChild(myArticle);
let myDiv = document.getElementById('ballast' + (i + 1));
// let myH2 = document.createElement('h3');
let myPara1 = document.createElement('p');
let myPara2 = document.createElement('p');
let myPara3 = document.createElement('p');
let myPara4 = document.createElement('p');
let myPara5 = document.createElement('p');
// myH2.textContent = 'Ballast' + ' #' + (i + 1);
myPara1.textContent = 'Start Hour: ' + ballasts[i].shour;
myPara2.textContent = 'Start Minute: ' + ballasts[i].smin;
myPara3.textContent = 'Fade Period: ' + ballasts[i].fadePeriod;
myPara4.textContent = 'End Hour: ' + ballasts[i].ehour;
myPara5.textContent = 'End Minute: ' + ballasts[i].emin;
// myDiv.appendChild(myH2);
myDiv.appendChild(myPara1);
myDiv.appendChild(myPara2);
myDiv.appendChild(myPara3);
myDiv.appendChild(myPara4);
myDiv.appendChild(myPara5);
}
return data;
})
.then(data => console.log(data))
// .then(incHeight());

186
frontend/src/main_bak.css Executable file
View File

@ -0,0 +1,186 @@
.nav-pills .nav-link.active,
.nav-pills .show>.nav-link {
color: #fff;
background-color: #00878F;
}
@font-face {
font-family: 'nunitoregular';
src: url('nunito-regular-webfont.woff2') format('woff2'), url('nunito-regular-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
iframe {
border: none;
align-self: center;
align-content: center;
align-items: center;
}
center {
width: 70%;
max-width: 100%;
margin: 0px auto;
font-family: 'Roboto', sans-serif;
color: #444;
}
header {
background-color: #00878F;
color: white;
padding: 6px;
font-family: 'Roboto', sans-serif;
box-shadow: 1px 1px 5px #555555;
position: relative;
}
h1 {
margin: 0px;
font-family: 'Roboto', sans-serif;
font-size: 32;
}
h2 {
color: #00878f;
}
h3 {
color: #00878f;
}
.shadow {
box-shadow: 1px 1px 5px #555555;
/* height: 500px; */
}
.navmenu {
margin-top: 10px;
margin-left: 0px;
}
.navm {
margin: 2px;
}
radio {
float: left;
}
.content {
margin: 10px 10px 10px 10px
}
table {
margin: 5px 24px 5px 12px;
border: #000000
}
.tbl {
margin-top: 10px;
margin-bottom: 5px;
border: #000000
}
td {
color: #555;
}
/* tr {
width: 100%;
} */
.button {
text-decoration: none;
border: none;
color: white;
background-color: #00878F;
padding: 6px 24px;
font-size: 16px;
cursor: pointer;
box-shadow: 1px 1px 6px #555;
outline: none;
margin: 12px auto 0px auto;
display: inline-block;
}
.button:hover {
box-shadow: 1px 1px 3px #444;
}
hr {
width: 97%;
margin-right: 10px;
}
input[type=range] {
-webkit-appearance: none;
outline: none;
width: auto;
margin: 6px;
}
input {
width: 95px;
height: 20px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
border: 0px;
height: 15px;
width: 15px;
margin-top: -6px;
border-radius: 7.5px;
}
input.disabled[type=range]::-webkit-slider-thumb {
background: #999;
}
input.enabled[type=range]::-webkit-slider-thumb {
background: #00878F;
cursor: pointer;
box-shadow: 1px 1px 2px #777, 0px 0px 1px #777;
}
input[type=range]::-webkit-slider-runnable-track {
width: inherit;
height: 4px;
background: #ccc;
border-radius: 1px;
}
input.enabled[type=range]::-webkit-slider-runnable-track {
cursor: pointer;
}
p {
font-family: 'Nunito', sans-serif;
}
/* section styles */
section article {
width: 33%;
float: left;
}
section p {
margin: 5px 0;
}
section ul {
margin-top: 0;
}
@media screen and (max-width: 900px) {
center {
width: 90%;
}
.content {
height: 600px;
}
}

11
frontend/src/manifest.json Executable file
View File

@ -0,0 +1,11 @@
{
"name": "LED Control",
"icons": [
{
"src": "\/favicon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
}
]
}

122
frontend/src/manualMode-ajax.html Executable file
View File

@ -0,0 +1,122 @@
<!-- <!DOCTYPE html> -->
<html>
<head>
<title>Manual Mode</title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<meta charset="utf-8">
<script src="loadMenu.js" defer></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.15.0/esm/popper.js" type="module"></script>
<link href='main.css' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="colors.css">
</head>
<body>
<div id="navigation"></div>
<div class="container">
<div class="shadow-lg p-3 mb-5 bg-white rounded">
<header>
<div class="container">
<div class="row">
<div class="col">
<div class="mt-3 pt-5">
<h2 class="display-4 text-center">
Manual Settings
</h2>
</div>
</div>
</div>
</div>
</header>
<section>
<div class="container">
<div class="row">
<div class="col-lg-6 offset-lg-3">
<div class="card text-white bg-ternary mb-3 card-form rounded mx-auto" id="manual-card">
<div class="card-body">
<div class="card-text text-center">
<form id="manual-form">
<div class="mb-4 mt-2">
<div class="form-check form-check-inline">
<input class="form-check-input" id="yes" type="radio" name="manual" value="True">
<label class="form-check-label" for="yes">Yes</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" id="no" type="radio" name="manual" value="False" checked>
<label class="form-check-label" for="no">No</label>
</div>
</div>
<div class="form-group">
<button class="form-control" class="button">Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
<script type="text/javascript">
var myForm = document.getElementById('manual-form');
myForm.addEventListener('submit', function(e) {
e.preventDefault();
//var data = JSON.stringify(formData(myForm));
loadJSON(formData(myForm));
})
function formData(form) {
// var el = form.querySelectorAll('input[type="text"]');
// var manualRadios = document.getElementsByName('manual').value;
var manualValue = document.querySelector('input[name="manual"]:checked').value;;
var myData = '';
myData += 'manual' + '=' + manualValue;
// for (var x = 0; x < el.length; x++) {
// var name = el[x].name;
// var value = el[x].value;
// //myData[name] = value;
// myData += name + '=' + value + '&'
// }
// return myData.slice(0, -1);
return myData;
}
function loadJSON(data) {
const url = 'manualMode.html';
console.log(data)
const myData = data
fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: myData
})
// .then(function (response) {
// return response.json()
// }).then(function (data) {
// console.log(data)
// })
.catch(error => console.log(error))
}
</script>
</body>
</html>

78
frontend/src/manualMode.html Executable file
View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<title>Manual Mode</title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<!-- <script src="include.js" type="text/javascript"></script> -->
<meta charset="utf-8">
<!-- <script src="jquery-3.4.1.min.js"></script> -->
<script src="loadMenu.js" defer></script>
<link href='main.css' rel='stylesheet' type='text/css'>
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="colors.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<!-- <link rel="stylesheet" href="bootstrap.min.css"> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<!-- <script src="bootstrap.min.js"></script> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<link href='main.css' rel='stylesheet' type='text/css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.15.0/esm/popper.js" type="module"></script>
</head>
<body>
<div id="navigation"></div>
<center>
<header>
<h1> Manual Mode </h1>
</header>
</center>
<form action="manualMode.html" method="GET">
<center>
<div class="shadow">
<!-- <iframe src="menu.html" width="90%"></iframe> -->
<table class="tbl">
<tr>
<td colspan="3">
<label>Manual mode?</label>
</td>
</tr>
<tr>
<td>
<input type="radio" name="manual" value="True"> Yes
</td>
<td>
<input type="radio" name="manual" value="False" checked> No
</td>
</tr>
<tr>
<td colspan="2">
<p style="margin:8px 0px" align="center"><button class="button">Submit</button></p>
</td>
</tr>
</table>
<!-- <div class="content">
<label>Manual mode?</label>
<input type="radio" name="manual" value="True"> Yes
<input type="radio" name="manual" value="False" checked> No <br>
<p style="margin:8px 0px"><button class="button">Submit</button></p><br>
</div> -->
</div>
</center>
</form>
</body>
</html>

26
frontend/src/menu.html Executable file
View File

@ -0,0 +1,26 @@
<nav class="navbar navbar-expand-md bg-ternary navbar-dark fixed-top">
<div class="container">
<a href="home.html" class="navbar-brand">Aquarium Controller</a>
<div class="navbar-header">
<button class="navbar-toggler" data-toggle="collapse" data-target="#navbarCollapse">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a href="index.html" class="nav-link">Home</a>
</li>
<li class="nav-item">
<a href="settings-ajax.html" class="nav-link">Settings</a>
</li>
<li class="nav-item">
<a href="manualMode-ajax.html" class="nav-link">Manual Mode</a>
</li>
<li class="nav-item">
<a href="edit.html" class="nav-link">Edit</a>
</li>
</ul>
</div>
</div>
</nav>

88
frontend/src/settings-ajax.html Executable file
View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html>
<head>
<title>Settings</title>
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="favicon.ico">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<meta charset="utf-8">
<script src="loadMenu.js" defer></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<link href='main.css' rel='stylesheet' type='text/css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.15.0/esm/popper.js" type="module"></script>
<script src="settings.js" defer></script>
<link rel="stylesheet" href="colors.css">
</head>
<body>
<div id="navigation"></div>
<section id="form-section">
<div class="container">
<div class="row">
<div class="col-lg-6 offset-lg-3">
<div class="py-5 mt-3">
<!-- <div class="bg-ternary rounded"> -->
<div class="card text-white bg-ternary card-form rounded mx-auto shadow-lg p-3 mb-5">
<h2 class="card-header text-center">Change Settings</h2>
<div class="card-body rounded">
<form id="myForm">
<div class="form-group">
<label>Select Ballast:</label>
<select class="form-control" id="myBallasts" name="ballast">
<option value = "1">Ballast #1</option>
<option value = "2">Ballast #2</option>
<option value = "3">Ballast #3</option>
</select>
</div>
<div class="form-group">
<label for="">Enter start hour:</label>
<input class="form-control" type="text" name="shour" placeholder="Hour" required>
</div>
<div class="form-group">
<label for="">Enter start minute:</label>
<input class="form-control" type="text" name="smin" placeholder="Minute" required>
</div>
<div class="form-group">
<label for="">Enter amount of time to fade in:</label>
<input class="form-control" type="text" name="fadePeriod" placeholder="Fade Period" required>
<small class="form-text text-white">Must be a whole number</small>
</div>
<div class="form-group">
<label for="">Enter end hour:</label>
<input class="form-control" type="text" name="ehour" placeholder="Hour" required>
</div>
<div class="form-group">
<label for="">Enter start minute:</label>
<input class="form-control" type="text" name="emin" placeholder="Minute" required>
</div>
<div class="form-group">
<button class="button form-control">Submit</button>
</div>
</form>
</div>
<!-- </div> -->
</div>
</div>
</div>
</div>
</div>
</section>
<script type="text/javascript">
</script>
</body>
</html>

View File

@ -0,0 +1,23 @@
<html>
<head>
<title>Settings update successful</title>
<link href='main.css' rel='stylesheet' type='text/css'>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
</head>
<body>
<center>
<header>
<h1>Success</h1>
</header>
<div>
<p>Settings changed successfully.</p>
<a class="button" href="/">Back</a>
</div>
</center>
</body>
</html>

88
frontend/src/settings.html Executable file
View File

@ -0,0 +1,88 @@
<!-- <!DOCTYPE html> -->
<html>
<head>
<title>Settings</title>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
<!-- <script src="include.js" type="text/javascript"></script> -->
<meta charset="utf-8">
<!-- <script src="jquery-3.4.1.min.js"></script> -->
<link href='main.css' rel='stylesheet' type='text/css'>
<!-- <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> -->
</head>
<body>
<center><header><h1> Settings page </h1></header></center>
<form action="/settings.html" method="POST">
<center>
<div class="shadow">
<iframe src="menu.html" width="90%"></iframe>
<div data-include="menu"></div>
<table class="tbl" cellspacing="2" cellpadding = "2">
<tr>
<td>
<label>Select Ballast:</label>
</td>
<td>
<select id = "myList" name="ballast">
<option value = "1">Ballast #1</option>
<option value = "2">Ballast #2</option>
<option value = "3">Ballast #3</option>
</select>
</td>
</tr>
<tr>
<td>
Enter start hour:</br>
</td>
<td>
<input type="text" name="shour" placeholder="Hour" required></br>
</td>
</tr>
<tr>
<td>
Enter start minute:</br>
</td>
<td>
<input type="text" name="smin" placeholder="Minute" required></br>
</td>
</tr>
<tr>
<td>
Enter amount of time to fade in(in mins)(cannot be decimal):</br>
</td>
<td>
<input type="text" name="fadePeriod" placeholder="Fade Period" required></br>
</td>
</tr>
<tr>
<td>
Enter end hour:</br>
</td>
<td>
<input type="text" name="ehour" placeholder="Hour" required></br>
</td>
</tr>
<tr>
<td>
Enter start minute:</br>
</td>
<td>
<input type="text" name="emin" placeholder="Minute" required></br>
<td>
</tr>
<tr align = "center">
<td colspan="3">
<p style="margin:8px 0px"><button class="button">Submit</button></p>
</td>
</tr>
</table>
</div>
</center>
</form>
</body>
</html>

39
frontend/src/settings.js Normal file
View File

@ -0,0 +1,39 @@
var myForm = document.getElementById('myForm')
myForm.addEventListener('submit', function(e) {
e.preventDefault();
//var data = JSON.stringify(formData(myForm));
loadJSON(formData(myForm))
})
function formData(form) {
let el = form.querySelectorAll('input[type="text"]');
let ballast = document.getElementById('myBallasts').value;
let myData = '';
myData += 'ballast' + '=' + ballast + '&';
for (var x = 0; x < el.length; x++) {
let name = el[x].name;
let value = el[x].value;
//myData[name] = value;
myData += name + '=' + value + '&'
}
return myData.slice(0, -1);
}
function loadJSON(data) {
const url = '/settings.html';
console.log(data)
const myData = data
fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: myData
})
// .then(function (response) {
// return response.json()
// }).then(function (data) {
// console.log(data)
// })
.catch(error => console.log(error))
}

7
frontend/src/settingsFile1.txt Executable file
View File

@ -0,0 +1,7 @@
{
"shour": 16,
"fadePeriod": 30,
"smin": 0,
"ehour": 22,
"emin": 0
}

7
frontend/src/settingsFile2.txt Executable file
View File

@ -0,0 +1,7 @@
{
"shour": 16,
"fadePeriod": 30,
"smin": 0,
"ehour": 22,
"emin": 0
}

7
frontend/src/settingsFile3.txt Executable file
View File

@ -0,0 +1,7 @@
{
"shour": 16,
"fadePeriod": 30,
"smin": 0,
"ehour": 22,
"emin": 0
}

23
frontend/src/success.html Executable file
View File

@ -0,0 +1,23 @@
<html>
<head>
<title>HTML Upload successful</title>
<link href='main.css' rel='stylesheet' type='text/css'>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144x144.png">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon.ico">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#00878f">
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport'>
</head>
<body>
<center>
<header>
<h1>HTML Uploader</h1>
</header>
<div>
<p>The upload was successful.</p>
<a class="button" href="/">Back</a>
</div>
</center>
</body>
</html>

View File

@ -0,0 +1,6 @@
{
"temperatures": [
48,
2
]
}

View File

@ -0,0 +1,8 @@
$('#upload-button').filestyle({
buttonName: 'btn-success',
buttonText: ' File selection',
btnClass: "btn-ternary",
dragDrop: true
});

View File

@ -1,10 +0,0 @@
$(function(){
var includes = $('[data-include]');
jQuery.each(includes, function(){
var file = '/' + $(this).data('include') + '.html';
$(this).load(file);
});
});
// $(document).ready(function(){
// $( "#header" ).load( "/menu.html" );
// });

View File

@ -29,6 +29,6 @@ board_build.f_cpu = 160000000L
; --ip=192.168.1.103
; --port=8266
; --auth=esp8266
build_flags =
-DLC_WIFI_SSID=${sysenv.WIFI_SSID}
-DLC_WIFI_PASS=${sysenv.WIFI_PASS}
; build_flags =
; -DLC_WIFI_SSID=${sysenv.WIFI_SSID}
; -DLC_WIFI_PASS=${sysenv.WIFI_PASS}

View File

@ -358,6 +358,8 @@ void startServer()
server.send(200, "text/plain", rtcDate);
});
server.serveStatic("/", SPIFFS, "www", "no-cache");
server.begin(); // start the HTTP server
Serial.println("HTTP server started.");
}
@ -366,6 +368,9 @@ void startServer()
void handleNotFound()
{ // if the requested file or page doesn't exist, return a 404 not found error
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
if (!handleFileRead(server.uri()))
{ // check if the file exists in the flash memory (SPIFFS), if so, send it
server.send(404, "application/json", "{\"status\":\"failed\",\"message\":\"Resource not found\"}");
@ -498,21 +503,24 @@ void combineSettings()
StaticJsonBuffer<100> jsonBuffer10;
JsonObject &root1 = jsonBuffer10.parseObject(buf1.get());
if (root1 == JsonObject::invalid())
if (!root1.success())
{
Serial.println("Error1");
Log.error("Error parsing JSON");
return;
}
StaticJsonBuffer<100> jsonBuffer11;
JsonObject &root2 = jsonBuffer11.parseObject(buf2.get());
if (root2 == JsonObject::invalid())
if (!root2.success())
{
Serial.println("Error2");
Log.error("Error parsing JSON");
return;
}
StaticJsonBuffer<100> jsonBuffer12;
JsonObject &root3 = jsonBuffer12.parseObject(buf3.get());
if (root3 == JsonObject::invalid())
if (!root3.success())
{
Serial.println("Error3");
Log.error("Error parsing JSON");
return;
}
StaticJsonBuffer<50> jsonBuffer16;
JsonObject &root5 = jsonBuffer16.parseObject(buf5.get());
@ -574,6 +582,7 @@ void combineSettings()
file5.close();
}
//FIXME borked - has strange/undefined behaviour
void combineSettings_new()
{
const File file1 = SPIFFS.open("/settingsFile1.txt", "r");

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.