Dashboard finished

pull/125/head
Robert Stein 2020-12-22 22:41:15 +01:00
parent 612d82b52a
commit f8f47a94f0
5 changed files with 549 additions and 287 deletions

View File

@ -14,7 +14,7 @@ void WebServer::setup() {
this->server = new ESP8266WebServer(this->globalDataController->getSystemSettings()->webserverPort); this->server = new ESP8266WebServer(this->globalDataController->getSystemSettings()->webserverPort);
this->serverUpdater = new ESP8266HTTPUpdateServer(); this->serverUpdater = new ESP8266HTTPUpdateServer();
this->server->on("/", []() { obj->displayPrinterStatus(); }); this->server->on("/", []() { obj->handleMainPage(); });
this->server->on("/systemreset", []() { obj->handleSystemReset(); }); this->server->on("/systemreset", []() { obj->handleSystemReset(); });
this->server->on("/forgetwifi", []() { obj->handleWifiReset(); }); this->server->on("/forgetwifi", []() { obj->handleWifiReset(); });
this->server->on("/configurestation/show", []() { obj->handleConfigureStation(); }); this->server->on("/configurestation/show", []() { obj->handleConfigureStation(); });
@ -28,8 +28,6 @@ void WebServer::setup() {
this->server->on("/configuresensor/update", []() { obj->handleConfigureSensor(); }); this->server->on("/configuresensor/update", []() { obj->handleConfigureSensor(); });
this->server->on("/update", HTTP_GET, []() { obj->handleUpdatePage(); }); this->server->on("/update", HTTP_GET, []() { obj->handleUpdatePage(); });
this->server->on("/updateconfig", []() { obj->handleUpdateConfig(); });
this->server->onNotFound([]() { obj->redirectHome(); }); this->server->onNotFound([]() { obj->redirectHome(); });
this->serverUpdater->setup( this->serverUpdater->setup(
this->server, this->server,
@ -64,33 +62,13 @@ void WebServer::redirectHome() {
} }
void WebServer::displayPrinterStatus() { void WebServer::displayPrinterStatus() {
WebserverMemoryVariables::sendHeader(this->server, this->globalDataController, "Status", "Monitor", true);
/*BasePrinterClient *printerClient = this->globalDataController->getPrinterClient(); /*BasePrinterClient *printerClient = this->globalDataController->getPrinterClient();
String html = ""; String html = "";
String displayTime =
this->globalDataController->getTimeClient()->getAmPmHours() + ":" +
this->globalDataController->getTimeClient()->getMinutes() + ":" +
this->globalDataController->getTimeClient()->getSeconds() + " " +
this->globalDataController->getTimeClient()->getAmPm();
if (this->globalDataController->getClockSettings()->is24h) {
displayTime =
this->globalDataController->getTimeClient()->getHours() + ":" +
this->globalDataController->getTimeClient()->getMinutes() + ":" +
this->globalDataController->getTimeClient()->getSeconds();
}
html += "<div class='w3-cell-row' style='width:100%'><h2>" + printerClient->getPrinterType() + " Monitor</h2></div><div class='w3-cell-row'>";
html += "<div class='w3-cell w3-container' style='width:100%'><p>";
if ((printerClient->getPrinterType() == "Repetier") || (printerClient->getPrinterType() == "Klipper")) {
html += "Printer Name: " + printerClient->getPrinterName() + " <a href='/configure' title='Configure'><i class='fa fa-cog'></i></a><br>";
}
else {
html += "Host Name: " + this->globalDataController->getPrinterHostName() + " <a href='/configure' title='Configure'><i class='fa fa-cog'></i></a><br>";
}
if (printerClient->getError() != "") { if (printerClient->getError() != "") {
html += "Status: Offline<br>"; html += "Status: Offline<br>";
@ -104,42 +82,9 @@ void WebServer::displayPrinterStatus() {
} }
if (printerClient->isPrinting()) { if (printerClient->isPrinting()) {
html += "File: " + printerClient->getFileName() + "<br>";
float fileSize = printerClient->getFileSize().toFloat();
if (fileSize > 0) {
fileSize = fileSize / 1024;
html += "File Size: " + String(fileSize) + "KB<br>";
}
int filamentLength = printerClient->getFilamentLength().toInt();
if (filamentLength > 0) {
float fLength = float(filamentLength) / 1000;
html += "Filament: " + String(fLength) + "m<br>";
}
html += "Tool Temperature: " + printerClient->getTempToolActual() + "&#176; C<br>";
if (printerClient->getTempBedActual() != 0 ) {
html += "Bed Temperature: " + printerClient->getTempBedActual() + "&#176; C<br>";
}
int val = printerClient->getProgressPrintTimeLeft().toInt();
int hours = this->globalDataController->numberOfHours(val);
int minutes = this->globalDataController->numberOfMinutes(val);
int seconds = this->globalDataController->numberOfSeconds(val);
html += "Est. Print Time Left: "
+ this->globalDataController->zeroPad(hours) + ":"
+ this->globalDataController->zeroPad(minutes) + ":"
+ this->globalDataController->zeroPad(seconds) + "<br>";
val = printerClient->getProgressPrintTime().toInt();
hours = this->globalDataController->numberOfHours(val);
minutes = this->globalDataController->numberOfMinutes(val);
seconds = this->globalDataController->numberOfSeconds(val);
html += "Printing Time: " + this->globalDataController->zeroPad(hours) + ":" + this->globalDataController->zeroPad(minutes) + ":" + this->globalDataController->zeroPad(seconds) + "<br>";
html += "<style>#myProgress {width: 100%;background-color: #ddd;}#myBar {width: " + printerClient->getProgressCompletion() + "%;height: 30px;background-color: #4CAF50;}</style>";
html += "<div id=\"myProgress\"><div id=\"myBar\" class=\"w3-medium w3-center\">" + printerClient->getProgressCompletion() + "%</div></div>";
} else {
html += "<hr>";
}
html += "</p></div></div>"; html += "</p></div></div>";
@ -147,79 +92,13 @@ void WebServer::displayPrinterStatus() {
this->server->sendContent(html); // spit out what we got this->server->sendContent(html); // spit out what we got
html = ""; html = "";
*/
if (this->globalDataController->getWeatherSettings()->show) {
OpenWeatherMapClient *weatherClient = this->globalDataController->getWeatherClient();
if (weatherClient->getCity(0) == "") {
html += "<p>Please <a href='/configureweather'>Configure Weather</a> API</p>";
if (weatherClient->getError() != "") {
html += "<p>Weather Error: <strong>" + weatherClient->getError() + "</strong></p>";
}
} else {
html += "<div class='w3-cell-row' style='width:100%'><h2>" + weatherClient->getCity(0) + ", " + weatherClient->getCountry(0) + "</h2></div><div class='w3-cell-row'>";
html += "<div class='w3-cell w3-left w3-medium' style='width:120px'>";
html += "<img src='http://openweathermap.org/img/w/" + weatherClient->getIcon(0) + ".png' alt='" + weatherClient->getDescription(0) + "'><br>";
html += weatherClient->getHumidity(0) + "% Humidity<br>";
html += weatherClient->getWind(0) + " <span class='w3-tiny'>" + weatherClient->getSpeedSymbol() + "</span> Wind<br>";
html += "</div>";
html += "<div class='w3-cell w3-container' style='width:100%'><p>";
html += weatherClient->getCondition(0) + " (" + weatherClient->getDescription(0) + ")<br>";
html += weatherClient->getTempRounded(0) + weatherClient->getTempSymbol(true) + "<br>";
html += "<a href='https://www.google.com/maps/@" + weatherClient->getLat(0) + "," + weatherClient->getLon(0) + ",10000m/data=!3m1!1e3' target='_BLANK'><i class='fa fa-map-marker' style='color:red'></i> Map It!</a><br>";
html += "</p></div></div>";
}
this->server->sendContent(html); // spit out what we got
html = ""; // fresh start
}*/
WebserverMemoryVariables::sendFooter(this->server, this->globalDataController);
} }
void WebServer::handleUpdateConfig() {
/*boolean flipOld = this->globalDataController->isDisplayInverted();
if (!this->authentication()) {
return this->server->requestAuthentication();
}
if (this->server->hasArg("printer")) {
this->globalDataController->getPrinterClient()->setPrinterName(
this->server->arg("printer")
);
}
this->globalDataController->setPrinterApiKey(this->server->arg("PrinterApiKey"));
this->globalDataController->setPrinterHostName(this->server->arg("PrinterHostName"));
this->globalDataController->setPrinterServer(this->server->arg("PrinterAddress"));
this->globalDataController->setPrinterPort(this->server->arg("PrinterPort").toInt());
this->globalDataController->setPrinterAuthUser(this->server->arg("octoUser"));
this->globalDataController->setPrinterAuthPass(this->server->arg("octoPass"));
this->globalDataController->setHasPrinterPsu(this->server->hasArg("hasPSU"));
this->globalDataController->setDisplayClock(this->server->hasArg("isClockEnabled"));
this->globalDataController->setIsDisplayInverted(this->server->hasArg("invDisp"));
this->globalDataController->setClockIs24h(this->server->hasArg("is24hour"));
this->globalDataController->setClockResyncMinutes(this->server->arg("refresh").toInt());
this->globalDataController->setWebserverTheme(this->server->arg("theme"));
this->globalDataController->setClockUtcOffset(this->server->arg("utcoffset").toFloat());
String temp = this->server->arg("userid");
this->globalDataController->setWebserverUsername(temp);
temp = this->server->arg("stationpassword");
this->globalDataController->setWebserverPassword(temp);
//this->globalDataController->setUseLedFlash(this->server->hasArg("useFlash"));
this->globalDataController->writeSettings();
this->findMDNS();
this->globalDataController->getPrinterClient()->getPrinterJobResults();
this->globalDataController->getPrinterClient()->getPrinterPsuState();
if (this->globalDataController->isDisplayInverted() != flipOld) {
this->globalDataController->getDisplayClient()->flipDisplayUpdate();
}
this->globalDataController->getDisplayClient()->handleUpdate();
this->globalDataController->getTimeClient()->resetLastEpoch();*/
this->redirectHome();
}
void WebServer::findMDNS() { void WebServer::findMDNS() {
return; // nothing to do here return; // nothing to do here
@ -254,151 +133,6 @@ void WebServer::findMDNS() {
}*/ }*/
} }
/*
void WebServer::handleConfigure() {
if (!this->authentication()) {
return this->server->requestAuthentication();
}
BasePrinterClient *printerClient = this->globalDataController->getPrinterClient();
String html = "";
WebserverMemoryVariables::sendHeader(this->server, this->globalDataController, "Configure", "Station");
// send javascript functions for repetier server test
html = "<script>function testRepetier(){var e=document.getElementById(\"RepetierTest\"),r=document.getElementById(\"PrinterAddress\").value,"
"t=document.getElementById(\"PrinterPort\").value;if(\"\"==r||\"\"==t)return e.innerHTML=\"* Address and Port are required\","
"void(e.style.background=\"\");var n=\"http://\"+r+\":\"+t;n+=\"/printer/api/?a=listPrinter&apikey=\"+document.getElementById(\"PrinterApiKey\").value,"
"console.log(n);var o=new XMLHttpRequest;o.open(\"GET\",n,!0),o.onload=function(){if(200===o.status){var r=JSON.parse(o.responseText);"
"if(!r.error&&r.length>0){var t=\"<label>Connected -- Select Printer</label> \";t+=\"<select class='w3-option w3-padding' name='printer'>\";"
"var n=document.getElementById(\"selectedPrinter\").value,i=\"\";for(printer in r)i=r[printer].slug==n?\"selected\":\"\","
"t+=\"<option value='\"+r[printer].slug+\"' \"+i+\">\"+r[printer].name+\"</option>\";t+=\"</select>\","
"e.innerHTML=t,e.style.background=\"lime\"}else e.innerHTML=\"Error invalid API Key: \"+r.error,"
"e.style.background=\"red\"}else e.innerHTML=\"Error: \"+o.statusText,e.style.background=\"red\"},"
"o.onerror=function(){e.innerHTML=\"Error connecting to server -- check IP and Port\",e.style.background=\"red\"},o.send(null)}</script>";
this->server->sendContent(html);
// send javascript functions for klipper test
html = "<script>function testKlipper(){var e=document.getElementById(\"KlipperTest\"),t=document.getElementById(\"PrinterAddress\").value,"
"n=document.getElementById(\"PrinterPort\").value;if(e.innerHTML=\"\",\"\"==t||\"\"==n)return e.innerHTML=\"* Address and Port are required\","
"void(e.style.background=\"\");var r=\"http://\"+t+\":\"+n;r+=\"/printer/info\",window.open(r,\"_blank\").focus()}</script>";
this->server->sendContent(html);
// send javascript functions for octoprint test
html = "<script>function testOctoPrint(){var e=document.getElementById(\"OctoPrintTest\"),t=document.getElementById(\"PrinterAddress\").value,"
"n=document.getElementById(\"PrinterPort\").value;if(e.innerHTML=\"\",\"\"==t||\"\"==n)return e.innerHTML=\"* Address and Port are required\","
"void(e.style.background=\"\");var r=\"http://\"+t+\":\"+n;r+=\"/api/job?apikey=\"+document.getElementById(\"PrinterApiKey\").value,window.open(r,\"_blank\").focus()}</script>";
this->server->sendContent(html);
// Let us create a form for all printers
html = "<form class='w3-container' action='/updateconfig' method='get'><h2>Station Config:</h2>";
html += "<table id='printerData' class='table table-striped table-condensed'></table><script>var target ='#printerData'; var customFields = { data: {";
this->server->sendContent(html);
for (int i=0; i<10; i++) {
html = "";
if (i > 0) {
html = ",";
}
html += String(i + 1) + ": { enabled:true, fieldName:'COMPANYCODE',prettyName:'Company Name', fieldType:'OEM', fieldValue:'' }";
this->server->sendContent(html);
}
html = "},";
html += "xref: { // Key ['Label','column width',0|1] 1=admin only";
html += " custom: ['#','10%',0],";
html += " actions: ['Actions','5%',1],";
html += " enabled: ['Enabled','5%',0],";
html += " fieldName: ['Field','20%',0],";
html += " prettyName: ['Pretty Name','20%',0],";
html += " fieldType: ['Type','30%',0],";
html += " fieldValue: ['Value','10%',0]";
html += "},";
html += "fieldTypes: [";
html += " [' ',' '],";
html += " ['ENV','Environment (ENV)'],";
html += " ['REG','Registry (REG)'],";
html += " ['WMI','Windows Management Inst. (WMI)'],";
html += " ['OEM','Original Equipment Manuf.(OEM)']";
html += "],";
html += "admin:true,";
html += "multiedit:false";
html += "}";
html += "</script>";
this->server->sendContent(html);
String form = "";
if (printerClient->getPrinterType() != "Klipper") {
form += "<p><label>" + printerClient->getPrinterType() + " API Key (get from your server)</label>"
"<input class='w3-input w3-border w3-margin-bottom' type='text' name='PrinterApiKey' id='PrinterApiKey' value='%PRINTERAPIKEY%' maxlength='60'></p>";
}
if ((printerClient->getPrinterType() == "OctoPrint") || (printerClient->getPrinterType() == "Klipper")) {
form += "<p><label>" + printerClient->getPrinterType() + " Host Name</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='PrinterHostName' value='%PRINTERHOST%' maxlength='60'></p>";
}
form += "<p><label>" + printerClient->getPrinterType() + " Address (do not include http://)</label>"
"<input class='w3-input w3-border w3-margin-bottom' type='text' name='PrinterAddress' id='PrinterAddress' value='%PRINTERADDRESS%' maxlength='60'></p>"
"<p><label>" + printerClient->getPrinterType() + " Port</label>"
"<input class='w3-input w3-border w3-margin-bottom' type='text' name='PrinterPort' id='PrinterPort' value='%PRINTERPORT%' maxlength='5' onkeypress='return isNumberKey(event)'></p>";
if ((printerClient->getPrinterType() == "Repetier") || (printerClient->getPrinterType() == "Klipper")) {
form += "<input type='button' value='Test Connection' onclick='test" + printerClient->getPrinterName() + "()'>"
"<input type='hidden' id='selectedPrinter' value='" + printerClient->getPrinterName() + "'><p id='" + printerClient->getPrinterName() + "Test'></p>"
"<script>test" + printerClient->getPrinterName() + "();</script>";
}
else {
form += "<input type='button' value='Test Connection and API JSON Response' onclick='testOctoPrint()'><p id='OctoPrintTest'></p>";
}
form += "<p><label>" + printerClient->getPrinterType() + " User (only needed if you have haproxy or basic auth turned on)</label><input class='w3-input w3-border w3-margin-bottom' type='text' name='octoUser' value='%PRINTERUSER%' maxlength='30'></p>"
"<p><label>" + printerClient->getPrinterType() + " Password </label><input class='w3-input w3-border w3-margin-bottom' type='password' name='octoPass' value='%PRINTERPASS%'></p>";
form.replace("%PRINTERAPIKEY%", this->globalDataController->getPrinterApiKey());
form.replace("%PRINTERHOST%", this->globalDataController->getPrinterHostName());
form.replace("%PRINTERADDRESS%", this->globalDataController->getPrinterServer());
form.replace("%PRINTERPORT%", String(this->globalDataController->getPrinterPort()));
form.replace("%PRINTERUSER%", this->globalDataController->getPrinterAuthUser());
form.replace("%PRINTERPASS%", this->globalDataController->getPrinterAuthPass());
this->server->sendContent(form);
this->server->sendContent(form);
WebserverMemoryVariables::sendFooter(this->server, this->globalDataController);
}*/
void WebServer::handleConfigureSensor() {
if (!this->authentication()) {
return this->server->requestAuthentication();
}
WebserverMemoryVariables::sendHeader(this->server, this->globalDataController, "Configure", "Sensor");
WebserverMemoryVariables::sendFooter(this->server, this->globalDataController);
}
/** /**
* @brief Handle authentification on all subsites * @brief Handle authentification on all subsites
* @return boolean * @return boolean
@ -412,6 +146,27 @@ boolean WebServer::authentication() {
return true; // Authentication not required return true; // Authentication not required
} }
/**
* @brief Send main page to client
*/
void WebServer::handleMainPage() {
WebserverMemoryVariables::sendHeader(this->server, this->globalDataController, "Status", "Monitor", true);
WebserverMemoryVariables::sendMainPage(this->server, this->globalDataController);
WebserverMemoryVariables::sendFooter(this->server, this->globalDataController);
}
/**
* @brief Send sensor configuration page to client
*/
void WebServer::handleConfigureSensor() {
if (!this->authentication()) {
return this->server->requestAuthentication();
}
WebserverMemoryVariables::sendHeader(this->server, this->globalDataController, "Configure", "Sensor");
WebserverMemoryVariables::sendFooter(this->server, this->globalDataController);
}
/** /**
* @brief Send printer configuration page to client * @brief Send printer configuration page to client
*/ */

View File

@ -26,10 +26,9 @@ public:
void redirectTarget(String targetUri); void redirectTarget(String targetUri);
void displayPrinterStatus(); void displayPrinterStatus();
void handleSystemReset(); void handleSystemReset();
void handleWifiReset(); void handleWifiReset();
void handleUpdateConfig();
void handleMainPage();
void handleConfigurePrinter(); void handleConfigurePrinter();
void handleUpdatePrinter(); void handleUpdatePrinter();
void handleDeletePrinter(); void handleDeletePrinter();

View File

@ -58,7 +58,14 @@ void WebserverMemoryVariables::sendHeader(
server->sendContent(pageLabel); server->sendContent(pageLabel);
server->sendContent("</h4><h1 id='page-title' class='page-header__title'>"); server->sendContent("</h4><h1 id='page-title' class='page-header__title'>");
server->sendContent(pageTitle); server->sendContent(pageTitle);
server->sendContent("</h1></div>"); server->sendContent("</h1>");
TimeClient *timeCli = globalDataController->getTimeClient();
String displayTime = timeCli->getAmPmHours() + ":" + timeCli->getMinutes() + ":" + timeCli->getSeconds() + " " + timeCli->getAmPm();
if (globalDataController->getClockSettings()->is24h) {
displayTime = timeCli->getHours() + ":" + timeCli->getMinutes() + ":" + timeCli->getSeconds();
}
server->sendContent("<div style='position:absolute;right:0;top:0;text-align:right'>Current time<br>" + displayTime + "</div></div>");
if (globalDataController->getSystemSettings()->lastError.length() > 0) { if (globalDataController->getSystemSettings()->lastError.length() > 0) {
String errorBlock = FPSTR(HEADER_BLOCK_ERROR); String errorBlock = FPSTR(HEADER_BLOCK_ERROR);
@ -105,6 +112,191 @@ void WebserverMemoryVariables::sendFooter(ESP8266WebServer *server, GlobalDataCo
globalDataController->ledOnOff(false); globalDataController->ledOnOff(false);
} }
/**
* @brief Send out main page
* @param server Send out instancce
* @param globalDataController Access to global data
*/
void WebserverMemoryVariables::sendMainPage(ESP8266WebServer *server, GlobalDataController *globalDataController) {
String rowStart = FPSTR(FORM_ITEM_ROW_START);
rowStart.replace("%ROWEXTRACLASS%", "");
// Show weather and sensordata
OpenWeatherMapClient *weatherClient = globalDataController->getWeatherClient();
server->sendContent(rowStart);
String startBlock = FPSTR(MAINPAGE_ROW_WEATHER_AND_SENSOR_START);
String displayBlock = FPSTR(MAINPAGE_ROW_WEATHER_AND_SENSOR_BLOCK);
if (!globalDataController->getWeatherSettings()->show || weatherClient->getCity(0) == "") {
startBlock.replace("%ICON%", "");
server->sendContent(startBlock);
if (globalDataController->getWeatherSettings()->show) {
displayBlock = FPSTR(MAINPAGE_ROW_WEATHER_ERROR_BLOCK);
if (weatherClient->getError() != "") {
displayBlock.replace("%ERRORMSG%", "Weather Error: " + weatherClient->getError());
} else {
displayBlock.replace("%ERRORMSG%", "");
}
} else {
displayBlock = "";
}
} else {
startBlock.replace("%ICON%", weatherClient->getIcon(0));
server->sendContent(startBlock);
displayBlock.replace("%BTITLE%", weatherClient->getCity(0) + ", " + weatherClient->getCountry(0));
displayBlock.replace("%BLABEL%", "Lat: " + weatherClient->getLat(0) + ", Lon: " + weatherClient->getLon(0));
displayBlock.replace("%TEMPICON%", FPSTR(ICON32_TEMP));
displayBlock.replace("%TEMPERATURE%", weatherClient->getTempRounded(0) + weatherClient->getTempSymbol(true));
displayBlock.replace("%ICONA%", FPSTR(ICON16_WIND));
displayBlock.replace("%ICONB%", FPSTR(ICON16_HUMIDITY));
displayBlock.replace("%TEXTA%", weatherClient->getWind(0) + " " + weatherClient->getSpeedSymbol() + " Winds");
displayBlock.replace("%TEXTB%", weatherClient->getHumidity(0) + "% Humidity");
displayBlock.replace("%EXTRABLOCK%", "Condition: " + weatherClient->getDescription(0));
}
server->sendContent(displayBlock);
// TODO: SensorData
/*displayBlock = FPSTR(MAINPAGE_ROW_WEATHER_AND_SENSOR_BLOCK);
displayBlock.replace("%TEMPICON%", FPSTR(ICON32_TEMP));
displayBlock.replace("%BTITLE%", "Sensor");
displayBlock.replace("%BLABEL%", "BME280");
displayBlock.replace("%TEMPERATURE%", "24&#176;C");
displayBlock.replace("%ICONA%", FPSTR(ICON16_HUMIDITY));
displayBlock.replace("%ICONB%", FPSTR(ICON16_PRESSURE));
displayBlock.replace("%TEXTA%", "30% Humidity");
displayBlock.replace("%TEXTB%", "1000 hPa");
displayBlock.replace("%EXTRABLOCK%", "");
server->sendContent(displayBlock);*/
server->sendContent(FPSTR(MAINPAGE_ROW_WEATHER_AND_SENSOR_END));
server->sendContent(FPSTR(FORM_ITEM_ROW_END));
// Show all printer states
int totalPrinters = globalDataController->getNumPrinters();
PrinterDataStruct *printerConfigs = globalDataController->getPrinterSettings();
String lineData = "";
int colCnt = 0;
server->sendContent(rowStart);
// Show all errors if printers have one
for(int i=0; i<totalPrinters; i++) {
if (colCnt >= 3) {
server->sendContent(FPSTR(FORM_ITEM_ROW_END));
server->sendContent(rowStart);
colCnt = 0;
}
if ((printerConfigs[i].state == PRINTER_STATE_ERROR) || (printerConfigs[i].state == PRINTER_STATE_OFFLINE)) {
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_S_ERROROFFLINE));
}
else if (printerConfigs[i].state == PRINTER_STATE_STANDBY) {
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_S_STANDBY));
}
else {
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_S_PRINTING));
}
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_TITLE);
lineData.replace("%NAME%", String(printerConfigs[i].customName));
lineData.replace("%API%", globalDataController->getPrinterClientType(&printerConfigs[i]));
server->sendContent(lineData);
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Host");
lineData.replace("%V%", String(printerConfigs[i].remoteAddress) + ":" + String(printerConfigs[i].remotePort));
server->sendContent(lineData);
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "State");
lineData.replace("%V%", globalDataController->getPrinterStateAsText(&printerConfigs[i]));
server->sendContent(lineData);
if (printerConfigs[i].state != PRINTER_STATE_STANDBY) {
if (printerConfigs[i].state == PRINTER_STATE_ERROR) {
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Reason");
lineData.replace("%V%", String(printerConfigs[i].error));
server->sendContent(lineData);
}
else if (printerConfigs[i].state == PRINTER_STATE_OFFLINE) {
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Reason");
lineData.replace("%V%", "Not reachable");
server->sendContent(lineData);
} else {
if ((printerConfigs[i].state == PRINTER_STATE_PRINTING) || (printerConfigs[i].state == PRINTER_STATE_PAUSED)) {
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_PROG);
lineData.replace("%P%", String(printerConfigs[i].progressCompletion) + "%");
server->sendContent(lineData);
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_HR));
int val = printerConfigs[i].progressPrintTime;
int hours = globalDataController->numberOfHours(val);
int minutes = globalDataController->numberOfMinutes(val);
int seconds = globalDataController->numberOfSeconds(val);
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Printing Time");
lineData.replace("%V%", globalDataController->zeroPad(hours) + ":" + globalDataController->zeroPad(minutes) + ":" + globalDataController->zeroPad(seconds));
server->sendContent(lineData);
val = printerConfigs[i].progressPrintTimeLeft;
hours = globalDataController->numberOfHours(val);
minutes = globalDataController->numberOfMinutes(val);
seconds = globalDataController->numberOfSeconds(val);
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Est. Print Time Left");
lineData.replace("%V%", globalDataController->zeroPad(hours) + ":" + globalDataController->zeroPad(minutes) + ":" + globalDataController->zeroPad(seconds));
server->sendContent(lineData);
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_HR));
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "File");
lineData.replace("%V%", String(printerConfigs[i].fileName));
server->sendContent(lineData);
float fileSize = printerConfigs[i].fileSize;
if (fileSize > 0) {
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Filesize");
lineData.replace("%V%", String(fileSize) + " KB");
server->sendContent(lineData);
}
int filamentLength = printerConfigs[i].filamentLength;
if (filamentLength > 0) {
float fLength = float(filamentLength) / 1000;
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Filament");
lineData.replace("%V%", String(fLength) + " m");
server->sendContent(lineData);
}
}
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_HR));
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Tool Temperature");
lineData.replace("%V%", String(printerConfigs[i].toolTemp) + "&#176; C [" + String(printerConfigs[i].toolTargetTemp) + "]");
server->sendContent(lineData);
if (printerConfigs[i].bedTemp > 0 ) {
lineData = FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_LINE);
lineData.replace("%T%", "Bed Temperature");
lineData.replace("%V%", String(printerConfigs[i].bedTemp) + "&#176; C [" + String(printerConfigs[i].bedTargetTemp) + "]");
server->sendContent(lineData);
}
}
}
server->sendContent(FPSTR(MAINPAGE_ROW_PRINTER_BLOCK_E));
colCnt++;
}
while(colCnt < 3) {
server->sendContent("<div class='bx--col bx--col--auto'></div>");
colCnt++;
}
server->sendContent(FPSTR(FORM_ITEM_ROW_END));
}
/** /**
* @brief Send out upload form for updates * @brief Send out upload form for updates
* @param server Send out instancce * @param server Send out instancce

View File

@ -11,7 +11,6 @@ static const char FORM_ITEM_ROW_START[] PROGMEM = "<div class='bx--row' %ROWEXTR
static const char FORM_ITEM_ROW_EXT[] PROGMEM = "bx--col bx--col--auto"; static const char FORM_ITEM_ROW_EXT[] PROGMEM = "bx--col bx--col--auto";
static const char FORM_ITEM_ROW_END[] PROGMEM = "</div>"; static const char FORM_ITEM_ROW_END[] PROGMEM = "</div>";
static const char FORM_ITEM_CHECKBOX[] PROGMEM = "<div class='%ROWEXT% bx--form-item' %DIVEXTRACLASS%>" static const char FORM_ITEM_CHECKBOX[] PROGMEM = "<div class='%ROWEXT% bx--form-item' %DIVEXTRACLASS%>"
"<input class='bx--toggle-input bx--toggle-input--small' id='%FORMID%' type='checkbox' name='%FORMID%' %CHECKED% %ONCHANGE%>" "<input class='bx--toggle-input bx--toggle-input--small' id='%FORMID%' type='checkbox' name='%FORMID%' %CHECKED% %ONCHANGE%>"
"<label class='bx--toggle-input__label' for='%FORMID%'>" "<label class='bx--toggle-input__label' for='%FORMID%'>"
@ -45,6 +44,25 @@ static const char FORM_ITEM_SUBMIT[] PROGMEM = "<div class='bx--form-item %ROWEX
"<button class='bx--btn bx--btn--primary' type='submit'>Save</button>" "<button class='bx--btn bx--btn--primary' type='submit'>Save</button>"
"</div>"; "</div>";
static const char ICON32_TEMP[] PROGMEM = "<svg id='icon' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' width='32' height='32' fill='currentColor'>"
"<path d='M13,17.26V6A4,4,0,0,0,5,6V17.26a7,7,0,1,0,8,0ZM9,4a2,2,0,0,1,2,2v7H7V6A2,2,0,0,1,9,4ZM9,28a5,5,0,0,1-2.5-9.33l.5-.28V15h4v3.39l.5.28A5,5,0,0,1,9,28Z' transform='translate(0 0)'/><rect x='20' y='4' width='10' height='2'/><rect x='20' y='10' width='7' height='2'/><rect x='20' y='16' width='10' height='2'/><rect x='20' y='22' width='7' height='2'/>"
"</svg>";
static const char ICON16_WIND[] PROGMEM = "<svg id='icon' xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 32 32' fill='currentColor'>"
"<path d='M21,15H8V13H21a3,3,0,1,0-3-3H16a5,5,0,1,1,5,5Z'/><path d='M23,28a5.0057,5.0057,0,0,1-5-5h2a3,3,0,1,0,3-3H4V18H23a5,5,0,0,1,0,10Z'/>"
"</svg>";
static const char ICON16_HUMIDITY[] PROGMEM = "<svg id='icon' xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 32 32' fill='currentColor'>"
"<path d='M23.4761,13.9932,16.8472,3.4365a1.04,1.04,0,0,0-1.6944,0L8.4941,14.0444A9.9861,9.9861,0,0,0,7,19a9,9,0,0,0,18,0A10.0632,10.0632,0,0,0,23.4761,13.9932ZM16,26.0005a7.0089,7.0089,0,0,1-7-7,7.978,7.978,0,0,1,1.2183-3.9438l.935-1.4888L21.2271,23.6411A6.9772,6.9772,0,0,1,16,26.0005Z'/>"
"</svg>";
static const char ICON16_PRESSURE[] PROGMEM = "<svg id='icon' xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 32 32' fill='currentColor'>"
"<path d='M17.5053,16l8.1591-7.2529A1,1,0,0,0,25,7H22V2H20V9h2.37L16,14.6621,9.63,9H12V2H10V7H7a1,1,0,0,0-.6646,1.7471L14.4945,16,6.3353,23.2529A1,1,0,0,0,7,25h3v5h2V23H9.63L16,17.3379,22.37,23H20v7h2V25h3a1,1,0,0,0,.6645-1.7471Z'/>"
"</svg>";
/** /**
* Webpage side menu right for main items * Webpage side menu right for main items
*/ */
@ -97,6 +115,7 @@ static const char HEADER_BLOCK1[] PROGMEM = "<!DOCTYPE HTML>"
static const char HEADER_BLOCK2[] PROGMEM = "<link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>" static const char HEADER_BLOCK2[] PROGMEM = "<link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>"
"<link rel='stylesheet' href='https://unpkg.com/carbon-components/css/carbon-components.min.css'></style>" "<link rel='stylesheet' href='https://unpkg.com/carbon-components/css/carbon-components.min.css'></style>"
"<link rel='stylesheet' href='https://use.fontawesome.com/releases/v5.15.1/css/all.css'>" "<link rel='stylesheet' href='https://use.fontawesome.com/releases/v5.15.1/css/all.css'>"
"<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/open-weather-icons@0.0.8/dist/css/open-weather-icons.css'>"
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>" "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>"
"<style>.hidden{display:none} .bx--form-item{margin-bottom:20px} .bx--table-column-menu{width: 3.25rem} .menitem{padding:6px 1rem;font-size:.875rem;font-weight:600;line-height:1.29;letter-spacing:.16px;display:flex;justify-content:space-between;text-decoration:none;color:#c6c6c6}</style>" "<style>.hidden{display:none} .bx--form-item{margin-bottom:20px} .bx--table-column-menu{width: 3.25rem} .menitem{padding:6px 1rem;font-size:.875rem;font-weight:600;line-height:1.29;letter-spacing:.16px;display:flex;justify-content:space-between;text-decoration:none;color:#c6c6c6}</style>"
"<script>function showhide(a,b) {var e=$(\"[data-sh='\"+b+\"']\");var f=$(\"#\" + a);if (f.checked||f.prop('checked')){e.removeClass('hidden');}else{e.addClass('hidden');}}</script>" "<script>function showhide(a,b) {var e=$(\"[data-sh='\"+b+\"']\");var f=$(\"#\" + a);if (f.checked||f.prop('checked')){e.removeClass('hidden');}else{e.addClass('hidden');}}</script>"
@ -139,7 +158,7 @@ static const char HEADER_BLOCK5[] PROGMEM = "</div>"
"</header>" "</header>"
"<script>function openSidebar(){document.getElementById('sidebar').classList.toggle('bx--header-panel--expanded');document.getElementById('chipinfo').classList.add('hidden');};function openChipInfo(){document.getElementById('sidebar').classList.remove('bx--header-panel--expanded');document.getElementById('chipinfo').classList.toggle('hidden');}</script>" "<script>function openSidebar(){document.getElementById('sidebar').classList.toggle('bx--header-panel--expanded');document.getElementById('chipinfo').classList.add('hidden');};function openChipInfo(){document.getElementById('sidebar').classList.remove('bx--header-panel--expanded');document.getElementById('chipinfo').classList.toggle('hidden');}</script>"
"<br><div class='bx--grid bx--grid--full-width' style='margin-top:60px'>" "<br><div class='bx--grid bx--grid--full-width' style='margin-top:60px'>"
"<div class='page-header' style='margin-bottom:20px'><h4 class='page-header__label'>"; "<div class='page-header' style='margin-bottom:20px;position:relative'><h4 class='page-header__label'>";
static const char HEADER_BLOCK_ERROR[] PROGMEM = "<div class='bx--inline-notification bx--inline-notification--error' role='alert' style='max-width:100%'>" static const char HEADER_BLOCK_ERROR[] PROGMEM = "<div class='bx--inline-notification bx--inline-notification--error' role='alert' style='max-width:100%'>"
"<div class='bx--inline-notification__details'>" "<div class='bx--inline-notification__details'>"
@ -453,14 +472,9 @@ static const char CONFPRINTER_FORM_ADDEDIT_END[] PROGMEM = "<br><br></div>"
"</div>" "</div>"
"</div>"; "</div>";
/**
* Danger modal
*/
static const char MODAL_DANGER[] PROGMEM = "<div data-modal id='%ID%' class='bx--modal bx--modal--danger' role='dialog' aria-modal='true' aria-labelledby='%ID%-label' aria-describedby='%ID%-heading' tabindex='-1'>" static const char MODAL_DANGER[] PROGMEM = "<div data-modal id='%ID%' class='bx--modal bx--modal--danger' role='dialog' aria-modal='true' aria-labelledby='%ID%-label' aria-describedby='%ID%-heading' tabindex='-1'>"
"<div class='bx--modal-container'>" "<div class='bx--modal-container'>"
"<div class='bx--modal-header'>" "<div class='bx--modal-header'>"
@ -482,6 +496,92 @@ static const char MODAL_DANGER[] PROGMEM = "<div data-modal id='%ID%' class='bx-
"<span tabindex='0'></span>" "<span tabindex='0'></span>"
"</div>"; "</div>";
/**
* Controls for main page
*/
static const char MAINPAGE_ROW_WEATHER_AND_SENSOR_START[] PROGMEM = "<div class='bx--col bx--col--auto'>"
"<div class='bx--inline-notification bx--inline-notification--info' style='max-width:100%'>"
"<div class='bx--inline-notification__details'>"
"<i class='bx--inline-notification__icon owi owi-%ICON%' style='font-size: 90px; margin: 3rem 1rem;'></i>"
"<div class='bx--inline-notification__text-wrapper' style='width:100%'>"
"<div class='bx--grid bx--grid--full-width' style='width:100%'>"
"<div class='bx--row'>";
static const char MAINPAGE_ROW_WEATHER_ERROR_BLOCK[] PROGMEM = "<div class='bx--col bx--col--auto' style='margin: 2rem 0;'>"
"<div class='bx--grid bx--grid--full-width'>"
"<div class='bx--inline-notification bx--inline-notification--error'>"
"<div class='bx--inline-notification__details'>"
"<div>"
"<p class='bx--inline-notification__title'>Please <a href='/configureweather/show'>Configure Weather</a></p>"
"<p class='bx--inline-notification__subtitle'>%ERRORMSG%</p>"
"</div>"
"</div>"
"</div>"
"</div>"
"</div>";
static const char MAINPAGE_ROW_WEATHER_AND_SENSOR_BLOCK[] PROGMEM = "<div class='bx--col bx--col--auto' style='margin: 2rem 0;'>"
"<div class='bx--grid bx--grid--full-width'>"
"<div class='bx--row'>"
"<div class='bx--col bx--col--auto'>"
"<h2>%BTITLE% <div class='bx--tag bx--tag--cool-gray'><span class='bx--tag__label'>%BLABEL%</span></div></h2>"
"</div>"
"</div>"
"<div class='bx--row'>"
"<div class='bx--col bx--col--auto'>"
"<h1>%TEMPICON% %TEMPERATURE%</h1>"
"</div>"
"</div>"
"<div class='bx--row'>"
"<div class='bx--col bx--col--auto'>"
"%EXTRABLOCK%"
"</div>"
"</div>"
"<div class='bx--row'>"
"<div class='bx--col bx--col--auto'>"
"%ICONA% %TEXTA%"
"%ICONB% %TEXTB%"
"</div>"
"</div>"
"</div>"
"</div>";
static const char MAINPAGE_ROW_WEATHER_AND_SENSOR_END[] PROGMEM = "</div>"
"</div>"
"</div>"
"</div>"
"</div>"
"</div>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_S_PRINTING[] PROGMEM = "<div class='bx--col bx--col--auto'>"
"<div class='bx--inline-notification bx--inline-notification--info bx--inline-notification--low-contrast'>"
"<div class='bx--inline-notification__details'>"
"<svg focusable='false' preserveAspectRatio='xMidYMid meet' style='will-change: transform;' xmlns='http://www.w3.org/2000/svg' class='bx--inline-notification__icon' width='20' height='20' viewBox='0 0 32 32' aria-hidden='true'><path d='M16,2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Zm0,5a1.5,1.5,0,1,1-1.5,1.5A1.5,1.5,0,0,1,16,7Zm4,17.12H12V21.88h2.88V15.12H13V12.88h4.13v9H20Z'></path></svg>"
"<div style='margin: .5rem 0;width: 100%;'>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_S_ERROROFFLINE[] PROGMEM = "<div class='bx--col bx--col--auto'>"
"<div class='bx--inline-notification bx--inline-notification--error bx--inline-notification--low-contrast'>"
"<div class='bx--inline-notification__details'>"
"<svg focusable='false' preserveAspectRatio='xMidYMid meet' style='will-change: transform;' xmlns='http://www.w3.org/2000/svg' class='bx--inline-notification__icon' width='20' height='20' viewBox='0 0 20 20' aria-hidden='true'><path d='M10,1c-5,0-9,4-9,9s4,9,9,9s9-4,9-9S15,1,10,1z M13.5,14.5l-8-8l1-1l8,8L13.5,14.5z'></path><path d='M13.5,14.5l-8-8l1-1l8,8L13.5,14.5z' data-icon-path='inner-path' opacity='0'></path></svg>"
"<div style='margin: .5rem 0;width: 100%;'>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_S_STANDBY[] PROGMEM = "<div class='bx--col bx--col--auto'>"
"<div class='bx--inline-notification bx--inline-notification--success bx--inline-notification--low-contrast'>"
"<div class='bx--inline-notification__details'>"
"<svg focusable='false' preserveAspectRatio='xMidYMid meet' style='will-change: transform;' xmlns='http://www.w3.org/2000/svg' class='bx--inline-notification__icon' width='20' height='20' viewBox='0 0 20 20' aria-hidden='true'><path d='M10,1c-4.9,0-9,4.1-9,9s4.1,9,9,9s9-4,9-9S15,1,10,1z M8.7,13.5l-3.2-3.2l1-1l2.2,2.2l4.8-4.8l1,1L8.7,13.5z'></path><path fill='none' d='M8.7,13.5l-3.2-3.2l1-1l2.2,2.2l4.8-4.8l1,1L8.7,13.5z' data-icon-path='inner-path' opacity='0'></path></svg>"
"<div style='margin: .5rem 0;width: 100%;'>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_TITLE[] PROGMEM = "<p class='bx--inline-notification__title'>%NAME% <span class='bx--tag bx--tag--gray bx--tag__label'>%API%</span></p>"
"<p class='bx--inline-notification__subtitle'>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_LINE[] PROGMEM = "<div><strong>%T%:</strong> %V%</div>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_PROG[] PROGMEM = "<div class='pStateBar'><div class='pStateBarD' style='width: %P%'>%P%</div></div>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_HR[] PROGMEM = "<hr class='pStateHr'>";
static const char MAINPAGE_ROW_PRINTER_BLOCK_E[] PROGMEM = "</p></div></div></div></div>";
/** /**
* @brief Class to generate HTML content from Memory * @brief Class to generate HTML content from Memory
*/ */
@ -494,6 +594,7 @@ public:
static void sendHeader(ESP8266WebServer *server, GlobalDataController *globalDataController, String pageLabel, String pageTitle, boolean refresh); static void sendHeader(ESP8266WebServer *server, GlobalDataController *globalDataController, String pageLabel, String pageTitle, boolean refresh);
static void sendFooter(ESP8266WebServer *server, GlobalDataController *globalDataController); static void sendFooter(ESP8266WebServer *server, GlobalDataController *globalDataController);
static void sendMainPage(ESP8266WebServer *server, GlobalDataController *globalDataController);
static void sendUpdateForm(ESP8266WebServer *server, GlobalDataController *globalDataController); static void sendUpdateForm(ESP8266WebServer *server, GlobalDataController *globalDataController);
static void sendWeatherConfigForm(ESP8266WebServer *server, GlobalDataController *globalDataController); static void sendWeatherConfigForm(ESP8266WebServer *server, GlobalDataController *globalDataController);
static void sendStationConfigForm(ESP8266WebServer *server, GlobalDataController *globalDataController); static void sendStationConfigForm(ESP8266WebServer *server, GlobalDataController *globalDataController);

