#include "AmsWebServer.h" #include "AmsWebHeaders.h" #include "version.h" #include "hexutils.h" #include "AmsData.h" #if defined(ESP32) #include #endif #include "root/head_html.h" #include "root/foot_html.h" #include "root/index_html.h" #include "root/application_js.h" #include "root/setup_html.h" #include "root/meter_html.h" #include "root/wifi_html.h" #include "root/mqtt_html.h" #include "root/web_html.h" #include "root/domoticz_html.h" #include "root/entsoe_html.h" #include "root/ntp_html.h" #include "root/gpio_html.h" #include "root/debugging_html.h" #include "root/restart_html.h" #include "root/restartwait_html.h" #include "root/boot_css.h" #include "root/github_svg.h" #include "root/upload_html.h" #include "root/firmware_html.h" #include "root/delete_html.h" #include "root/reset_html.h" #include "root/temperature_html.h" #include "root/notfound_html.h" #include "root/data_json.h" #include "root/tempsensor_json.h" #include "root/dayplot_json.h" #include "root/monthplot_json.h" #include "root/energyprice_json.h" #include "root/thresholds_html.h" #include "root/configfile_html.h" #include "root/meteradvanced_html.h" #include "base64.h" AmsWebServer::AmsWebServer(uint8_t* buf, RemoteDebug* Debug, HwTools* hw) { this->debugger = Debug; this->hw = hw; this->buf = (char*) buf; } void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, MeterConfig* meterConfig, AmsData* meterState, AmsDataStorage* ds, EnergyAccounting* ea) { this->config = config; this->gpioConfig = gpioConfig; this->meterConfig = meterConfig; this->meterState = meterState; this->ds = ds; this->ea = ea; snprintf_P(buf, 32, PSTR("/application-%s.js"), VERSION); server.on(F("/"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on(F("/"), HTTP_POST, std::bind(&AmsWebServer::handleSetup, this)); server.on(buf, HTTP_GET, std::bind(&AmsWebServer::applicationJs, this)); server.on(F("/temperature"), HTTP_GET, std::bind(&AmsWebServer::temperature, this)); server.on(F("/temperature"), HTTP_POST, std::bind(&AmsWebServer::temperaturePost, this)); server.on(F("/temperature.json"), HTTP_GET, std::bind(&AmsWebServer::temperatureJson, this)); server.on(F("/meter"), HTTP_GET, std::bind(&AmsWebServer::configMeterHtml, this)); server.on(F("/meteradvanced"), HTTP_GET, std::bind(&AmsWebServer::configMeterAdvancedHtml, this)); server.on(F("/wifi"), HTTP_GET, std::bind(&AmsWebServer::configWifiHtml, this)); server.on(F("/mqtt"), HTTP_GET, std::bind(&AmsWebServer::configMqttHtml, this)); server.on(F("/web"), HTTP_GET, std::bind(&AmsWebServer::configWebHtml, this)); server.on(F("/domoticz"),HTTP_GET, std::bind(&AmsWebServer::configDomoticzHtml, this)); server.on(F("/entsoe"),HTTP_GET, std::bind(&AmsWebServer::configEntsoeHtml, this)); server.on(F("/thresholds"),HTTP_GET, std::bind(&AmsWebServer::configThresholdsHtml, this)); server.on(F("/boot.css"), HTTP_GET, std::bind(&AmsWebServer::bootCss, this)); server.on(F("/github.svg"), HTTP_GET, std::bind(&AmsWebServer::githubSvg, this)); server.on(F("/data.json"), HTTP_GET, std::bind(&AmsWebServer::dataJson, this)); server.on(F("/dayplot.json"), HTTP_GET, std::bind(&AmsWebServer::dayplotJson, this)); server.on(F("/monthplot.json"), HTTP_GET, std::bind(&AmsWebServer::monthplotJson, this)); server.on(F("/energyprice.json"), HTTP_GET, std::bind(&AmsWebServer::energyPriceJson, this)); server.on(F("/configfile"),HTTP_GET, std::bind(&AmsWebServer::configFileHtml, this)); server.on(F("/configfile"), HTTP_POST, std::bind(&AmsWebServer::uploadPost, this), std::bind(&AmsWebServer::configFileUpload, this)); server.on(F("/configfile.cfg"),HTTP_GET, std::bind(&AmsWebServer::configFileDownload, this)); server.on(F("/save"), HTTP_POST, std::bind(&AmsWebServer::handleSave, this)); server.on(F("/ntp"), HTTP_GET, std::bind(&AmsWebServer::configNtpHtml, this)); server.on(F("/gpio"), HTTP_GET, std::bind(&AmsWebServer::configGpioHtml, this)); server.on(F("/debugging"), HTTP_GET, std::bind(&AmsWebServer::configDebugHtml, this)); server.on(F("/firmware"), HTTP_GET, std::bind(&AmsWebServer::firmwareHtml, this)); server.on(F("/firmware"), HTTP_POST, std::bind(&AmsWebServer::firmwarePost, this), std::bind(&AmsWebServer::firmwareUpload, this)); server.on(F("/upgrade"), HTTP_GET, std::bind(&AmsWebServer::firmwareDownload, this)); server.on(F("/restart"), HTTP_GET, std::bind(&AmsWebServer::restartHtml, this)); server.on(F("/restart"), HTTP_POST, std::bind(&AmsWebServer::restartPost, this)); server.on(F("/restart-wait"), HTTP_GET, std::bind(&AmsWebServer::restartWaitHtml, this)); server.on(F("/is-alive"), HTTP_GET, std::bind(&AmsWebServer::isAliveCheck, this)); server.on(F("/mqtt-ca"), HTTP_GET, std::bind(&AmsWebServer::mqttCa, this)); server.on(F("/mqtt-ca"), HTTP_POST, std::bind(&AmsWebServer::mqttCaDelete, this), std::bind(&AmsWebServer::mqttCaUpload, this)); server.on(F("/mqtt-cert"), HTTP_GET, std::bind(&AmsWebServer::mqttCert, this)); server.on(F("/mqtt-cert"), HTTP_POST, std::bind(&AmsWebServer::mqttCertDelete, this), std::bind(&AmsWebServer::mqttCertUpload, this)); server.on(F("/mqtt-key"), HTTP_GET, std::bind(&AmsWebServer::mqttKey, this)); server.on(F("/mqtt-key"), HTTP_POST, std::bind(&AmsWebServer::mqttKeyDelete, this), std::bind(&AmsWebServer::mqttKeyUpload, this)); server.on(F("/reset"), HTTP_GET, std::bind(&AmsWebServer::factoryResetHtml, this)); server.on(F("/reset"), HTTP_POST, std::bind(&AmsWebServer::factoryResetPost, this)); server.onNotFound(std::bind(&AmsWebServer::notFound, this)); server.begin(); // Web server start config->getWebConfig(webConfig); MqttConfig mqttConfig; config->getMqttConfig(mqttConfig); mqttEnabled = strlen(mqttConfig.host) > 0; } void AmsWebServer::setMqtt(MQTTClient* mqtt) { this->mqtt = mqtt; } void AmsWebServer::setTimezone(Timezone* tz) { this->tz = tz; } void AmsWebServer::setMqttEnabled(bool enabled) { mqttEnabled = enabled; } void AmsWebServer::setEntsoeApi(EntsoeApi* eapi) { this->eapi = eapi; } void AmsWebServer::loop() { server.handleClient(); if(maxPwr == 0 && meterState->getListType() > 1 && meterConfig->mainFuse > 0 && meterConfig->distributionSystem > 0) { int voltage = meterConfig->distributionSystem == 2 ? 400 : 230; if(meterState->isThreePhase()) { maxPwr = meterConfig->mainFuse * sqrt(3) * voltage; } else if(meterState->isTwoPhase()) { maxPwr = meterConfig->mainFuse * voltage; } else { maxPwr = meterConfig->mainFuse * 230; } } } bool AmsWebServer::checkSecurity(byte level) { bool access = WiFi.getMode() == WIFI_AP || webConfig.security < level; if(!access && webConfig.security >= level && server.hasHeader(F("Authorization"))) { String expectedAuth = String(webConfig.username) + ":" + String(webConfig.password); String providedPwd = server.header(F("Authorization")); providedPwd.replace(F("Basic "), F("")); #if defined(ESP8266) String expectedBase64 = base64::encode(expectedAuth, false); #elif defined(ESP32) String expectedBase64 = base64::encode(expectedAuth); #endif debugger->printf_P(PSTR("Expected auth: %s\n"), expectedBase64.c_str()); debugger->printf_P(PSTR("Provided auth: %s\n"), providedPwd.c_str()); access = providedPwd.equals(expectedBase64); } if(!access) { server.sendHeader(HEADER_AUTHENTICATE, AUTHENTICATE_BASIC); server.setContentLength(0); server.send_P(401, MIME_HTML, PSTR("")); } return access; } void AmsWebServer::temperature() { printD(F("Serving /temperature.html over http...")); if(!checkSecurity(2)) return; server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(HEAD_HTML_LEN + TEMPERATURE_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(TEMPERATURE_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::temperaturePost() { if(!checkSecurity(1)) return; printD(F("Saving temperature sensors...")); for(int i = 0; i < 32; i++) { if(!server.hasArg("sensor" + String(i, DEC))) break; String address = server.arg("sensor" + String(i, DEC)); String name = server.arg("sensor" + String(i, DEC) + "name").substring(0,16); bool common = server.hasArg("sensor" + String(i, DEC) + "common") && server.arg("sensor" + String(i, DEC) + "common") == F("true"); if(debugger->isActive(RemoteDebug::DEBUG)) { debugger->printf("Addr: %s, name: %s\n", address.c_str(), name.c_str()); } uint8_t hexStr[8]; fromHex(hexStr, address, 8); config->updateTempSensorConfig(hexStr, name.c_str(), common); delay(1); } //if (debugger->isActive(RemoteDebug::DEBUG)) config->print(debugger); if(config->save()) { printD(F("Successfully saved temperature sensors")); server.sendHeader(HEADER_LOCATION, F("/temperature"), true); server.send (302, MIME_PLAIN, F("")); } else { printE(F("Error saving configuration")); server.send_P(500, MIME_HTML, PSTR("

Error saving configuration!

