557 lines
14 KiB
C
557 lines
14 KiB
C
|
|
/*
|
|
* HTML constants for header, footer, css, ...
|
|
* Note: I know of the existence of SPIFFS and ESPHtmlTemplateProcessor,
|
|
* but to keep things simple for compiling and upload for others, I decided
|
|
* to not use those.
|
|
*/
|
|
|
|
// Template: const char HTMLexamplepage[] PROGMEM = R"EOF()EOF";
|
|
|
|
// first part of HTML header stuff
|
|
const char HTMLheaderP1[] PROGMEM = R"EOF(
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset='UTF-8'>
|
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
|
)EOF";
|
|
// here comes the page title in returnHTMLheader()
|
|
|
|
// second part of HTML header stuff
|
|
// Having the whole CSS here ensures it's all the time present
|
|
const char HTMLheaderP2[] PROGMEM = R"EOF(
|
|
<link rel='icon' href='data:;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAABcElEQVQ4y42TzU/bQBDFf7Nx1qGuAYVgQSuo2khBggPhyIH//9AiJAQ9tEeLqCKiUD6sxF52OMSEBCdW57aa9968fTsr3V5XWVLPO6sANNL7ZRAMNeU6Ea4T1UEI6pr55kcAwhpMrYOpk2/r/yEQmKWkIonf+TZVgex4Fw0bIEtIAALF3gbZ8U5VwKa3PJ18JT9IpiLvyflBwuhLG5veVUM0/0aoCONPa2hQjWZ8uEVeupJnXSBwO8YOH8iTeAKc2Q4Xt2C1VZL93F7MjbK/bxDnp5Zn7b+So+9pdQ+K/Q5qJlrRj5Ts6DM+rK7Ih7Mr3HaM7jYQVZqXQ6Tb6yqBYdTfomhHiFfUyMI3f+01/z7RHNzTGDyWGThP63SA2d8EEfIkrgQpzmOvH0AV+3M4zegNpUwagAYG8Yp4BS0nl4Kz5Mpf0JXJMby6w/66Aa+M+9uE53/Iexsggq4ESOYWC0jmsBfX8xdXhcJjL4cLc3kBl8uJGQ/CrpAAAAAASUVORK5CYII='>
|
|
<style>
|
|
body {
|
|
color: #cae0d0;
|
|
background-color: #1d211e;
|
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
}
|
|
|
|
.center {
|
|
width: 100%;
|
|
margin: auto;
|
|
}
|
|
|
|
.centered {
|
|
display: block;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5 {
|
|
text-align: center;
|
|
}
|
|
a:link, a:visited {
|
|
color: #04AA6D;
|
|
}
|
|
a:hover {
|
|
color: #64AA6D;
|
|
}
|
|
a:active {
|
|
color: #04AA6D;
|
|
}
|
|
.infomsg , .warnmsg {
|
|
color: #fff;
|
|
border-radius: 3px;
|
|
padding: 4px;
|
|
width: fit-content; min-width: 200px; max-width: 420px;
|
|
margin: auto;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
|
|
}
|
|
.infomsg {
|
|
background: #04AA6D;
|
|
}
|
|
.warnmsg {
|
|
background: #aa4204;
|
|
}
|
|
.inputShort {
|
|
width: 42px;
|
|
}
|
|
|
|
.helpbox {
|
|
font-size: 0.8em;
|
|
margin-left: 15px;
|
|
margin-top: 5px;
|
|
margin-bottom: 5px;
|
|
}
|
|
.nav {
|
|
background: #333;
|
|
width: 100%;
|
|
margin: auto;
|
|
margin-bottom: 10px;
|
|
padding: 0;
|
|
position: relative;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.subnav {
|
|
text-align: center;
|
|
display: table;
|
|
margin: auto;
|
|
margin-bottom: 10px;
|
|
padding: 0;
|
|
position: relative;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.nav li {
|
|
display: inline-block;
|
|
list-style: none;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.subnav li {
|
|
background: #026b45;
|
|
list-style: none;
|
|
border-radius: 3px;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.nav li:first-of-type {
|
|
background: #026b45;
|
|
border-top-left-radius: 3px;
|
|
border-bottom-left-radius: 3px;
|
|
}
|
|
.nav li a, .nav span, .subnav li a, .subnav span, .button, .button:link, input[type=button], input[type=submit], input[type=reset] {
|
|
color: #ddd;
|
|
display: block;
|
|
font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
|
|
font-size:0.8em;
|
|
padding: 10px 20px;
|
|
text-decoration: none;
|
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.nav li a:hover, .subnav li a:hover, .activeMenu, .button:link:hover, .button:visited:hover, input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover {
|
|
background: #04AA6D;
|
|
color: #fff;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.nav li a:active, .subnav li a:active {
|
|
background: #026b45;
|
|
color: #cae0d0;
|
|
}
|
|
|
|
.activeMenu {
|
|
background: #444;
|
|
}
|
|
|
|
.MenuTime {
|
|
background: #292929;
|
|
}
|
|
|
|
.button, .button:link, .button:visited, input[type=button], input[type=submit], input[type=reset] {
|
|
background: #026b45;
|
|
color: #fff;
|
|
border-radius: 3px;
|
|
padding: 6px 12px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
display: inline-block;
|
|
border: none;
|
|
}
|
|
|
|
.button:link:active, .button:visited:active, input[type=button]:active, input[type=submit]:active, input[type=reset]:active {
|
|
background: #026b45;
|
|
color: #cae0d0;
|
|
}
|
|
|
|
input[type=text], input[type=date], input[type=number], input[type=password], select {
|
|
background: #cae0d0;
|
|
color: #1d211e;
|
|
border: 1px solid #026b45;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
@media only screen and (min-width: 1820px) {
|
|
.center, .nav {
|
|
width: 60%; min-width: 420px;
|
|
}
|
|
.subnav li {
|
|
display: '';
|
|
margin-bottom: 3px;
|
|
}
|
|
}
|
|
|
|
@media only screen and (min-width: 640px) {
|
|
.subnav li {
|
|
display: inline-block;
|
|
margin-bottom: 3px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<ul class='nav'>)EOF";
|
|
// here comes the menu as unordered List in returnHTMLheader()
|
|
|
|
|
|
const char HTMLfooter[] PROGMEM = R"EOF(
|
|
</div>
|
|
</body>
|
|
</html>
|
|
)EOF";
|
|
|
|
|
|
const char HTMLsuccess[] PROGMEM = R"EOF(
|
|
<div class='infomsg'>✅ Successfully saved!</div>
|
|
)EOF";
|
|
|
|
const char HTMLneedRestart[] PROGMEM = R"EOF(
|
|
<div class='warnmsg'>❗ Restart is required to apply new WiFi settings!
|
|
<form action='/system/restart'>
|
|
<input type='submit' value='Restart now' />
|
|
</form>
|
|
</div>
|
|
)EOF";
|
|
|
|
const char HTMLhelp[] PROGMEM = R"EOF(
|
|
<h2>❓ Help</h2>
|
|
Here you will get some helpful help.
|
|
<h3>API</h3>
|
|
<a href='/api/sensors' target='_blank'>Sensor data</a>: <code>GET /api/sensors</code><br>
|
|
<a href='/api/debug' target='_blank'>Debug all data:</a> <code>GET /api/debug</code>
|
|
)EOF";
|
|
|
|
|
|
const char JSconvertDateToEpoch[] PROGMEM = R"EOF(
|
|
<script>
|
|
function convertDateToEpoch(src, dst) {
|
|
var valGrowStart = document.getElementById(src).value ;
|
|
document.getElementById(dst).value = new Date(valGrowStart).getTime() / 1000;
|
|
}
|
|
</script>
|
|
)EOF";
|
|
|
|
// The gauge meter are based on sathomas' gaugemeter
|
|
// https://github.com/sathomas/material-gauge
|
|
|
|
const char CSSgauge[] PROGMEM = R"EOF(
|
|
.gauge {
|
|
position: relative;
|
|
}
|
|
|
|
.gaugeWrapper {
|
|
overflow: hidden;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.gauge__container {
|
|
margin: 0;
|
|
padding: 0;
|
|
position: absolute;
|
|
left: 50%;
|
|
overflow: hidden;
|
|
text-align: center;
|
|
-webkit-transform: translateX(-50%);
|
|
-moz-transform: translateX(-50%);
|
|
-ms-transform: translateX(-50%);
|
|
-o-transform: translateX(-50%);
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.gauge__background {
|
|
z-index: 0;
|
|
position: absolute;
|
|
background-color: #cae0d0;
|
|
top: 0;
|
|
border-radius: 300px 300px 0 0;
|
|
}
|
|
|
|
.gauge__data {
|
|
z-index: 1;
|
|
position: absolute;
|
|
background-color: #04AA6D;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
border-radius: 300px 300px 0 0;
|
|
-webkit-transform-origin: center bottom;
|
|
-moz-transform-origin: center bottom;
|
|
-ms-transform-origin: center bottom;
|
|
-o-transform-origin: center bottom;
|
|
transform-origin: center bottom;
|
|
}
|
|
|
|
.gauge__center {
|
|
z-index: 2;
|
|
position: absolute;
|
|
background-color: #1d211e;
|
|
margin-right: auto;
|
|
border-radius: 300px 300px 0 0;
|
|
}
|
|
|
|
.gauge__marker {
|
|
z-index: 3;
|
|
background-color: #fff;
|
|
position: absolute;
|
|
width: 1px;
|
|
}
|
|
|
|
.gauge__needle {
|
|
z-index: 4;
|
|
background-color: #E91E63;
|
|
height: 3px;
|
|
position: absolute;
|
|
-webkit-transform-origin: left center;
|
|
-moz-transform-origin: left center;
|
|
-ms-transform-origin: left center;
|
|
-o-transform-origin: left center;
|
|
transform-origin: left center;
|
|
}
|
|
|
|
.gauge__labels {
|
|
display: table;
|
|
margin: 0 auto;
|
|
position: relative;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.gauge__label--low {
|
|
display: table-cell;
|
|
text-align: center;
|
|
}
|
|
|
|
.gauge__label--spacer {
|
|
display: table-cell;
|
|
}
|
|
|
|
.gauge__label--high {
|
|
display: table-cell;
|
|
text-align: center;
|
|
}
|
|
|
|
|
|
.gauge { height: calc(60px + 3em); }
|
|
.gauge__container { width: 120px; height: 60px; }
|
|
.gauge__marker { height: 60px; left: 59.5px; }
|
|
.gauge__background { width: 120px; height: 60px; }
|
|
.gauge__center { width: 72px; height: 36px; top: 24px; margin-left: 24px; }
|
|
.gauge__data { width: 120px; height: 60px; }
|
|
.gauge__needle { left: 60px; top: 58px; width: 60px; }
|
|
.gauge__labels { top: 60px; width: 120px; }
|
|
.gauge__label--low { width: 24px; }
|
|
.gauge__label--spacer { width: 72px; text-align: center;}
|
|
.gauge__label--high { width: 24px; }
|
|
.gaugeLabel { text-align: center; }
|
|
|
|
|
|
@media only screen and (min-width: 720px) {
|
|
.gauge { height: calc(120px + 4.2em); }
|
|
.gauge__container { width: 240px; height: 120px; }
|
|
.gauge__marker { height: 120px; left: 119.5px; }
|
|
.gauge__background { width: 240px; height: 120px; }
|
|
.gauge__center { width: 144px; height: 72px; top: 48px; margin-left: 48px; }
|
|
.gauge__data { width: 240px; height: 120px; }
|
|
.gauge__needle { left: 120px; top: 117px; width: 120px; }
|
|
.gauge__labels { top: 120px; width: 240px; }
|
|
.gauge__label--low { width: 48px; }
|
|
.gauge__label--spacer { width: 144px; text-align: center;}
|
|
.gauge__label--high { width: 48px; }
|
|
.gaugeLabel { font-size: 1.3em; }
|
|
.gauge__labels { font-size: 2em; }
|
|
}
|
|
|
|
.gauge--liveupdate .gauge__data,
|
|
.gauge--liveupdate .gauge__needle {
|
|
-webkit-transition: all 1s ease-in-out;
|
|
-moz-transition: all 1s ease-in-out;
|
|
-ms-transition: all 1s ease-in-out;
|
|
-o-transition: all 1s ease-in-out;
|
|
transition: all 1s ease-in-out;
|
|
}
|
|
|
|
|
|
.gauge__data {
|
|
-webkit-transform: rotate(-.50turn);
|
|
-moz-transform: rotate(-.50turn);
|
|
-ms-transform: rotate(-.50turn);
|
|
-o-transform: rotate(-.50turn);
|
|
transform: rotate(-.50turn);
|
|
}
|
|
.gauge__needle {
|
|
-webkit-transform: rotate(-.50turn);
|
|
-moz-transform: rotate(-.50turn);
|
|
-ms-transform: rotate(-.50turn);
|
|
-o-transform: rotate(-.50turn);
|
|
transform: rotate(-.50turn);
|
|
}
|
|
|
|
)EOF";
|
|
|
|
const char JSgauge[] PROGMEM = R"EOF(
|
|
|
|
function Gauge(el) {
|
|
|
|
var element, // Containing element for the info component
|
|
data, // `.gauge__data` element
|
|
needle, // `.gauge__needle` element
|
|
value = 0.0, // Current gauge value from 0 to 1
|
|
prop, // Style for transform
|
|
valueLabel; // `.gauge__label--spacer` element
|
|
|
|
var setElement = function(el) {
|
|
// Keep a reference to the various elements and sub-elements
|
|
element = el;
|
|
data = element.querySelector('.gauge__data');
|
|
needle = element.querySelector('.gauge__needle');
|
|
valueLabel = element.querySelector('.gauge__label--spacer');
|
|
|
|
};
|
|
|
|
var setValue = function(x, max, unit) {
|
|
percentage = x * 100 / max;
|
|
value = percentage / 100;
|
|
var turns = -0.5 + (value * 0.5);
|
|
data.style[prop] = 'rotate(' + turns + 'turn)';
|
|
needle.style[prop] = 'rotate(' + turns + 'turn)';
|
|
valueLabel.textContent = x + unit;
|
|
|
|
};
|
|
|
|
function exports() { };
|
|
|
|
exports.element = function(el) {
|
|
if (!arguments.length) { return element; }
|
|
setElement(el);
|
|
return this;
|
|
};
|
|
|
|
exports.value = function(x, max=100, unit='%') {
|
|
if (!arguments.length) { return value; }
|
|
setValue(x, max, unit);
|
|
return this;
|
|
};
|
|
|
|
var body = document.getElementsByTagName('body')[0];
|
|
['webkitTransform', 'mozTransform', 'msTransform', 'oTransform', 'transform'].
|
|
forEach(function(p) {
|
|
if (typeof body.style[p] !== 'undefined') { prop = p; }
|
|
}
|
|
);
|
|
|
|
if (arguments.length) {
|
|
setElement(el);
|
|
}
|
|
|
|
return exports;
|
|
|
|
};
|
|
|
|
|
|
)EOF";
|
|
|
|
const char HTMLgauge[] PROGMEM = R"EOF(
|
|
|
|
<div class='gaugeWrapper'>
|
|
<div class='gauge gauge--liveupdate spacer' id='gaugeTemperature' style='float:left; margin-right: 10px;'>
|
|
<div class='gaugeLabel'>Temperature</div>
|
|
<div class='gauge__container'>
|
|
<div class='gauge__background'></div>
|
|
<div class='gauge__center'></div>
|
|
<div class='gauge__data'></div>
|
|
<div class='gauge__needle'></div>
|
|
</div>
|
|
<div class='gauge__labels mdl-typography__headline'>
|
|
<span class='gauge__label--low'></span>
|
|
<span class='gauge__label--spacer'></span></span>
|
|
<span class='gauge__label--high'></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class='gauge gauge--liveupdate spacer' id='gaugeHumidity' style='float:left; margin-right: 10px;'>
|
|
<div class='gaugeLabel'>Humidity</div>
|
|
<div class='gauge__container'>
|
|
<div class='gauge__background'></div>
|
|
<div class='gauge__center'></div>
|
|
<div class='gauge__data'></div>
|
|
<div class='gauge__needle'></div>
|
|
</div>
|
|
<div class='gauge__labels mdl-typography__headline'>
|
|
<span class='gauge__label--low'></span>
|
|
<span class='gauge__label--spacer'></span>
|
|
<span class='gauge__label--high'></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class='gauge gauge--liveupdate' id='gaugeSoilmoisture' style='float:left;'>
|
|
<div class='gaugeLabel'>Soilmoisture</div>
|
|
<div class='gauge__container'>
|
|
<div class='gauge__background'></div>
|
|
<div class='gauge__center'></div>
|
|
<div class='gauge__data'></div>
|
|
<div class='gauge__needle'></div>
|
|
</div>
|
|
<div class='gauge__labels mdl-typography__headline'>
|
|
<span class='gauge__label--low'></span>
|
|
<span class='gauge__label--spacer'></span>
|
|
<span class='gauge__label--high'></span>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<script src='gauge.js'></script>
|
|
<script>
|
|
var gaugeTemperature = new Gauge(document.getElementById('gaugeTemperature'));
|
|
var gaugeHumidity = new Gauge(document.getElementById('gaugeHumidity'));
|
|
var gaugeSoilmoisture = new Gauge(document.getElementById('gaugeSoilmoisture'));
|
|
</script>
|
|
|
|
)EOF";
|
|
|
|
|
|
const char HTMLupdate[] PROGMEM = R"EOF(
|
|
<p>You find the latest CanGrow firmware version on the <a href='https://git.la10cy.net/DeltaLima/CanGrow/releases' target='_blank'>release page</a> of the git repository.</p>
|
|
<form method='POST' action='/system/applyUpdate' enctype='multipart/form-data' onsubmit="document.getElementById('divUploading').style.display = '';">
|
|
<b>Select .bin file:</b><br>
|
|
<input type='file' accept='.bin,.bin.gz' name='firmware' required>
|
|
<input type='submit' value='Update Firmware'>
|
|
</form>
|
|
<div id='divUploading' style='display: none;' class='warnmsg'>🛜 Uploading, please wait...<div>
|
|
)EOF";
|
|
|
|
const char HTMLsystemSubNav[] PROGMEM = R"EOF(
|
|
<ul class='subnav'>
|
|
<li><a href='/system/update'>🔄 Firmware update</a></li>
|
|
<li><a href='/system/restart' >🔁 CanGrow restart</a></li>
|
|
<li><a href='/system/wipe' >💣 Factory reset</a></li>
|
|
</ul>
|
|
)EOF";
|
|
|
|
const char JSsoilmoistureTypeSelect[] PROGMEM = R"EOF(
|
|
<script>
|
|
function MoistureSensorType() {
|
|
let selVal = document.getElementById('SelMoistureSensor_Type').value;
|
|
let wet = document.getElementById('iSoilmoistureWet');
|
|
let dry = document.getElementById('iSoilmoistureDry');
|
|
switch(selVal) {
|
|
case '1':
|
|
wet.value = 160;
|
|
dry.value = 360;
|
|
console.log(selVal);
|
|
break;
|
|
|
|
case '2':
|
|
wet.value = 485;
|
|
dry.value = 250;
|
|
console.log(selVal);
|
|
break;
|
|
|
|
default:
|
|
wet.value = 0;
|
|
dry.value = 0;
|
|
console.log(selVal);
|
|
break;
|
|
}
|
|
}
|
|
</script>
|
|
)EOF";
|