View File

@ -8,6 +8,7 @@
<meta name='viewport' content='width=device-width, initial-scale=1'> <meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'> <link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>
<link rel='stylesheet' href='https://unpkg.com/carbon-components/css/carbon-components.min.css'> <link rel='stylesheet' href='https://unpkg.com/carbon-components/css/carbon-components.min.css'>
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/open-weather-icons@0.0.8/dist/css/open-weather-icons.css'>
</style> </style>
<link rel='stylesheet' href='https://use.fontawesome.com/releases/v5.15.1/css/all.css'> <link rel='stylesheet' href='https://use.fontawesome.com/releases/v5.15.1/css/all.css'>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script> <script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
@ -176,10 +177,224 @@
<script>function openSidebar() { document.getElementById('sidebar').classList.toggle('bx--header-panel--expanded'); document.getElementById('chipinfo').classList.add('hidden'); }; function openChipInfo() { document.getElementById('sidebar').classList.remove('bx--header-panel--expanded'); document.getElementById('chipinfo').classList.toggle('hidden'); }</script> <script>function openSidebar() { document.getElementById('sidebar').classList.toggle('bx--header-panel--expanded'); document.getElementById('chipinfo').classList.add('hidden'); }; function openChipInfo() { document.getElementById('sidebar').classList.remove('bx--header-panel--expanded'); document.getElementById('chipinfo').classList.toggle('hidden'); }</script>
<br> <br>
<div class='bx--grid bx--grid--full-width' style='margin-top:60px'> <div class='bx--grid bx--grid--full-width' style='margin-top:60px'>
<div class='page-header' style='margin-bottom:20px'> <div class='page-header' style='margin-bottom:20px;position:relative'>
<h4 class='page-header__label'>Status</h4> <h4 class='page-header__label'>Status</h4>
<h1 id='page-title' class='page-header__title'>Monitor</h1> <h1 id='page-title' class='page-header__title'>Monitor</h1>
<div style="position:absolute;right:0;top:0;text-align:right">Current time<br>21:04:46</div>
</div> </div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<div data-notification class="bx--inline-notification bx--inline-notification--info" role="alert" style="max-width:100%">
<div class="bx--inline-notification__details">
<i class="bx--inline-notification__icon owi owi-02n" style="font-size: 90px; margin: 3rem 1rem;"></i>
<div class="bx--inline-notification__text-wrapper" style="width:100%">
<div class='bx--grid bx--grid--full-width' style="width:100%">
<div class="bx--row">
<div class="bx--col bx--col--auto" style="margin: 2rem 0;">
<div class='bx--grid bx--grid--full-width'>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<h2>MyCity, XX <div class="bx--tag bx--tag--cool-gray"><span class="bx--tag__label">lat: 00.00, lon: 00.00</span></div></h2>
</div>
</div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<h1>
<svg id="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="currentColor">
<path d="M13,17.26V6A4,4,0,0,0,5,6V17.26a7,7,0,1,0,8,0ZM9,4a2,2,0,0,1,2,2v7H7V6A2,2,0,0,1,9,4ZM9,28a5,5,0,0,1-2.5-9.33l.5-.28V15h4v3.39l.5.28A5,5,0,0,1,9,28Z" transform="translate(0 0)"/><rect x="20" y="4" width="10" height="2"/><rect x="20" y="10" width="7" height="2"/><rect x="20" y="16" width="10" height="2"/><rect x="20" y="22" width="7" height="2"/>
</svg>
12°C
</h1>
</div>
</div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
Condition: Mäßig bewölkt
</div>
</div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<svg id="icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32" fill="currentColor">
<path d="M21,15H8V13H21a3,3,0,1,0-3-3H16a5,5,0,1,1,5,5Z"/><path d="M23,28a5.0057,5.0057,0,0,1-5-5h2a3,3,0,1,0,3-3H4V18H23a5,5,0,0,1,0,10Z"/>
</svg>
8.70 km/h Winds
<svg id="icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32" fill="currentColor">
<path d="M23.4761,13.9932,16.8472,3.4365a1.04,1.04,0,0,0-1.6944,0L8.4941,14.0444A9.9861,9.9861,0,0,0,7,19a9,9,0,0,0,18,0A10.0632,10.0632,0,0,0,23.4761,13.9932ZM16,26.0005a7.0089,7.0089,0,0,1-7-7,7.978,7.978,0,0,1,1.2183-3.9438l.935-1.4888L21.2271,23.6411A6.9772,6.9772,0,0,1,16,26.0005Z"/>
</svg>
87% Humidity
</div>
</div>
</div>
</div>
<div class="bx--col bx--col--auto" style="margin: 2rem 0;">
<div class='bx--grid bx--grid--full-width'>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<h2>Sensor</h2>
</div>
</div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<h1>
<svg id="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="currentColor">
<path d="M13,17.26V6A4,4,0,0,0,5,6V17.26a7,7,0,1,0,8,0ZM9,4a2,2,0,0,1,2,2v7H7V6A2,2,0,0,1,9,4ZM9,28a5,5,0,0,1-2.5-9.33l.5-.28V15h4v3.39l.5.28A5,5,0,0,1,9,28Z" transform="translate(0 0)"/><rect x="20" y="4" width="10" height="2"/><rect x="20" y="10" width="7" height="2"/><rect x="20" y="16" width="10" height="2"/><rect x="20" y="22" width="7" height="2"/>
</svg>
24.9°C
</h1>
</div>
</div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<svg id="icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32" fill="currentColor">
<path d="M17.5053,16l8.1591-7.2529A1,1,0,0,0,25,7H22V2H20V9h2.37L16,14.6621,9.63,9H12V2H10V7H7a1,1,0,0,0-.6646,1.7471L14.4945,16,6.3353,23.2529A1,1,0,0,0,7,25h3v5h2V23H9.63L16,17.3379,22.37,23H20v7h2V25h3a1,1,0,0,0,.6645-1.7471Z"/>
</svg>
1002 hPa
<svg id="icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32" fill="currentColor">
<path d="M23.4761,13.9932,16.8472,3.4365a1.04,1.04,0,0,0-1.6944,0L8.4941,14.0444A9.9861,9.9861,0,0,0,7,19a9,9,0,0,0,18,0A10.0632,10.0632,0,0,0,23.4761,13.9932ZM16,26.0005a7.0089,7.0089,0,0,1-7-7,7.978,7.978,0,0,1,1.2183-3.9438l.935-1.4888L21.2271,23.6411A6.9772,6.9772,0,0,1,16,26.0005Z"/>
</svg>
38% Humidity
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<div data-notification class="bx--inline-notification bx--inline-notification--info" role="alert" style="max-width:100%">
<div class="bx--inline-notification__details">
<i class="bx--inline-notification__icon owi owi-" style="font-size: 90px; margin: 3rem 1rem;"></i>
<div class="bx--inline-notification__text-wrapper" style="width:100%">
<div class='bx--grid bx--grid--full-width' style="width:100%">
<div class="bx--row">
<div class="bx--col bx--col--auto" style="margin: 2rem 0;">
<div class='bx--grid bx--grid--full-width'>
<div data-notification class="bx--inline-notification bx--inline-notification--error" role="alert">
<div class="bx--inline-notification__details">
<div>
<p class="bx--inline-notification__title">Please <a href='/configureweather/show'>Configure Weather</a></p>
<p class="bx--inline-notification__subtitle">Weather Error:</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.pStateBar {
width: 100%;
background-color: #ddd;
margin: 5px 0;
}
.pStateBarD {
height: 20px;
background-color: #24a148;
text-align: center!important;
font-size: 13px!important;
color: #ffffff;
padding: 4px 0;
}
.pStateHr {
border-top: 1px solid #0043ce;margin: 5px 0;
}
</style>
<div class="bx--row">
<div class="bx--col bx--col--auto">
<div class="bx--inline-notification bx--inline-notification--info bx--inline-notification--low-contrast">
<div class="bx--inline-notification__details">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" class="bx--inline-notification__icon" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true"><path d="M16,2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Zm0,5a1.5,1.5,0,1,1-1.5,1.5A1.5,1.5,0,0,1,16,7Zm4,17.12H12V21.88h2.88V15.12H13V12.88h4.13v9H20Z"></path></svg>
<div style="margin: .5rem 0;width: 100%;">
<p class="bx--inline-notification__title">AnycubicI3 <span class="bx--tag bx--tag--gray bx--tag__label">Klipper</span></p>
<p class="bx--inline-notification__subtitle">
<div><strong>Host:</strong> 192.168.0.241:7125</div>
<div><strong>Status:</strong> Printing</div>
<div class="pStateBar"><div class="pStateBarD" style="width: 30%;">30%</div></div>
<hr class="pStateHr">
<div><strong>Printing Time:</strong> 29:18:28</div>
<div><strong>Est. Print Time Left:</strong> 00:01:00</div>
<hr class="pStateHr">
<div><strong>Tool Temperature:</strong> 215&#176; C [215]</div>
<div><strong>Bed Temperature:</strong> 70&#176; C [72]</div>
<hr class="pStateHr">
<div><strong>File:</strong> blablabla.gcode</div>
<div><strong>Filesize:</strong> 2089 KB</div>
<div><strong>Filament:</strong> 219 m</div>
</p>
</div>
</div>
</div>
</div>
<div class="bx--col bx--col--auto">
<div class="bx--inline-notification bx--inline-notification--error bx--inline-notification--low-contrast">
<div class="bx--inline-notification__details">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" class="bx--inline-notification__icon" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path d="M10,1c-5,0-9,4-9,9s4,9,9,9s9-4,9-9S15,1,10,1z M13.5,14.5l-8-8l1-1l8,8L13.5,14.5z"></path><path d="M13.5,14.5l-8-8l1-1l8,8L13.5,14.5z" data-icon-path="inner-path" opacity="0"></path></svg>
<div style="margin: .5rem 0;">
<p class="bx--inline-notification__title">AnycubicI3 <span class="bx--tag bx--tag--gray bx--tag__label">Klipper</span></p>
<p class="bx--inline-notification__subtitle">
<div><strong>Host:</strong> 192.168.0.241:7125</div>
<div><strong>Status:</strong> Error</div>
<div><strong>Reason:</strong> Server address or host name is required</div>
</p>
</div>
</div>
</div>
</div>
<div class="bx--col bx--col--auto">
<div class="bx--inline-notification bx--inline-notification--success bx--inline-notification--low-contrast">
<div class="bx--inline-notification__details">
<svg focusable="false" preserveAspectRatio="xMidYMid meet" style="will-change: transform;" xmlns="http://www.w3.org/2000/svg" class="bx--inline-notification__icon" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path d="M10,1c-4.9,0-9,4.1-9,9s4.1,9,9,9s9-4,9-9S15,1,10,1z M8.7,13.5l-3.2-3.2l1-1l2.2,2.2l4.8-4.8l1,1L8.7,13.5z"></path><path fill="none" d="M8.7,13.5l-3.2-3.2l1-1l2.2,2.2l4.8-4.8l1,1L8.7,13.5z" data-icon-path="inner-path" opacity="0"></path></svg>
<div style="margin: .5rem 0;">
<p class="bx--inline-notification__title">AnycubicI3 <span class="bx--tag bx--tag--gray bx--tag__label">Klipper</span></p>
<p class="bx--inline-notification__subtitle">
<div><strong>Host:</strong> 192.168.0.241:7125</div>
<div><strong>Status:</strong> Standby</div>
</p>
</div>
</div>
</div>
</div>
</div>
Hallo
<div data-modal id='resetSettingsModal' class='bx--modal bx--modal--danger' role='dialog' aria-modal='true' <div data-modal id='resetSettingsModal' class='bx--modal bx--modal--danger' role='dialog' aria-modal='true'
aria-labelledby='resetSettingsModal-label' aria-describedby='resetSettingsModal-heading' tabindex='-1'> aria-labelledby='resetSettingsModal-label' aria-describedby='resetSettingsModal-heading' tabindex='-1'>
<div class='bx--modal-container'> <div class='bx--modal-container'>