//----------------------------------------------------------------------------- // 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- #include "web.h" #include "html/h/index_html.h" #include "html/h/style_css.h" #include "favicon.h" #include "html/h/setup_html.h" #include "html/h/visualization_html.h" //----------------------------------------------------------------------------- web::web(app *main, sysConfig_t *sysCfg, config_t *config, char version[]) { mMain = main; mSysCfg = sysCfg; mConfig = config; mVersion = version; mWeb = new ESP8266WebServer(80); mUpdater = new ESP8266HTTPUpdateServer(); mUpdater->setup(mWeb); } //----------------------------------------------------------------------------- void web::setup(void) { mWeb->begin(); mWeb->on("/", std::bind(&web::showIndex, this)); mWeb->on("/style.css", std::bind(&web::showCss, this)); mWeb->on("/favicon.ico", std::bind(&web::showFavicon, this)); mWeb->onNotFound ( std::bind(&web::showNotFound, this)); mWeb->on("/uptime", std::bind(&web::showUptime, this)); mWeb->on("/reboot", std::bind(&web::showReboot, this)); mWeb->on("/erase", std::bind(&web::showErase, this)); mWeb->on("/factory", std::bind(&web::showFactoryRst, this)); mWeb->on("/setup", std::bind(&web::showSetup, this)); mWeb->on("/save", std::bind(&web::showSave, this)); mWeb->on("/cmdstat", std::bind(&web::showStatistics, this)); mWeb->on("/visualization", std::bind(&web::showVisualization, this)); mWeb->on("/livedata", std::bind(&web::showLiveData, this)); mWeb->on("/json", std::bind(&web::showJson, this)); mWeb->on("/api", HTTP_POST, std::bind(&web::showWebApi, this)); } //----------------------------------------------------------------------------- void web::loop(void) { mWeb->handleClient(); } //----------------------------------------------------------------------------- void web::showIndex(void) { DPRINTLN(DBG_VERBOSE, F("showIndex")); String html = FPSTR(index_html); html.replace(F("{DEVICE}"), mSysCfg->deviceName); html.replace(F("{VERSION}"), mVersion); html.replace(F("{TS}"), String(mConfig->sendInterval) + " "); html.replace(F("{JS_TS}"), String(mConfig->sendInterval * 1000)); html.replace(F("{BUILD}"), String(AUTO_GIT_HASH)); mWeb->send(200, "text/html", html); } //----------------------------------------------------------------------------- void web::showCss(void) { mWeb->send(200, "text/css", FPSTR(style_css)); } //----------------------------------------------------------------------------- void web::showFavicon(void) { static const char favicon_type[] PROGMEM = "image/x-icon"; static const char favicon_content[] PROGMEM = FAVICON_PANEL_16; mWeb->send_P(200, favicon_type, favicon_content, sizeof(favicon_content)); } //----------------------------------------------------------------------------- void web::showNotFound(void) { DPRINTLN(DBG_VERBOSE, F("showNotFound - ") + mWeb->uri()); String msg = F("File Not Found\n\nURI: "); msg += mWeb->uri(); mWeb->send(404, F("text/plain"), msg); } //----------------------------------------------------------------------------- void web::showUptime(void) { char time[21] = {0}; uint32_t uptime = mMain->getUptime(); uint32_t upTimeSc = uint32_t((uptime) % 60); uint32_t upTimeMn = uint32_t((uptime / (60)) % 60); uint32_t upTimeHr = uint32_t((uptime / (60 * 60)) % 24); uint32_t upTimeDy = uint32_t((uptime / (60 * 60 * 24)) % 365); snprintf(time, 20, "%d Days, %02d:%02d:%02d", upTimeDy, upTimeHr, upTimeMn, upTimeSc); mWeb->send(200, "text/plain", String(time) + "; now: " + mMain->getDateTimeStr(mMain->getTimestamp())); } //----------------------------------------------------------------------------- void web::showReboot(void) { mWeb->send(200, F("text/html"), F("
Inverter ") + String(i) + "
"; inv += F(""); inv += F("serial.u64, HEX); inv += F("\"/ maxlength=\"12\" onkeyup=\"checkSerial()\">"); inv += F(""); inv += F("name); inv += F("\"/ maxlength=\"") + String(MAX_NAME_LENGTH) + "\">"; inv += F(""); inv += F("powerLimit[0]); inv += F("\"/ maxlength=\"") + String(6) + "\">"; inv += F(""); inv += F(""); if(iv->powerLimit[1] == RelativNonPersistent) inv += F("PowerLimitControl\">"); if(iv->powerLimit[1] == AbsolutPersistent) inv += F("PowerLimitControl\">"); if(iv->powerLimit[1] == RelativPersistent) inv += F("PowerLimitControl\">"); } else inv += F("PowerLimitControl\">"); // UGLY! But I do not know it a better way --// inv += F(""); for(uint8_t j = 0; j < 4; j++) { inv += F("chMaxPwr[j]); inv += F("\"/ maxlength=\"4\">"); } inv += F("saved
")); } } //----------------------------------------------------------------------------- void web::showStatistics(void) { DPRINTLN(DBG_VERBOSE, F("web::showStatistics")); mWeb->send(200, F("text/plain"), mMain->getStatistics()); } //----------------------------------------------------------------------------- void web::showVisualization(void) { DPRINTLN(DBG_VERBOSE, F("web::showVisualization")); String html = FPSTR(visualization_html); html.replace(F("{DEVICE}"), mSysCfg->deviceName); html.replace(F("{VERSION}"), mVersion); html.replace(F("{TS}"), String(mConfig->sendInterval) + " "); html.replace(F("{JS_TS}"), String(mConfig->sendInterval * 1000)); mWeb->send(200, F("text/html"), html); } //----------------------------------------------------------------------------- void web::showLiveData(void) { DPRINTLN(DBG_VERBOSE, F("web::showLiveData")); mWeb->send(200, F("text/html"), mMain->getLiveData()); } //----------------------------------------------------------------------------- void web::showJson(void) { DPRINTLN(DBG_VERBOSE, F("web::showJson")); mWeb->send(200, F("application/json"), mMain->getJson()); } //----------------------------------------------------------------------------- void web::showWebApi(void) { DPRINTLN(DBG_VERBOSE, F("web::showWebApi")); DPRINTLN(DBG_DEBUG, mWeb->arg("plain")); const size_t capacity = 200; // Use arduinojson.org/assistant to compute the capacity. DynamicJsonDocument response(capacity); // Parse JSON object deserializeJson(response, mWeb->arg("plain")); // ToDo: error handling for payload uint8_t iv_id = response["inverter"]; Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id); if (NULL != iv) { if (response["tx_request"] == (uint8_t)TX_REQ_INFO) { //mMain->resetPayload(iv); // start request from new mMain->mSys->NextInfoCmd = response["cmd"]; // process payload from web request corresponding to the cmd if (mMain->mSys->NextInfoCmd == AlarmData) iv->alarmMesIndex = response["payload"]; DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(mMain->mSys->InfoCmd) + F(" and payload ") + String(response["payload"])); } if (response["tx_request"] == (uint8_t)TX_REQ_DEVCONTROL) { if (response["cmd"] == (uint8_t)ActivePowerContr) { uint16_t webapiPayload = response["payload"]; uint16_t webapiPayload2 = response["payload2"]; if (webapiPayload > 0 && webapiPayload < 10000) { iv->devControlCmd = ActivePowerContr; iv->powerLimit[0] = webapiPayload; if (webapiPayload2 > 0) { iv->powerLimit[1] = webapiPayload2; // dev option, no sanity check } else { // if not set, set it to 0x0000 default iv->powerLimit[1] = AbsolutNonPersistent; // payload will be seted temporay in Watt absolut } if (iv->powerLimit[1] & 0x0001) { DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("% via REST API")); } else { DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W via REST API")); } iv->devControlRequest = true; // queue it in the request loop } } } } mWeb->send(200, "text/json", "{success:true}"); }