")); } } void AmsWebServer::temperatureJson() { printD(F("Serving /temperature.json over http...")); if(!checkSecurity(2)) return; int count = hw->getTempSensorCount(); snprintf_P(buf, 16, PSTR("{\"c\":%d,\"s\":["), count); for(int i = 0; i < count; i++) { TempSensorData* data = hw->getTempSensorData(i); if(data == NULL) continue; TempSensorConfig* conf = config->getTempSensorConfig(data->address); char* pos = buf+strlen(buf); snprintf_P(pos, 72, TEMPSENSOR_JSON, i, toHex(data->address, 8).c_str(), conf == NULL ? "" : String(conf->name).substring(0,16).c_str(), conf == NULL || conf->common ? 1 : 0, data->lastRead ); delay(10); } char* pos = buf+strlen(buf); snprintf_P(count == 0 ? pos : pos-1, 8, PSTR("]}")); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); } void AmsWebServer::indexHtml() { printD(F("Serving /index.html over http...")); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); if(WiFi.getMode() == WIFI_AP) { SystemConfig sys; config->getSystemConfig(sys); WiFiConfig wifi; config->clearWifi(wifi); String html = String((const __FlashStringHelper*) SETUP_HTML); for(int i = 0; i<255; i++) { html.replace("${config.boardType" + String(i) + "}", sys.boardType == i ? F("selected") : F("")); } html.replace(F("${config.wifiSsid}"), wifi.ssid); html.replace(F("${config.wifiPassword}"), wifi.psk); html.replace(F("${config.wifiStaticIp}"), strlen(wifi.ip) > 0 ? F("checked") : F("")); html.replace(F("${config.wifiIp}"), wifi.ip); html.replace(F("${config.wifiGw}"), wifi.gateway); html.replace(F("${config.wifiSubnet}"), wifi.subnet); html.replace(F("${config.wifiDns1}"), wifi.dns1); html.replace(F("${config.wifiDns2}"), wifi.dns2); html.replace(F("${config.wifiHostname}"), wifi.hostname); server.send(200, MIME_HTML, html); } else { if(!checkSecurity(2)) return; server.setContentLength(INDEX_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(INDEX_HTML); server.sendContent_P(FOOT_HTML); } } void AmsWebServer::applicationJs() { printD(F("Serving /application.js over http...")); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR); server.send_P(200, PSTR("application/javascript"), APPLICATION_JS); } void AmsWebServer::configMeterHtml() { printD(F("Serving /meter.html over http...")); if(!checkSecurity(1)) return; String html = String((const __FlashStringHelper*) METER_HTML); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); String manufacturer; switch(meterState->getMeterType()) { case AmsTypeAidon: manufacturer = F("Aidon"); break; case AmsTypeKaifa: manufacturer = F("Kaifa"); break; case AmsTypeKamstrup: manufacturer = F("Kamstrup"); break; case AmsTypeIskra: manufacturer = F("Iskra"); break; case AmsTypeLandis: manufacturer = F("Landis + Gyro"); break; case AmsTypeSagemcom: manufacturer = F("Sagemcom"); break; case AmsTypeLng: manufacturer = F("L&G"); break; default: manufacturer = F("Unknown"); break; } html.replace(F("{maf}"), manufacturer); html.replace(F("{mod}"), meterState->getMeterModel()); html.replace(F("{mid}"), meterState->getMeterId()); html.replace(F("{b}"), String(meterConfig->baud)); html.replace(F("{b2400}"), meterConfig->baud == 2400 ? F("selected") : F("")); html.replace(F("{b4800}"), meterConfig->baud == 4800 ? F("selected") : F("")); html.replace(F("{b9600}"), meterConfig->baud == 9600 ? F("selected") : F("")); html.replace(F("{b19200}"), meterConfig->baud == 19200 ? F("selected") : F("")); html.replace(F("{b38400}"), meterConfig->baud == 38400 ? F("selected") : F("")); html.replace(F("{b57600}"), meterConfig->baud == 57600 ? F("selected") : F("")); html.replace(F("{b115200}"), meterConfig->baud == 115200 ? F("selected") : F("")); html.replace(F("{c}"), String(meterConfig->baud)); html.replace(F("{c2}"), meterConfig->parity == 2 ? F("selected") : F("")); html.replace(F("{c3}"), meterConfig->parity == 3 ? F("selected") : F("")); html.replace(F("{c10}"), meterConfig->parity == 10 ? F("selected") : F("")); html.replace(F("{c11}"), meterConfig->parity == 11 ? F("selected") : F("")); html.replace(F("{i}"), meterConfig->invert ? F("checked") : F("")); html.replace(F("{d}"), String(meterConfig->distributionSystem)); for(int i = 0; i<3; i++) { html.replace("{d" + String(i) + "}", meterConfig->distributionSystem == i ? F("selected") : F("")); } html.replace(F("{f}"), String(meterConfig->mainFuse)); html.replace(F("{p}"), String(meterConfig->productionCapacity)); if(meterConfig->encryptionKey[0] != 0x00) { String encryptionKeyHex = "0x"; encryptionKeyHex += toHex(meterConfig->encryptionKey, 16); html.replace(F("{e}"), encryptionKeyHex); String authenticationKeyHex = "0x"; authenticationKeyHex += toHex(meterConfig->authenticationKey, 16); html.replace(F("{a}"), authenticationKeyHex); } else { html.replace(F("{e}"), F("")); html.replace(F("{a}"), F("")); } server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configMeterAdvancedHtml() { printD(F("Serving /meteradvanced.html over http...")); if(!checkSecurity(1)) return; snprintf_P(buf, BufferSize, METERADVANCED_HTML, meterConfig->wattageMultiplier / 1000.0, meterConfig->voltageMultiplier / 1000.0, meterConfig->amperageMultiplier / 1000.0, meterConfig->accumulatedMultiplier / 1000.0 ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf) + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(buf); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configWifiHtml() { printD(F("Serving /wifi.html over http...")); if(!checkSecurity(1)) return; String html = String((const __FlashStringHelper*) WIFI_HTML); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); WiFiConfig wifi; config->getWiFiConfig(wifi); html.replace(F("{s}"), wifi.ssid); html.replace(F("{p}"), wifi.psk); if(strlen(wifi.ip) > 0) { html.replace(F("{st}"), F("checked")); html.replace(F("{i}"), wifi.ip); html.replace(F("{g}"), wifi.gateway); html.replace(F("{sn}"), wifi.subnet); html.replace(F("{d1}"), wifi.dns1); html.replace(F("{d2}"), wifi.dns2); } else { html.replace(F("{st}"), F("")); html.replace(F("{i}"), WiFi.localIP().toString()); html.replace(F("{g}"), WiFi.gatewayIP().toString()); html.replace(F("{sn}"), WiFi.subnetMask().toString()); html.replace(F("{d1}"), WiFi.dnsIP().toString()); html.replace(F("{d2}"), F("")); } html.replace(F("{h}"), wifi.hostname); html.replace(F("{m}"), wifi.mdns ? F("checked") : F("")); html.replace(F("{w}"), String(wifi.power / 10.0, 1)); #if defined(ESP32) html.replace(F("{wm}"), "19.5"); #elif defined(ESP8266) html.replace(F("{wm}"), "20.5"); #endif server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configMqttHtml() { printD(F("Serving /mqtt.html over http...")); if(!checkSecurity(1)) return; String html = String((const __FlashStringHelper*) MQTT_HTML); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); MqttConfig mqtt; config->getMqttConfig(mqtt); html.replace(F("{m}"), strlen(mqtt.host) == 0 ? F("") : F("checked")); html.replace(F("{h}"), mqtt.host); if(mqtt.port > 0) { html.replace(F("{p}"), String(mqtt.port)); } else { html.replace(F("{p}"), String(1883)); } html.replace(F("{i}"), mqtt.clientId); html.replace(F("{t}"), mqtt.publishTopic); html.replace(F("{st}"), mqtt.subscribeTopic); html.replace(F("{u}"), mqtt.username); html.replace(F("{pw}"), mqtt.password); html.replace(F("{f}"), String(mqtt.payloadFormat)); for(int i = 0; i<5; i++) { html.replace("{f" + String(i) + "}", mqtt.payloadFormat == i ? F("selected") : F("")); } html.replace(F("{f255}"), mqtt.payloadFormat == 255 ? F("selected") : F("")); html.replace(F("{s}"), mqtt.ssl ? F("checked") : F("")); if(LittleFS.begin()) { html.replace(F("{dcu}"), LittleFS.exists(FILE_MQTT_CA) ? F("none") : F("")); html.replace(F("{dcf}"), LittleFS.exists(FILE_MQTT_CA) ? F("") : F("none")); html.replace(F("{deu}"), LittleFS.exists(FILE_MQTT_CERT) ? F("none") : F("")); html.replace(F("{def}"), LittleFS.exists(FILE_MQTT_CERT) ? F("") : F("none")); html.replace(F("{dku}"), LittleFS.exists(FILE_MQTT_KEY) ? F("none") : F("")); html.replace(F("{dkf}"), LittleFS.exists(FILE_MQTT_KEY) ? F("") : F("none")); LittleFS.end(); } else { html.replace(F("{dcu}"), F("")); html.replace(F("{dcf}"), F("none")); html.replace(F("{deu}"), F("")); html.replace(F("{def}"), F("none")); html.replace(F("{dku}"), F("")); html.replace(F("{dkf}"), F("none")); } server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configDomoticzHtml() { printD(F("Serving /domoticz.html over http...")); if(!checkSecurity(1)) return; server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); DomoticzConfig domo; config->getDomoticzConfig(domo); snprintf_P(buf, BufferSize, DOMOTICZ_HTML, domo.elidx, domo.cl1idx, domo.vl1idx, domo.vl2idx, domo.vl3idx ); server.setContentLength(strlen(buf) + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(buf); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configEntsoeHtml() { printD(F("Serving /entsoe.html over http...")); if(!checkSecurity(1)) return; EntsoeConfig entsoe; config->getEntsoeConfig(entsoe); if(ESP.getFreeHeap() > 25000) { String html = String((const __FlashStringHelper*) ENTSOE_HTML); html.replace(F("{et}"), entsoe.token); html.replace(F("{em}"), String(entsoe.multiplier / 1000.0, 3)); html.replace(F("{eaNo1}"), strcmp(entsoe.area, "10YNO-1--------2") == 0 ? F("selected") : F("")); html.replace(F("{eaNo2}"), strcmp(entsoe.area, "10YNO-2--------T") == 0 ? F("selected") : F("")); html.replace(F("{eaNo3}"), strcmp(entsoe.area, "10YNO-3--------J") == 0 ? F("selected") : F("")); html.replace(F("{eaNo4}"), strcmp(entsoe.area, "10YNO-4--------9") == 0 ? F("selected") : F("")); html.replace(F("{eaNo5}"), strcmp(entsoe.area, "10Y1001A1001A48H") == 0 ? F("selected") : F("")); html.replace(F("{eaSe1}"), strcmp(entsoe.area, "10Y1001A1001A44P") == 0 ? F("selected") : F("")); html.replace(F("{eaSe2}"), strcmp(entsoe.area, "10Y1001A1001A45N") == 0 ? F("selected") : F("")); html.replace(F("{eaSe3}"), strcmp(entsoe.area, "10Y1001A1001A46L") == 0 ? F("selected") : F("")); html.replace(F("{eaSe4}"), strcmp(entsoe.area, "10Y1001A1001A47J") == 0 ? F("selected") : F("")); html.replace(F("{eaDk1}"), strcmp(entsoe.area, "10YDK-1--------W") == 0 ? F("selected") : F("")); html.replace(F("{eaDk2}"), strcmp(entsoe.area, "10YDK-2--------M") == 0 ? F("selected") : F("")); html.replace(F("{at}"), strcmp(entsoe.area, "10YAT-APG------L") == 0 ? F("selected") : F("")); html.replace(F("{be}"), strcmp(entsoe.area, "10YBE----------2") == 0 ? F("selected") : F("")); html.replace(F("{cz}"), strcmp(entsoe.area, "10YCZ-CEPS-----N") == 0 ? F("selected") : F("")); html.replace(F("{ee}"), strcmp(entsoe.area, "10Y1001A1001A39I") == 0 ? F("selected") : F("")); html.replace(F("{fi}"), strcmp(entsoe.area, "10YFI-1--------U") == 0 ? F("selected") : F("")); html.replace(F("{fr}"), strcmp(entsoe.area, "10YFR-RTE------C") == 0 ? F("selected") : F("")); html.replace(F("{de}"), strcmp(entsoe.area, "10Y1001A1001A83F") == 0 ? F("selected") : F("")); html.replace(F("{gb}"), strcmp(entsoe.area, "10YGB----------A") == 0 ? F("selected") : F("")); html.replace(F("{lv}"), strcmp(entsoe.area, "10YLV-1001A00074") == 0 ? F("selected") : F("")); html.replace(F("{lt}"), strcmp(entsoe.area, "10YLT-1001A0008Q") == 0 ? F("selected") : F("")); html.replace(F("{nl}"), strcmp(entsoe.area, "10YNL----------L") == 0 ? F("selected") : F("")); html.replace(F("{pl}"), strcmp(entsoe.area, "10YPL-AREA-----S") == 0 ? F("selected") : F("")); html.replace(F("{ch}"), strcmp(entsoe.area, "10YCH-SWISSGRIDZ") == 0 ? F("selected") : F("")); html.replace(F("{ecNOK}"), strcmp(entsoe.currency, "NOK") == 0 ? F("selected") : F("")); html.replace(F("{ecSEK}"), strcmp(entsoe.currency, "SEK") == 0 ? F("selected") : F("")); html.replace(F("{ecDKK}"), strcmp(entsoe.currency, "DKK") == 0 ? F("selected") : F("")); html.replace(F("{ecEUR}"), strcmp(entsoe.currency, "EUR") == 0 ? F("selected") : F("")); server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } else { notFound(); } } void AmsWebServer::configThresholdsHtml() { printD(F("Serving /thresholds.html over http...")); if(!checkSecurity(1)) return; EnergyAccountingConfig* config = ea->getConfig(); String html = String((const __FlashStringHelper*) THRESHOLDS_HTML); for(int i = 0; i < 9; i++) { html.replace("{t" + String(i) + "}", String(config->thresholds[i], 10)); } html.replace(F("{h}"), String(config->hours, 10)); server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configWebHtml() { printD(F("Serving /web.html over http...")); if(!checkSecurity(1)) return; snprintf_P(buf, BufferSize, WEB_HTML, (char*) (webConfig.security == 0 ? F("selected") : F("")), (char*) (webConfig.security == 1 ? F("selected") : F("")), (char*) (webConfig.security == 2 ? F("selected") : F("")), webConfig.username, webConfig.password ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf) + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(buf); server.sendContent_P(FOOT_HTML); } void AmsWebServer::bootCss() { printD(F("Serving /boot.css over http...")); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR); server.send_P(200, "text/css", BOOT_CSS); } void AmsWebServer::githubSvg() { printD(F("Serving /github.svg over http...")); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR); server.send_P(200, PSTR("image/svg+xml"), GITHUB_SVG); } void AmsWebServer::dataJson() { printD(F("Serving /data.json over http...")); uint64_t now = millis64(); if(!checkSecurity(2)) return; float vcc = hw->getVcc(); int rssi = hw->getWifiRssi(); uint8_t espStatus; #if defined(ESP8266) if(vcc == 0) { espStatus = 1; } else if(vcc > 3.1 && vcc < 3.5) { espStatus = 1; } else if(vcc > 3.0 && vcc < 3.6) { espStatus = 2; } else { espStatus = 3; } #elif defined(ESP32) if(vcc == 0) { espStatus = 1; } else if(vcc > 2.8 && vcc < 3.5) { espStatus = 1; } else if(vcc > 2.7 && vcc < 3.6) { espStatus = 2; } else { espStatus = 3; } #endif uint8_t hanStatus; if(meterConfig->baud == 0) { hanStatus = 0; } else if(now - meterState->getLastUpdateMillis() < 15000) { hanStatus = 1; } else if(now - meterState->getLastUpdateMillis() < 30000) { hanStatus = 2; } else { hanStatus = 3; } uint8_t wifiStatus; if(rssi > -75) { wifiStatus = 1; } else if(rssi > -95) { wifiStatus = 2; } else { wifiStatus = 3; } uint8_t mqttStatus; if(!mqttEnabled) { mqttStatus = 0; } else if(mqtt != NULL && mqtt->connected()) { mqttStatus = 1; } else if(mqtt != NULL && mqtt->lastError() == 0) { mqttStatus = 2; } else { mqttStatus = 3; } float price = ENTSOE_NO_VALUE; if(eapi != NULL && strlen(eapi->getToken()) > 0) price = eapi->getValueForHour(0); String peaks = ""; uint8_t peakCount = ea->getConfig()->hours; if(peakCount > 5) peakCount = 5; for(uint8_t i = 1; i <= peakCount; i++) { if(!peaks.isEmpty()) peaks += ","; peaks += String(ea->getPeak(i)); } snprintf_P(buf, BufferSize, DATA_JSON, maxPwr == 0 ? meterState->isThreePhase() ? 20000 : 10000 : maxPwr, meterConfig->productionCapacity, meterConfig->mainFuse == 0 ? 32 : meterConfig->mainFuse, meterState->getActiveImportPower(), meterState->getActiveExportPower(), meterState->getReactiveImportPower(), meterState->getReactiveExportPower(), meterState->getActiveImportCounter(), meterState->getActiveExportCounter(), meterState->getReactiveImportCounter(), meterState->getReactiveExportCounter(), meterState->getL1Voltage(), meterState->getL2Voltage(), meterState->getL3Voltage(), meterState->getL1Current(), meterState->getL2Current(), meterState->getL3Current(), meterState->getPowerFactor(), meterState->getL1PowerFactor(), meterState->getL2PowerFactor(), meterState->getL3PowerFactor(), vcc, rssi, hw->getTemperature(), (uint32_t) (now / 1000), ESP.getFreeHeap(), espStatus, hanStatus, wifiStatus, mqttStatus, mqtt == NULL ? 0 : (int) mqtt->lastError(), price == ENTSOE_NO_VALUE ? PSTR("null") : String(price, 2).c_str(), meterState->getMeterType(), meterConfig->distributionSystem, ea->getMonthMax(), peaks.c_str(), ea->getCurrentThreshold(), ea->getUseThisHour(), ea->getCostThisHour(), ea->getProducedThisHour(), ea->getUseToday(), ea->getCostToday(), ea->getProducedToday(), ea->getUseThisMonth(), ea->getCostThisMonth(), ea->getProducedThisMonth(), (uint32_t) time(nullptr) ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); } void AmsWebServer::dayplotJson() { printD(F("Serving /dayplot.json over http...")); if(!checkSecurity(2)) return; if(ds == NULL) { notFound(); } else { snprintf_P(buf, BufferSize, DAYPLOT_JSON, ds->getHourImport(0) / 1000.0, ds->getHourImport(1) / 1000.0, ds->getHourImport(2) / 1000.0, ds->getHourImport(3) / 1000.0, ds->getHourImport(4) / 1000.0, ds->getHourImport(5) / 1000.0, ds->getHourImport(6) / 1000.0, ds->getHourImport(7) / 1000.0, ds->getHourImport(8) / 1000.0, ds->getHourImport(9) / 1000.0, ds->getHourImport(10) / 1000.0, ds->getHourImport(11) / 1000.0, ds->getHourImport(12) / 1000.0, ds->getHourImport(13) / 1000.0, ds->getHourImport(14) / 1000.0, ds->getHourImport(15) / 1000.0, ds->getHourImport(16) / 1000.0, ds->getHourImport(17) / 1000.0, ds->getHourImport(18) / 1000.0, ds->getHourImport(19) / 1000.0, ds->getHourImport(20) / 1000.0, ds->getHourImport(21) / 1000.0, ds->getHourImport(22) / 1000.0, ds->getHourImport(23) / 1000.0, ds->getHourExport(0) / 1000.0, ds->getHourExport(1) / 1000.0, ds->getHourExport(2) / 1000.0, ds->getHourExport(3) / 1000.0, ds->getHourExport(4) / 1000.0, ds->getHourExport(5) / 1000.0, ds->getHourExport(6) / 1000.0, ds->getHourExport(7) / 1000.0, ds->getHourExport(8) / 1000.0, ds->getHourExport(9) / 1000.0, ds->getHourExport(10) / 1000.0, ds->getHourExport(11) / 1000.0, ds->getHourExport(12) / 1000.0, ds->getHourExport(13) / 1000.0, ds->getHourExport(14) / 1000.0, ds->getHourExport(15) / 1000.0, ds->getHourExport(16) / 1000.0, ds->getHourExport(17) / 1000.0, ds->getHourExport(18) / 1000.0, ds->getHourExport(19) / 1000.0, ds->getHourExport(20) / 1000.0, ds->getHourExport(21) / 1000.0, ds->getHourExport(22) / 1000.0, ds->getHourExport(23) / 1000.0 ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); } } void AmsWebServer::monthplotJson() { printD(F("Serving /monthplot.json over http...")); if(!checkSecurity(2)) return; if(ds == NULL) { notFound(); } else { snprintf_P(buf, BufferSize, MONTHPLOT_JSON, ds->getDayImport(1) / 1000.0, ds->getDayImport(2) / 1000.0, ds->getDayImport(3) / 1000.0, ds->getDayImport(4) / 1000.0, ds->getDayImport(5) / 1000.0, ds->getDayImport(6) / 1000.0, ds->getDayImport(7) / 1000.0, ds->getDayImport(8) / 1000.0, ds->getDayImport(9) / 1000.0, ds->getDayImport(10) / 1000.0, ds->getDayImport(11) / 1000.0, ds->getDayImport(12) / 1000.0, ds->getDayImport(13) / 1000.0, ds->getDayImport(14) / 1000.0, ds->getDayImport(15) / 1000.0, ds->getDayImport(16) / 1000.0, ds->getDayImport(17) / 1000.0, ds->getDayImport(18) / 1000.0, ds->getDayImport(19) / 1000.0, ds->getDayImport(20) / 1000.0, ds->getDayImport(21) / 1000.0, ds->getDayImport(22) / 1000.0, ds->getDayImport(23) / 1000.0, ds->getDayImport(24) / 1000.0, ds->getDayImport(25) / 1000.0, ds->getDayImport(26) / 1000.0, ds->getDayImport(27) / 1000.0, ds->getDayImport(28) / 1000.0, ds->getDayImport(29) / 1000.0, ds->getDayImport(30) / 1000.0, ds->getDayImport(31) / 1000.0, ds->getDayExport(1) / 1000.0, ds->getDayExport(2) / 1000.0, ds->getDayExport(3) / 1000.0, ds->getDayExport(4) / 1000.0, ds->getDayExport(5) / 1000.0, ds->getDayExport(6) / 1000.0, ds->getDayExport(7) / 1000.0, ds->getDayExport(8) / 1000.0, ds->getDayExport(9) / 1000.0, ds->getDayExport(10) / 1000.0, ds->getDayExport(11) / 1000.0, ds->getDayExport(12) / 1000.0, ds->getDayExport(13) / 1000.0, ds->getDayExport(14) / 1000.0, ds->getDayExport(15) / 1000.0, ds->getDayExport(16) / 1000.0, ds->getDayExport(17) / 1000.0, ds->getDayExport(18) / 1000.0, ds->getDayExport(19) / 1000.0, ds->getDayExport(20) / 1000.0, ds->getDayExport(21) / 1000.0, ds->getDayExport(22) / 1000.0, ds->getDayExport(23) / 1000.0, ds->getDayExport(24) / 1000.0, ds->getDayExport(25) / 1000.0, ds->getDayExport(26) / 1000.0, ds->getDayExport(27) / 1000.0, ds->getDayExport(28) / 1000.0, ds->getDayExport(29) / 1000.0, ds->getDayExport(30) / 1000.0, ds->getDayExport(31) / 1000.0 ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); } } void AmsWebServer::energyPriceJson() { printD(F("Serving /energyprice.json over http...")); if(!checkSecurity(2)) return; float prices[36]; for(int i = 0; i < 36; i++) { prices[i] = eapi == NULL ? ENTSOE_NO_VALUE : eapi->getValueForHour(i); } snprintf_P(buf, BufferSize, ENERGYPRICE_JSON, eapi == NULL ? "" : eapi->getCurrency(), prices[0] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[0], 4).c_str(), prices[1] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[1], 4).c_str(), prices[2] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[2], 4).c_str(), prices[3] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[3], 4).c_str(), prices[4] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[4], 4).c_str(), prices[5] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[5], 4).c_str(), prices[6] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[6], 4).c_str(), prices[7] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[7], 4).c_str(), prices[8] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[8], 4).c_str(), prices[9] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[9], 4).c_str(), prices[10] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[10], 4).c_str(), prices[11] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[11], 4).c_str(), prices[12] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[12], 4).c_str(), prices[13] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[13], 4).c_str(), prices[14] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[14], 4).c_str(), prices[15] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[15], 4).c_str(), prices[16] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[16], 4).c_str(), prices[17] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[17], 4).c_str(), prices[18] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[18], 4).c_str(), prices[19] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[19], 4).c_str(), prices[20] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[20], 4).c_str(), prices[21] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[21], 4).c_str(), prices[22] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[22], 4).c_str(), prices[23] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[23], 4).c_str(), prices[24] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[24], 4).c_str(), prices[25] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[25], 4).c_str(), prices[26] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[26], 4).c_str(), prices[27] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[27], 4).c_str(), prices[28] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[28], 4).c_str(), prices[29] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[29], 4).c_str(), prices[30] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[30], 4).c_str(), prices[31] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[31], 4).c_str(), prices[32] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[32], 4).c_str(), prices[33] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[33], 4).c_str(), prices[34] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[34], 4).c_str(), prices[35] == ENTSOE_NO_VALUE ? PSTR("null") : String(prices[35], 4).c_str() ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf)); server.send(200, MIME_JSON, buf); } void AmsWebServer::handleSetup() { printD(F("Handling setup method from http")); if(!server.hasArg(F("wifiSsid")) || server.arg(F("wifiSsid")).isEmpty() || !server.hasArg(F("wifiPassword")) || server.arg(F("wifiPassword")).isEmpty()) { server.sendHeader(HEADER_LOCATION, F("/"), true); server.send (302, MIME_PLAIN, F("")); } else { SystemConfig sys { static_cast(server.arg(F("board")).toInt()) }; DebugConfig debugConfig; config->getDebugConfig(debugConfig); config->clear(); switch(sys.boardType) { case 0: // roarfred gpioConfig->hanPin = 3; gpioConfig->apPin = 0; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; gpioConfig->tempSensorPin = 5; break; case 1: // Arnio Kamstrup gpioConfig->hanPin = 3; gpioConfig->apPin = 0; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; gpioConfig->ledPinRed = 13; gpioConfig->ledPinGreen = 14; gpioConfig->ledRgbInverted = true; break; case 2: // spenceme gpioConfig->hanPin = 3; gpioConfig->apPin = 0; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; gpioConfig->tempSensorPin = 5; gpioConfig->vccBootLimit = 33; break; case 3: // Pow UART0 gpioConfig->hanPin = 3; gpioConfig->apPin = 0; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; gpioConfig->ledPinRed = 13; gpioConfig->ledPinGreen = 14; gpioConfig->ledRgbInverted = true; break; case 4: // Pow GPIO12 gpioConfig->hanPin = 12; gpioConfig->apPin = 0; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; gpioConfig->ledPinRed = 13; gpioConfig->ledPinGreen = 14; gpioConfig->ledRgbInverted = true; break; case 5: // Pow-K+ UART2 gpioConfig->hanPin = 16; gpioConfig->apPin = 0; gpioConfig->ledPinRed = 13; gpioConfig->ledPinGreen = 14; gpioConfig->ledRgbInverted = true; gpioConfig->vccPin = 10; gpioConfig->vccResistorGnd = 22; gpioConfig->vccResistorVcc = 33; break; case 6: // Pow-P1 gpioConfig->hanPin = 16; gpioConfig->apPin = 0; gpioConfig->ledPinRed = 13; gpioConfig->ledPinGreen = 14; gpioConfig->ledRgbInverted = true; gpioConfig->vccPin = 10; gpioConfig->vccResistorGnd = 22; gpioConfig->vccResistorVcc = 33; break; case 7: // Pow-U+ gpioConfig->hanPin = 16; gpioConfig->apPin = 0; gpioConfig->ledPinRed = 13; gpioConfig->ledPinGreen = 14; gpioConfig->ledRgbInverted = true; gpioConfig->vccPin = 10; gpioConfig->vccResistorGnd = 22; gpioConfig->vccResistorVcc = 33; break; case 101: // D1 gpioConfig->hanPin = 5; gpioConfig->apPin = 4; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; gpioConfig->vccMultiplier = 1100; break; case 100: // ESP8266 gpioConfig->hanPin = 3; gpioConfig->ledPin = 2; gpioConfig->ledInverted = true; break; case 201: // D32 gpioConfig->hanPin = 16; gpioConfig->apPin = 4; gpioConfig->ledPin = 5; gpioConfig->ledInverted = true; break; case 202: // Feather gpioConfig->hanPin = 16; gpioConfig->ledPin = 2; gpioConfig->ledInverted = false; break; case 203: // DevKitC gpioConfig->hanPin = 16; gpioConfig->ledPin = 2; gpioConfig->ledInverted = false; break; case 200: // ESP32 gpioConfig->hanPin = 16; gpioConfig->ledPin = 2; gpioConfig->ledInverted = false; break; case 50: // S2 gpioConfig->hanPin = 18; break; case 51: // S2-mini gpioConfig->hanPin = 18; gpioConfig->ledPin = 15; gpioConfig->ledInverted = false; gpioConfig->apPin = 0; break; } WiFiConfig wifi; config->clearWifi(wifi); strcpy(wifi.ssid, server.arg(F("wifiSsid")).c_str()); strcpy(wifi.psk, server.arg(F("wifiPassword")).c_str()); if(server.hasArg(F("wifiIpType")) && server.arg(F("wifiIpType")).toInt() == 1) { strcpy(wifi.ip, server.arg(F("wifiIp")).c_str()); strcpy(wifi.gateway, server.arg(F("wifiGw")).c_str()); strcpy(wifi.subnet, server.arg(F("wifiSubnet")).c_str()); strcpy(wifi.dns1, server.arg(F("wifiDns1")).c_str()); } if(server.hasArg(F("wifiHostname")) && !server.arg(F("wifiHostname")).isEmpty()) { strcpy(wifi.hostname, server.arg(F("wifiHostname")).c_str()); wifi.mdns = true; } else { wifi.mdns = false; } MqttConfig mqttConfig; config->clearMqtt(mqttConfig); config->clearAuth(webConfig); NtpConfig ntp; config->clearNtp(ntp); bool success = true; if(!config->setSystemConfig(sys)) { printD(F("Unable to set system config")); success = false; } if(!config->setWiFiConfig(wifi)) { printD(F("Unable to set WiFi config")); success = false; } if(!config->setMqttConfig(mqttConfig)) { printD(F("Unable to set MQTT config")); success = false; } if(!config->setWebConfig(webConfig)) { printD(F("Unable to set web config")); success = false; } if(!config->setGpioConfig(*gpioConfig)) { printD(F("Unable to set GPIO config")); success = false; } if(!config->setNtpConfig(ntp)) { printD(F("Unable to set NTP config")); success = false; } config->setDebugConfig(debugConfig); if(success && config->save()) { performRestart = true; server.sendHeader(HEADER_LOCATION,"/restart-wait"); server.send(303); } else { printE(F("Error saving configuration")); server.send_P(500, MIME_HTML, PSTR("

Error saving configuration!

")); } } } void AmsWebServer::handleSave() { printD(F("Handling save method from http")); if(!checkSecurity(1)) return; if(server.hasArg(F("mc")) && server.arg(F("mc")) == F("true")) { printD(F("Received meter config")); config->getMeterConfig(*meterConfig); meterConfig->baud = server.arg(F("b")).toInt(); meterConfig->parity = server.arg(F("c")).toInt(); meterConfig->invert = server.hasArg(F("i")) && server.arg(F("i")) == F("true"); meterConfig->distributionSystem = server.arg(F("d")).toInt(); meterConfig->mainFuse = server.arg(F("f")).toInt(); meterConfig->productionCapacity = server.arg(F("p")).toInt(); maxPwr = 0; String encryptionKeyHex = server.arg(F("e")); if(!encryptionKeyHex.isEmpty()) { encryptionKeyHex.replace(F("0x"), F("")); fromHex(meterConfig->encryptionKey, encryptionKeyHex, 16); } String authenticationKeyHex = server.arg(F("a")); if(!authenticationKeyHex.isEmpty()) { authenticationKeyHex.replace(F("0x"), F("")); fromHex(meterConfig->authenticationKey, authenticationKeyHex, 16); } config->setMeterConfig(*meterConfig); } if(server.hasArg(F("ma")) && server.arg(F("ma")) == F("true")) { printD(F("Received meter advanced config")); config->getMeterConfig(*meterConfig); meterConfig->wattageMultiplier = server.arg(F("wm")).toDouble() * 1000; meterConfig->voltageMultiplier = server.arg(F("vm")).toDouble() * 1000; meterConfig->amperageMultiplier = server.arg(F("am")).toDouble() * 1000; meterConfig->accumulatedMultiplier = server.arg(F("cm")).toDouble() * 1000; config->setMeterConfig(*meterConfig); } if(server.hasArg(F("wc")) && server.arg(F("wc")) == F("true")) { printD(F("Received WiFi config")); WiFiConfig wifi; config->clearWifi(wifi); strcpy(wifi.ssid, server.arg(F("s")).c_str()); strcpy(wifi.psk, server.arg(F("p")).c_str()); if(server.hasArg(F("st")) && server.arg(F("st")).toInt() == 1) { strcpy(wifi.ip, server.arg(F("i")).c_str()); strcpy(wifi.gateway, server.arg(F("g")).c_str()); strcpy(wifi.subnet, server.arg(F("sn")).c_str()); strcpy(wifi.dns1, server.arg(F("d1")).c_str()); strcpy(wifi.dns2, server.arg(F("d2")).c_str()); } if(server.hasArg(F("h")) && !server.arg(F("h")).isEmpty()) { strcpy(wifi.hostname, server.arg(F("h")).c_str()); } wifi.power = server.arg(F("w")).toFloat() * 10; config->setWiFiConfig(wifi); } if(server.hasArg(F("mqc")) && server.arg(F("mqc")) == F("true")) { printD(F("Received MQTT config")); MqttConfig mqtt; if(server.hasArg(F("m")) && server.arg(F("m")) == F("true")) { strcpy(mqtt.host, server.arg(F("h")).c_str()); strcpy(mqtt.clientId, server.arg(F("i")).c_str()); strcpy(mqtt.publishTopic, server.arg(F("t")).c_str()); strcpy(mqtt.subscribeTopic, server.arg(F("st")).c_str()); strcpy(mqtt.username, server.arg(F("u")).c_str()); strcpy(mqtt.password, server.arg(F("pw")).c_str()); mqtt.payloadFormat = server.arg(F("f")).toInt(); mqtt.ssl = server.arg(F("s")) == F("true"); mqtt.port = server.arg(F("p")).toInt(); if(mqtt.port == 0) { mqtt.port = mqtt.ssl ? 8883 : 1883; } } else { config->clearMqtt(mqtt); } config->setMqttConfig(mqtt); } if(server.hasArg(F("dc")) && server.arg(F("dc")) == F("true")) { printD(F("Received Domoticz config")); DomoticzConfig domo { static_cast(server.arg(F("elidx")).toInt()), static_cast(server.arg(F("vl1idx")).toInt()), static_cast(server.arg(F("vl2idx")).toInt()), static_cast(server.arg(F("vl3idx")).toInt()), static_cast(server.arg(F("cl1idx")).toInt()) }; config->setDomoticzConfig(domo); } if(server.hasArg(F("ac")) && server.arg(F("ac")) == F("true")) { printD(F("Received web config")); webConfig.security = server.arg(F("as")).toInt(); if(webConfig.security > 0) { strcpy(webConfig.username, server.arg(F("au")).c_str()); strcpy(webConfig.password, server.arg(F("ap")).c_str()); debugger->setPassword(webConfig.password); } else { strcpy_P(webConfig.username, PSTR("")); strcpy_P(webConfig.password, PSTR("")); debugger->setPassword(F("")); } config->setWebConfig(webConfig); } if(server.hasArg(F("gc")) && server.arg(F("gc")) == F("true")) { printD(F("Received GPIO config")); gpioConfig->hanPin = server.hasArg(F("h")) && !server.arg(F("h")).isEmpty() ? server.arg(F("h")).toInt() : 3; gpioConfig->ledPin = server.hasArg(F("l")) && !server.arg(F("l")).isEmpty() ? server.arg(F("l")).toInt() : 0xFF; gpioConfig->ledInverted = server.hasArg(F("i")) && server.arg(F("i")) == F("true"); gpioConfig->ledPinRed = server.hasArg(F("r")) && !server.arg(F("r")).isEmpty() ? server.arg(F("r")).toInt() : 0xFF; gpioConfig->ledPinGreen = server.hasArg(F("e")) && !server.arg(F("e")).isEmpty() ? server.arg(F("e")).toInt() : 0xFF; gpioConfig->ledPinBlue = server.hasArg(F("b")) && !server.arg(F("b")).isEmpty() ? server.arg(F("b")).toInt() : 0xFF; gpioConfig->ledRgbInverted = server.hasArg(F("n")) && server.arg(F("n")) == F("true"); gpioConfig->apPin = server.hasArg(F("a")) && !server.arg(F("a")).isEmpty() ? server.arg(F("a")).toInt() : 0xFF; gpioConfig->tempSensorPin = server.hasArg(F("t")) && !server.arg(F("t")).isEmpty() ?server.arg(F("t")).toInt() : 0xFF; gpioConfig->tempAnalogSensorPin = server.hasArg(F("m")) && !server.arg(F("m")).isEmpty() ?server.arg(F("m")).toInt() : 0xFF; gpioConfig->vccPin = server.hasArg(F("v")) && !server.arg(F("v")).isEmpty() ? server.arg(F("v")).toInt() : 0xFF; gpioConfig->vccOffset = server.hasArg(F("o")) && !server.arg(F("o")).isEmpty() ? server.arg(F("o")).toFloat() * 100 : 0; gpioConfig->vccMultiplier = server.hasArg(F("u")) && !server.arg(F("u")).isEmpty() ? server.arg(F("u")).toFloat() * 1000 : 1000; gpioConfig->vccBootLimit = server.hasArg(F("c")) && !server.arg(F("c")).isEmpty() ? server.arg(F("c")).toFloat() * 10 : 0; gpioConfig->vccResistorGnd = server.hasArg(F("d")) && !server.arg(F("d")).isEmpty() ? server.arg(F("d")).toInt() : 0; gpioConfig->vccResistorVcc = server.hasArg(F("s")) && !server.arg(F("s")).isEmpty() ? server.arg(F("s")).toInt() : 0; config->setGpioConfig(*gpioConfig); } if(server.hasArg(F("debugConfig")) && server.arg(F("debugConfig")) == F("true")) { printD(F("Received Debug config")); DebugConfig debug; config->getDebugConfig(debug); bool active = debug.serial || debug.telnet; debug.telnet = server.hasArg(F("debugTelnet")) && server.arg(F("debugTelnet")) == F("true"); debug.serial = server.hasArg(F("debugSerial")) && server.arg(F("debugSerial")) == F("true"); debug.level = server.arg(F("debugLevel")).toInt(); if(debug.telnet || debug.serial) { if(webConfig.security > 0) { debugger->setPassword(webConfig.password); } else { debugger->setPassword(F("")); } debugger->setSerialEnabled(debug.serial); WiFiConfig wifi; if(config->getWiFiConfig(wifi) && strlen(wifi.hostname) > 0) { debugger->begin(wifi.hostname, (uint8_t) debug.level); if(!debug.telnet) { debugger->stop(); } } } else if(active) { performRestart = true; } config->setDebugConfig(debug); } if(server.hasArg(F("nc")) && server.arg(F("nc")) == F("true")) { printD(F("Received NTP config")); NtpConfig ntp { server.hasArg(F("n")) && server.arg(F("n")) == F("true"), server.hasArg(F("nd")) && server.arg(F("nd")) == F("true"), static_cast(server.arg(F("o")).toInt() / 10), static_cast(server.arg(F("so")).toInt() / 10) }; strcpy(ntp.server, server.arg(F("ns")).c_str()); config->setNtpConfig(ntp); } if(server.hasArg(F("ec")) && server.arg(F("ec")) == F("true")) { printD(F("Received ENTSO-E config")); EntsoeConfig entsoe; strcpy(entsoe.token, server.arg(F("et")).c_str()); strcpy(entsoe.area, server.arg(F("ea")).c_str()); strcpy(entsoe.currency, server.arg(F("ecu")).c_str()); entsoe.multiplier = server.arg(F("em")).toFloat() * 1000; config->setEntsoeConfig(entsoe); } if(server.hasArg(F("cc")) && server.arg(F("cc")) == F("true")) { printD(F("Received energy accounting config")); EnergyAccountingConfig eac; eac.thresholds[0] = server.arg(F("t0")).toInt(); eac.thresholds[1] = server.arg(F("t1")).toInt(); eac.thresholds[2] = server.arg(F("t2")).toInt(); eac.thresholds[3] = server.arg(F("t3")).toInt(); eac.thresholds[4] = server.arg(F("t4")).toInt(); eac.thresholds[5] = server.arg(F("t5")).toInt(); eac.thresholds[6] = server.arg(F("t6")).toInt(); eac.thresholds[7] = server.arg(F("t7")).toInt(); eac.thresholds[8] = server.arg(F("t8")).toInt(); eac.hours = server.arg(F("h")).toInt(); config->setEnergyAccountingConfig(eac); } printI(F("Saving configuration now...")); //if (debugger->isActive(RemoteDebug::DEBUG)) config->print(debugger); if (config->save()) { printI(F("Successfully saved.")); if(config->isWifiChanged() || performRestart) { performRestart = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); } else { server.sendHeader(HEADER_LOCATION, F("/"), true); server.send_P(302, MIME_PLAIN, PSTR("")); hw->setup(gpioConfig, config); } } else { printE(F("Error saving configuration")); server.send_P(500, MIME_HTML, PSTR("

Error saving configuration!

")); } } void AmsWebServer::configNtpHtml() { printD(F("Serving /ntp.html over http...")); if(!checkSecurity(1)) return; NtpConfig ntp; config->getNtpConfig(ntp); snprintf_P(buf, BufferSize, NTP_HTML, (char*) (ntp.enable ? F("checked") : F("")), (char*) (ntp.offset == 0 ? F("selected") : F("")), (char*) (ntp.offset == 360 ? F("selected") : F("")), (char*) (ntp.offset == 720 ? F("selected") : F("")), (char*) (ntp.summerOffset == 0 ? F("selected") : F("")), (char*) (ntp.summerOffset == 360 ? F("selected") : F("")), (char*) (ntp.server), (char*) (ntp.dhcp ? F("checked") : F("")) ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(strlen(buf) + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(buf); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configGpioHtml() { printD(F("Serving /gpio.html over http...")); if(!checkSecurity(1)) return; String html = String((const __FlashStringHelper*) GPIO_HTML); #if defined(CONFIG_IDF_TARGET_ESP32S2) html.replace(F("${g}"), F("44")); #elif defined(ESP32) html.replace(F("${g}"), F("39")); #else html.replace(F("${g}"), F("16")); #endif html.replace(F("${h}"), getSerialSelectOptions(gpioConfig->hanPin)); html.replace(F("${l}"), gpioConfig->ledPin == 0xFF ? "" : String(gpioConfig->ledPin)); html.replace(F("${i}"), gpioConfig->ledInverted ? F("checked") : F("")); html.replace(F("${r}"), gpioConfig->ledPinRed == 0xFF ? "" : String(gpioConfig->ledPinRed)); html.replace(F("${e}"), gpioConfig->ledPinGreen == 0xFF ? "" : String(gpioConfig->ledPinGreen)); html.replace(F("${b}"), gpioConfig->ledPinBlue == 0xFF ? "" : String(gpioConfig->ledPinBlue)); html.replace(F("${n}"), gpioConfig->ledRgbInverted ? F("checked") : F("")); html.replace(F("${a}"), gpioConfig->apPin == 0xFF ? "" : String(gpioConfig->apPin)); html.replace(F("${t}"), gpioConfig->tempSensorPin == 0xFF ? "" : String(gpioConfig->tempSensorPin)); html.replace(F("${m}"), gpioConfig->tempAnalogSensorPin == 0xFF ? "" : String(gpioConfig->tempAnalogSensorPin)); html.replace(F("${v}"), gpioConfig->vccPin == 0xFF ? "" : String(gpioConfig->vccPin)); html.replace(F("${o}"), gpioConfig->vccOffset > 0 ? String(gpioConfig->vccOffset / 100.0, 2) : F("")); html.replace(F("${u}"), gpioConfig->vccMultiplier > 0 ? String(gpioConfig->vccMultiplier / 1000.0, 2) : F("")); html.replace(F("${c}"), gpioConfig->vccBootLimit > 0 ? String(gpioConfig->vccBootLimit / 10.0, 1) : F("")); html.replace(F("${d}"), gpioConfig->vccResistorGnd > 0 ? String(gpioConfig->vccResistorGnd) : F("")); html.replace(F("${s}"), gpioConfig->vccResistorVcc > 0 ? String(gpioConfig->vccResistorVcc) : F("")); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configDebugHtml() { printD(F("Serving /debugging.html over http...")); if(!checkSecurity(1)) return; DebugConfig debug; config->getDebugConfig(debug); snprintf_P(buf, BufferSize, DEBUGGING_HTML, (char*) (debug.telnet ? F("checked") : F("")), (char*) (debug.serial ? F("checked") : F("")), (char*) (debug.level == 1 ? F("selected") : F("")), (char*) (debug.level == 2 ? F("selected") : F("")), (char*) (debug.level == 3 ? F("selected") : F("")), (char*) (debug.level == 4 ? F("selected") : F("")) ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.setContentLength(strlen(buf) + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(buf); server.sendContent_P(FOOT_HTML); } String AmsWebServer::getSerialSelectOptions(int selected) { String gpioOptions; if(selected == 3) { gpioOptions += F(""); } else { gpioOptions += F(""); } #if defined(CONFIG_IDF_TARGET_ESP32S2) int numGpio = 30; int gpios[] = {4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,21,22,23,25,32,33,34,35,36,39,40,41,42,43,44}; if(selected == 18) { gpioOptions += F(""); } else { gpioOptions += F(""); } #elif defined(ESP32) int numGpio = 24; int gpios[] = {4,5,6,7,8,10,11,12,13,14,15,17,18,19,21,22,23,25,32,33,34,35,36,39}; if(selected == 9) { gpioOptions += F(""); } else { gpioOptions += F(""); } if(selected == 16) { gpioOptions += F(""); } else { gpioOptions += F(""); } #elif defined(ESP8266) int numGpio = 9; int gpios[] = {4,5,9,10,12,13,14,15,16}; if(selected == 113) { gpioOptions += F(""); } else { gpioOptions += F(""); } #endif for(int i = 0; i < numGpio; i++) { int gpio = gpios[i]; char buffer [16]; sprintf(buffer, "%02u", gpio); if(gpio == selected) { gpioOptions += ""; } else { gpioOptions += ""; } } return gpioOptions; } void AmsWebServer::uploadPost() { server.send(200); } HTTPUpload& AmsWebServer::uploadFile(const char* path) { HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START){ if(uploading) { printE(F("Upload already in progress")); server.send_P(500, MIME_HTML, PSTR("

Upload already in progress!

")); } else if (!LittleFS.begin()) { printE(F("An Error has occurred while mounting LittleFS")); server.send_P(500, MIME_HTML, PSTR("

Unable to mount LittleFS!

")); } else { uploading = true; if(debugger->isActive(RemoteDebug::DEBUG)) { debugger->printf_P(PSTR("handleFileUpload file: %s\n"), path); } if(LittleFS.exists(path)) { LittleFS.remove(path); } file = LittleFS.open(path, "w"); if(debugger->isActive(RemoteDebug::DEBUG)) { debugger->printf_P(PSTR("handleFileUpload Open file and write: %u\n"), upload.currentSize); } size_t written = file.write(upload.buf, upload.currentSize); if(debugger->isActive(RemoteDebug::DEBUG)) { debugger->printf_P(PSTR("handleFileUpload Written: %u\n"), written); } } } else if(upload.status == UPLOAD_FILE_WRITE) { if(debugger->isActive(RemoteDebug::DEBUG)) { debugger->printf_P(PSTR("handleFileUpload Writing: %u\n"), upload.currentSize); } if(file) { size_t written = file.write(upload.buf, upload.currentSize); if(debugger->isActive(RemoteDebug::DEBUG)) { debugger->printf_P(PSTR("handleFileUpload Written: %u\n"), written); } delay(1); if(written != upload.currentSize) { file.flush(); file.close(); LittleFS.remove(path); LittleFS.end(); printE(F("An Error has occurred while writing file")); server.send_P(500, MIME_HTML, PSTR("

Unable to write file!

")); } } } else if(upload.status == UPLOAD_FILE_END) { if(file) { file.flush(); file.close(); // LittleFS.end(); } else { server.send_P(500, MIME_PLAIN, PSTR("500: couldn't create file")); } } return upload; } void AmsWebServer::deleteFile(const char* path) { if(LittleFS.begin()) { LittleFS.remove(path); LittleFS.end(); } } void AmsWebServer::firmwareHtml() { printD(F("Serving /firmware.html over http...")); if(!checkSecurity(1)) return; server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); String html = String((const __FlashStringHelper*) FIRMWARE_HTML); #if defined(ESP8266) html.replace(F("{chipset}"), F("ESP8266")); #elif defined(CONFIG_IDF_TARGET_ESP32S2) html.replace(F("{chipset}"), F("ESP32S2")); #elif defined(ESP32) #if defined(CONFIG_FREERTOS_UNICORE) html.replace(F("{chipset}"), F("ESP32SOLO")); #else html.replace(F("{chipset}"), F("ESP32")); #endif #endif server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent(html); server.sendContent_P(FOOT_HTML); } void AmsWebServer::firmwarePost() { printD(F("Handlling firmware post...")); if(!checkSecurity(1)) return; if(rebootForUpgrade) { server.send(200); } else { if(server.hasArg(F("url"))) { String url = server.arg(F("url")); if(!url.isEmpty() && (url.startsWith(F("http://")) || url.startsWith(F("https://")))) { printD(F("Custom firmware URL was provided")); customFirmwareUrl = url; performUpgrade = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); return; } } server.sendHeader(HEADER_LOCATION,F("/firmware")); server.send(303); } } void AmsWebServer::firmwareUpload() { printD(F("Handlling firmware upload...")); if(!checkSecurity(1)) return; HTTPUpload& upload = server.upload(); String filename = upload.filename; if(filename.isEmpty()) return; if(upload.status == UPLOAD_FILE_START) { if(!filename.endsWith(".bin")) { server.send_P(500, MIME_PLAIN, PSTR("500: couldn't create file")); } else { #if defined(ESP32) esp_task_wdt_delete(NULL); esp_task_wdt_deinit(); #elif defined(ESP8266) ESP.wdtDisable(); #endif } } uploadFile(FILE_FIRMWARE); if(upload.status == UPLOAD_FILE_END) { rebootForUpgrade = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); } } void AmsWebServer::firmwareDownload() { if(!checkSecurity(1)) return; printD(F("Firmware download URL triggered")); performUpgrade = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); } void AmsWebServer::restartHtml() { printD(F("Serving /restart.html over http...")); if(!checkSecurity(1)) return; server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(RESTART_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(RESTART_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::restartPost() { if(!checkSecurity(1)) return; printD(F("Setting restart flag and redirecting")); performRestart = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); } void AmsWebServer::restartWaitHtml() { printD(F("Serving /restart-wait.html over http...")); if(!checkSecurity(1)) return; String html = String((const __FlashStringHelper*) RESTARTWAIT_HTML); WiFiConfig wifi; config->getWiFiConfig(wifi); if(WiFi.getMode() != WIFI_AP) { html.replace(F("boot.css"), BOOTSTRAP_URL); } if(strlen(wifi.ip) == 0 && WiFi.getMode() != WIFI_AP) { html.replace(F("${ip}"), WiFi.localIP().toString()); } else { html.replace(F("${ip}"), wifi.ip); } html.replace(F("${hostname}"), wifi.hostname); if(performUpgrade || rebootForUpgrade) { html.replace(F("{rs}"), "d-none"); html.replace(F("{us}"), F("")); } else { html.replace(F("{rs}"), F("")); html.replace(F("{us}"), "d-none"); } server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(html.length()); server.send(200, MIME_HTML, html); yield(); if(performRestart || rebootForUpgrade) { if(ds != NULL) { ds->save(); } printI(F("Rebooting")); delay(1000); #if defined(ESP8266) ESP.reset(); #elif defined(ESP32) ESP.restart(); #endif performRestart = false; } else if(performUpgrade) { WiFiClient client; String url = customFirmwareUrl.isEmpty() || !customFirmwareUrl.startsWith(F("http")) ? F("http://ams2mqtt.rewiredinvent.no/hub/firmware/update") : customFirmwareUrl; #if defined(ESP8266) String chipType = F("esp8266"); #elif defined(CONFIG_IDF_TARGET_ESP32S2) String chipType = F("esp32s2"); #elif defined(ESP32) #if defined(CONFIG_FREERTOS_UNICORE) String chipType = F("esp32solo"); #else String chipType = F("esp32"); #endif #endif #if defined(ESP8266) ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION); #elif defined(ESP32) HTTPUpdate httpUpdate; httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType); #endif switch(ret) { case HTTP_UPDATE_FAILED: printE(F("Update failed")); break; case HTTP_UPDATE_NO_UPDATES: printI(F("No Update")); break; case HTTP_UPDATE_OK: printI(F("Update OK")); break; } performUpgrade = false; } } void AmsWebServer::isAliveCheck() { server.sendHeader(F("Access-Control-Allow-Origin"), F("*")); server.send(200); } void AmsWebServer::uploadHtml(const char* label, const char* action, const char* menu) { server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(UPLOAD_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(UPLOAD_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::deleteHtml(const char* label, const char* action, const char* menu) { server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(DELETE_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(DELETE_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::mqttCa() { printD(F("Serving /mqtt-ca.html over http...")); if(!checkSecurity(1)) return; if(LittleFS.begin()) { if(LittleFS.exists(FILE_MQTT_CA)) { deleteHtml("CA file", "/mqtt-ca/delete", "mqtt"); } else { uploadHtml("CA file", "/mqtt-ca", "mqtt"); } LittleFS.end(); } else { server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); } } void AmsWebServer::mqttCaUpload() { if(!checkSecurity(1)) return; uploadFile(FILE_MQTT_CA); HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_END) { server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); MqttConfig mqttConfig; if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { config->setMqttChanged(); } } } void AmsWebServer::mqttCaDelete() { if(!checkSecurity(1)) return; if(!uploading) { // Not an upload deleteFile(FILE_MQTT_CA); server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); MqttConfig mqttConfig; if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { config->setMqttChanged(); } } else { uploading = false; server.send(200); } } void AmsWebServer::mqttCert() { printD(F("Serving /mqtt-cert.html over http...")); if(!checkSecurity(1)) return; if(LittleFS.begin()) { if(LittleFS.exists(FILE_MQTT_CERT)) { deleteHtml("Certificate", "/mqtt-cert/delete", "mqtt"); } else { uploadHtml("Certificate", "/mqtt-cert", "mqtt"); } LittleFS.end(); } else { server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); } } void AmsWebServer::mqttCertUpload() { if(!checkSecurity(1)) return; uploadFile(FILE_MQTT_CERT); HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_END) { server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); MqttConfig mqttConfig; if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { config->setMqttChanged(); } } } void AmsWebServer::mqttCertDelete() { if(!checkSecurity(1)) return; if(!uploading) { // Not an upload deleteFile(FILE_MQTT_CERT); server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); MqttConfig mqttConfig; if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { config->setMqttChanged(); } } else { uploading = false; server.send(200); } } void AmsWebServer::mqttKey() { printD(F("Serving /mqtt-key.html over http...")); if(!checkSecurity(1)) return; if(LittleFS.begin()) { if(LittleFS.exists(FILE_MQTT_KEY)) { deleteHtml("Private key", "/mqtt-key/delete", "mqtt"); } else { uploadHtml("Private key", "/mqtt-key", "mqtt"); } LittleFS.end(); } else { server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); } } void AmsWebServer::mqttKeyUpload() { if(!checkSecurity(1)) return; uploadFile(FILE_MQTT_KEY); HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_END) { server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); MqttConfig mqttConfig; if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { config->setMqttChanged(); } } } void AmsWebServer::mqttKeyDelete() { if(!checkSecurity(1)) return; if(!uploading) { // Not an upload deleteFile(FILE_MQTT_KEY); server.sendHeader(HEADER_LOCATION,F("/mqtt")); server.send(303); MqttConfig mqttConfig; if(config->getMqttConfig(mqttConfig) && mqttConfig.ssl) { config->setMqttChanged(); } } else { uploading = false; server.send(200); } } void AmsWebServer::factoryResetHtml() { if(!checkSecurity(1)) return; server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR); server.setContentLength(RESET_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(RESET_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::factoryResetPost() { if(!checkSecurity(1)) return; printD(F("Performing factory reset")); if(server.hasArg(F("perform")) && server.arg(F("perform")) == F("true")) { printD(F("Formatting LittleFS")); LittleFS.format(); printD(F("Clearing configuration")); config->clear(); printD(F("Setting restart flag and redirecting")); performRestart = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); } else { server.sendHeader(HEADER_LOCATION,F("/")); server.send(303); } } void AmsWebServer::notFound() { server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR); server.setContentLength(NOTFOUND_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(404, MIME_HTML, HEAD_HTML); server.sendContent_P(NOTFOUND_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::printD(String fmt, ...) { va_list args; va_start(args, fmt); if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args); va_end(args); } void AmsWebServer::printI(String fmt, ...) { va_list args; va_start(args, fmt); if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args); va_end(args); } void AmsWebServer::printW(String fmt, ...) { va_list args; va_start(args, fmt); if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args); va_end(args); } void AmsWebServer::printE(String fmt, ...) { va_list args; va_start(args, fmt); if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args); va_end(args); } void AmsWebServer::configFileHtml() { printD(F("Serving /configfile.html over http...")); if(!checkSecurity(1)) return; server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.setContentLength(CONFIGFILE_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); server.send_P(200, MIME_HTML, HEAD_HTML); server.sendContent_P(CONFIGFILE_HTML); server.sendContent_P(FOOT_HTML); } void AmsWebServer::configFileDownload() { printD(F("Serving /configfile.cfg over http...")); if(!checkSecurity(1)) return; bool includeSecrets = server.hasArg(F("ic")) && server.arg(F("ic")) == F("true"); bool includeWifi = server.hasArg(F("iw")) && server.arg(F("iw")) == F("true"); bool includeMqtt = server.hasArg(F("im")) && server.arg(F("im")) == F("true"); bool includeWeb = server.hasArg(F("ie")) && server.arg(F("ie")) == F("true"); bool includeMeter = server.hasArg(F("it")) && server.arg(F("it")) == F("true"); bool includeGpio = server.hasArg(F("ig")) && server.arg(F("ig")) == F("true"); bool includeDomo = server.hasArg(F("id")) && server.arg(F("id")) == F("true"); bool includeNtp = server.hasArg(F("in")) && server.arg(F("in")) == F("true"); bool includeEntsoe = server.hasArg(F("is")) && server.arg(F("is")) == F("true"); bool includeThresholds = server.hasArg(F("nh")) && server.arg(F("nh")) == F("true"); SystemConfig sys; config->getSystemConfig(sys); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE); server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF); server.sendHeader(F("Content-Disposition"), F("attachment; filename=configfile.cfg")); server.setContentLength(CONTENT_LENGTH_UNKNOWN); server.send_P(200, MIME_PLAIN, PSTR("amsconfig\n")); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("version %s\n"), VERSION)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("boardType %d\n"), sys.boardType)); if(includeWifi) { WiFiConfig wifi; config->getWiFiConfig(wifi); if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("hostname %s\n"), wifi.hostname)); if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ssid %s\n"), wifi.ssid)); if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("psk %s\n"), wifi.psk)); if(strlen(wifi.ip) > 0) { server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ip %s\n"), wifi.ip)); if(strlen(wifi.gateway) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gateway %s\n"), wifi.gateway)); if(strlen(wifi.subnet) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("subnet %s\n"), wifi.subnet)); if(strlen(wifi.dns1) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dns1 %s\n"), wifi.dns1)); if(strlen(wifi.dns2) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dns2 %s\n"), wifi.dns2)); } server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mdns %d\n"), wifi.mdns ? 1 : 0)); } if(includeMqtt) { MqttConfig mqtt; config->getMqttConfig(mqtt); if(strlen(mqtt.host) > 0) { server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttHost %s\n"), mqtt.host)); if(mqtt.port > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPort %d\n"), mqtt.port)); if(strlen(mqtt.clientId) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttClientId %s\n"), mqtt.clientId)); if(strlen(mqtt.publishTopic) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPublishTopic %s\n"), mqtt.publishTopic)); if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttUsername %s\n"), mqtt.username)); if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPassword %s\n"), mqtt.password)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPayloadFormat %d\n"), mqtt.payloadFormat)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttSsl %d\n"), mqtt.ssl ? 1 : 0)); } } if(includeWeb && includeSecrets) { WebConfig web; config->getWebConfig(web); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("webSecurity %d\n"), web.security)); if(web.security > 0) { server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("webUsername %s\n"), web.username)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("webPassword %s\n"), web.password)); } } if(includeMeter) { MeterConfig meter; config->getMeterConfig(meter); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterBaud %d\n"), meter.baud)); char parity[4] = ""; switch(meter.parity) { case 2: strcpy_P(parity, PSTR("7N1")); break; case 3: strcpy_P(parity, PSTR("8N1")); break; case 10: strcpy_P(parity, PSTR("7E1")); break; case 11: strcpy_P(parity, PSTR("8E1")); break; } if(strlen(parity) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterParity %s\n"), parity)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterInvert %d\n"), meter.invert ? 1 : 0)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterDistributionSystem %d\n"), meter.distributionSystem)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterMainFuse %d\n"), meter.mainFuse)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterProductionCapacity %d\n"), meter.productionCapacity)); if(includeSecrets) { if(meter.encryptionKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterEncryptionKey %s\n"), toHex(meter.encryptionKey, 16).c_str())); if(meter.authenticationKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAuthenticationKey %s\n"), toHex(meter.authenticationKey, 16).c_str())); } } if(includeGpio) { GpioConfig gpio; config->getGpioConfig(gpio); if(gpio.hanPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioHanPin %d\n"), gpio.hanPin)); if(gpio.apPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioApPin %d\n"), gpio.apPin)); if(gpio.ledPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPin %d\n"), gpio.ledPin)); if(gpio.ledPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedInverted %d\n"), gpio.ledInverted ? 1 : 0)); if(gpio.ledPinRed != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPinRed %d\n"), gpio.ledPinRed)); if(gpio.ledPinGreen != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPinGreen %d\n"), gpio.ledPinGreen)); if(gpio.ledPinBlue != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPinBlue %d\n"), gpio.ledPinBlue)); if(gpio.ledPinRed != 0xFF || gpio.ledPinGreen != 0xFF || gpio.ledPinBlue != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedRgbInverted %d\n"), gpio.ledRgbInverted ? 1 : 0)); if(gpio.tempSensorPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioTempSensorPin %d\n"), gpio.tempSensorPin)); if(gpio.tempAnalogSensorPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioTempAnalogSensorPin %d\n"), gpio.tempAnalogSensorPin)); if(gpio.vccPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccPin %d\n"), gpio.vccPin)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccOffset %.2f\n"), gpio.vccOffset / 100.0)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccMultiplier %.3f\n"), gpio.vccMultiplier / 1000.0)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccBootLimit %.1f\n"), gpio.vccBootLimit / 10.0)); if(gpio.vccPin != 0xFF && gpio.vccResistorGnd != 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccResistorGnd %d\n"), gpio.vccResistorGnd)); if(gpio.vccPin != 0xFF && gpio.vccResistorVcc != 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccResistorVcc %d\n"), gpio.vccResistorVcc)); } if(includeDomo) { DomoticzConfig domo; config->getDomoticzConfig(domo); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzElidx %d\n"), domo.elidx)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzVl1idx %d\n"), domo.vl1idx)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzVl2idx %d\n"), domo.vl2idx)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzVl3idx %d\n"), domo.vl3idx)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzCl1idx %d\n"), domo.cl1idx)); } if(includeNtp) { NtpConfig ntp; config->getNtpConfig(ntp); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpEnable %d\n"), ntp.enable ? 1 : 0)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpDhcp %d\n"), ntp.dhcp ? 1 : 0)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpOffset %d\n"), ntp.offset * 10)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpSummerOffset %d\n"), ntp.summerOffset * 10)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpServer %s\n"), ntp.server)); } if(includeEntsoe) { EntsoeConfig entsoe; config->getEntsoeConfig(entsoe); if(strlen(entsoe.token) == 36 && includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeToken %s\n"), entsoe.token)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeArea %s\n"), entsoe.area)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeCurrency %s\n"), entsoe.currency)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeMultiplier %.3f\n"), entsoe.multiplier / 1000.0)); } if(includeThresholds) { EnergyAccountingConfig eac; config->getEnergyAccountingConfig(eac); if(eac.thresholds[9] > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("thresholds %d %d %d %d %d %d %d %d %d %d %d\n"), eac.thresholds[0], eac.thresholds[1], eac.thresholds[2], eac.thresholds[3], eac.thresholds[4], eac.thresholds[5], eac.thresholds[6], eac.thresholds[7], eac.thresholds[8], eac.thresholds[9], eac.hours )); } if(ds != NULL) { DayDataPoints day = ds->getDayData(); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dayplot %d %lld %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), day.version, (int64_t) day.lastMeterReadTime, day.activeImport, ds->getHourImport(0), ds->getHourImport(1), ds->getHourImport(2), ds->getHourImport(3), ds->getHourImport(4), ds->getHourImport(5), ds->getHourImport(6), ds->getHourImport(7), ds->getHourImport(8), ds->getHourImport(9), ds->getHourImport(10), ds->getHourImport(11), ds->getHourImport(12), ds->getHourImport(13), ds->getHourImport(14), ds->getHourImport(15), ds->getHourImport(16), ds->getHourImport(17), ds->getHourImport(18), ds->getHourImport(19), ds->getHourImport(20), ds->getHourImport(21), ds->getHourImport(22), ds->getHourImport(23) )); if(day.activeExport > 0) { server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR(" %u %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"), day.activeExport, ds->getHourExport(0), ds->getHourExport(1), ds->getHourExport(2), ds->getHourExport(3), ds->getHourExport(4), ds->getHourExport(5), ds->getHourExport(6), ds->getHourExport(7), ds->getHourExport(8), ds->getHourExport(9), ds->getHourExport(10), ds->getHourExport(11), ds->getHourExport(12), ds->getHourExport(13), ds->getHourExport(14), ds->getHourExport(15), ds->getHourExport(16), ds->getHourExport(17), ds->getHourExport(18), ds->getHourExport(19), ds->getHourExport(20), ds->getHourExport(21), ds->getHourExport(22), ds->getHourExport(23) )); } else { server.sendContent(F("\n")); } MonthDataPoints month = ds->getMonthData(); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("monthplot %d %lld %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), month.version, (int64_t) month.lastMeterReadTime, month.activeImport, ds->getDayImport(1), ds->getDayImport(2), ds->getDayImport(3), ds->getDayImport(4), ds->getDayImport(5), ds->getDayImport(6), ds->getDayImport(7), ds->getDayImport(8), ds->getDayImport(9), ds->getDayImport(10), ds->getDayImport(11), ds->getDayImport(12), ds->getDayImport(13), ds->getDayImport(14), ds->getDayImport(15), ds->getDayImport(16), ds->getDayImport(17), ds->getDayImport(18), ds->getDayImport(19), ds->getDayImport(20), ds->getDayImport(21), ds->getDayImport(22), ds->getDayImport(23), ds->getDayImport(24), ds->getDayImport(25), ds->getDayImport(26), ds->getDayImport(27), ds->getDayImport(28), ds->getDayImport(29), ds->getDayImport(30), ds->getDayImport(31) )); if(month.activeExport > 0) { server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR(" %u %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"), month.activeExport, ds->getDayExport(1), ds->getDayExport(2), ds->getDayExport(3), ds->getDayExport(4), ds->getDayExport(5), ds->getDayExport(6), ds->getDayExport(7), ds->getDayExport(8), ds->getDayExport(9), ds->getDayExport(10), ds->getDayExport(11), ds->getDayExport(12), ds->getDayExport(13), ds->getDayExport(14), ds->getDayExport(15), ds->getDayExport(16), ds->getDayExport(17), ds->getDayExport(18), ds->getDayExport(19), ds->getDayExport(20), ds->getDayExport(21), ds->getDayExport(22), ds->getDayExport(23), ds->getDayExport(24), ds->getDayExport(25), ds->getDayExport(26), ds->getDayExport(27), ds->getDayExport(28), ds->getDayExport(29), ds->getDayExport(30), ds->getDayExport(31) )); } else { server.sendContent(F("\n")); } } if(ea != NULL) { EnergyAccountingConfig eac; config->getEnergyAccountingConfig(eac); EnergyAccountingData ead = ea->getData(); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("energyaccounting %d %d %.2f %.2f %.2f %.2f %d %.2f %d %.2f %d %.2f %d %.2f %d %.2f"), ead.version, ead.month, 0.0, // Old max ead.costYesterday / 10.0, ead.costThisMonth / 1.0, ead.costLastMonth / 1.0, ead.peaks[0].day, ead.peaks[0].value / 100.0, ead.peaks[1].day, ead.peaks[1].value / 100.0, ead.peaks[2].day, ead.peaks[2].value / 100.0, ead.peaks[3].day, ead.peaks[3].value / 100.0, ead.peaks[4].day, ead.peaks[4].value / 100.0 )); server.sendContent("\n"); } } void AmsWebServer::configFileUpload() { if(!checkSecurity(1)) return; HTTPUpload& upload = uploadFile(FILE_CFG); if(upload.status == UPLOAD_FILE_END) { performRestart = true; server.sendHeader(HEADER_LOCATION,F("/restart-wait")); server.send(303); } }