//todo: // - more accurate TX/RX indication (for security concern), e.g. favicon is served static, thus not display TX/RX arrow. // - Login and apikey access for next version #include "wifi.h" #include "upsCfg.h" #ifndef UPSIO_H #include "upsIo.h" #endif //#include //for esp32 #include //for esp8266 #include int8_t wifiStatePrev = 99; bool configChanged = false; AsyncWebServer server(80); void applyConfigs(){ autoPowerOn = (cfgAutoOn >= AUTO_POWER_ON); //auto power on //cfgChargeMode: charge mode: switch (cfgChargeMode){ case CHARGE_MODE_ALWAYS:{ chargeAlwaysActive = true; } break; case CHARGE_MODE_RANGE_90_100:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 100; chargePercentageForResume = 90; } break; case CHARGE_MODE_RANGE_85_100:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 100; chargePercentageForResume = 85; } break; case CHARGE_MODE_RANGE_80_100:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 100; chargePercentageForResume = 80; } break; case CHARGE_MODE_RANGE_90_95:{ chargeAlwaysActive = false; chargeToFull = false; chargePercentageForStop = 95; chargePercentageForResume = 90; } break; case CHARGE_MODE_RANGE_85_95:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 95; chargePercentageForResume = 85; } break; case CHARGE_MODE_RANGE_80_95:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 95; chargePercentageForResume = 80; } break; case CHARGE_MODE_RANGE_85_90:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 90; chargePercentageForResume = 85; } break; case CHARGE_MODE_RANGE_80_90:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 90; chargePercentageForResume = 80; } break; case CHARGE_MODE_RANGE_80_85:{ chargeAlwaysActive = false; chargeToFull = true; chargePercentageForStop = 85; chargePercentageForResume = 80; } break; } //cfgInputLostTriggerChargeMode: switch (cfgInputLostTriggerChargeMode){ case INPUT_LOST_TRIGGER_CHARGE_NONE:{ chargeAfterInputLost = false; } break; case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_ALWAYS:{ chargeAfterInputLost = true; fullChargeAfterInputLost = true; } break; case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_95:{ chargeAfterInputLost = true; fullChargeAfterInputLost = false; triggerFullChargeAfterInputLostAtPercentage = 95; } break; case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_90:{ chargeAfterInputLost = true; fullChargeAfterInputLost = false; triggerFullChargeAfterInputLostAtPercentage = 90; } break; case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_85:{ chargeAfterInputLost = true; fullChargeAfterInputLost = false; triggerFullChargeAfterInputLostAtPercentage = 85; } break; case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_80:{ chargeAfterInputLost = true; fullChargeAfterInputLost = false; triggerFullChargeAfterInputLostAtPercentage = 80; } break; } //cfgChargeOvervoltageProtection: chargeOvervoltageProtection = (cfgChargeOvervoltageProtection > 0); //cfgChargeAutomaticReducer: chargeReduceAtBatteryFull = (cfgChargeAutomaticReducer > 0); } bool getShutdownSuggestionFlag(){ if (cfgShutdownSuggestion==SHUTDOWN_SUGGESTION_DISABLED) return false; if (!startupReady) return false; switch(cfgShutdownSuggestion){ case SHUTDOWN_SUGGESTION_ON_BATT:{ return powerOutputActive && !inputVoltageKeepup; } break; case SHUTDOWN_SUGGESTION_ON_BATT_75:{ return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=3); } break; case SHUTDOWN_SUGGESTION_ON_BATT_50:{ return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=2); } break; case SHUTDOWN_SUGGESTION_ON_BATT_25:{ return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=1); } break; case SHUTDOWN_SUGGESTION_ON_BATT_CRITICAL:{ return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=1) && isUpsBatteryCritical(); } break; default:{ return false; } } return false; } void handleGetState(AsyncWebServerRequest *request) { setNetworkIndicatorInTransmission(true, false); bool isRefresh = true; String v = ""; uint8_t i = 0; if (request->hasParam("on")) { isRefresh = false; v = request->getParam("on")->value(); bool tgtState = v != "0"; Serial.print("Switch request: "); Serial.println(tgtState); if (tgtState != powerOutputActive) { setOutputPowerState(tgtState); Serial.println("Switching..."); } } if (request->hasParam("auto")) { isRefresh = false; v = request->getParam("auto")->value(); uint8_t tmpAutoOnMode = v.toInt(); if ((tmpAutoOnMode != cfgAutoOn) && (tmpAutoOnMode <= AUTO_POWER_PERSIST) ) { cfgAutoOn = tmpAutoOnMode; configChanged = true; } } if (request->hasParam("shutdown")) { isRefresh = false; v = request->getParam("shutdown")->value(); uint16_t tSec = v.toInt(); if (tSec > 999) tSec = 999; uint32_t nanoT = ((uint32_t)tSec) * 1000000UL; if (nanoT < 999999) nanoT = 999999; bool ok = setPersistentShutdown(nanoT); if ((cfgShutdownInSeconds != tSec && ok)){ cfgShutdownInSeconds = tSec; configChanged = true; } Serial.print("Timed Shutdown request: "); Serial.println(tSec); //Serial.println(ok); } if (request->hasParam("shutlv")) { isRefresh = false; v = request->getParam("shutlv")->value(); int16_t lv = v.toInt(); if (lv >= SHUTDOWN_SUGGESTION_DISABLED && lv <= SHUTDOWN_SUGGESTION_ON_BATT_CRITICAL) { if (lv != cfgShutdownSuggestion) { cfgShutdownSuggestion = lv; configChanged = true; Serial.print("Shutdown level changed: "); Serial.println(lv); } } else { Serial.print("Invalid shutdown level: "); Serial.println(v); } } //battery management params: //cfgChargeMode (charge mode): if (request->hasParam("cm")) { isRefresh = false; v = request->getParam("cm")->value(); i = v.toInt(); if (i<=CHARGE_MODE_RANGE_80_85){ cfgChargeMode = i; configChanged = true; Serial.print("CM: "); Serial.println(i); } } //cfgInputLostTriggerChargeMode (charge on input lost): if (request->hasParam("coil")) { isRefresh = false; v = request->getParam("coil")->value(); i = v.toInt(); if (i<=INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_80){ cfgInputLostTriggerChargeMode = i; configChanged = true; Serial.print("COIL: "); Serial.println(i); } } //cfgChargeOvervoltageProtection (charge overvoltage protection): if (request->hasParam("covp")) { isRefresh = false; v = request->getParam("covp")->value(); i = v.toInt(); if (i>1) i = 1; if (i!=cfgChargeOvervoltageProtection){ cfgChargeOvervoltageProtection = i; configChanged = true; Serial.print("COVP: "); Serial.println(i); } } //reset "On-Going Full Charge" triggered after input lost (reset ongoing full charge): if (request->hasParam("rofc")) { isRefresh = false; v = request->getParam("rofc")->value(); i = v.toInt(); if (i>0 && needFullChargeOnce) { resetOnGoingFullChargeAfterLineLost(); Serial.print("ROFC: "); Serial.println(i); } } //cfgChargeAutomaticReducer (charge automatic reducer) if (request->hasParam("car")) { isRefresh = false; v = request->getParam("car")->value(); i = v.toInt(); if (i>1) i = 1; if (i!=cfgChargeAutomaticReducer){ cfgChargeAutomaticReducer = i; configChanged = true; Serial.print("CAR: "); Serial.println(i); } } DynamicJsonDocument jsonDoc(1024); String jsonResponse; //String stateOn = isUpsOn()?"1":"0"; //String autoOn = String(cfgAutoOn); //String voltageValue = String(batteryVoltage, 2); //2 battery, 1 Input line or AC, 0 OFF //String lineActive = isUpsPoweredFromBattery()?"2":isInputLinePowered()?"1":isUpsOn()?"2":"0"; uint8_t lineActiveState = isUpsPoweredFromBattery()?2:isInputLinePowered()?1:isUpsOn()?2:0; //String batt = String(batteryLevel); //String battMax = "4"; // String battBlink = IsUpsChargingOrBlink()?"1":"0"; // String chargeLv = IsUpsChargingOrBlink()?(String(UpsBattMinimalLevelUnderChargingOrBlink())):(String(UpsBatteryLevel())); // String battCritical = IsUpsBatteryCritical()?"1":"0"; // String isTimedShut = timedShutdownFlag?"1":"0"; // String onShutRemaining = String(GetShutdownCountdown()); // String shutdownSuggest = GetShutdownSuggestionFlag()?"1":"0"; // String shutdownSuggestMode = String(cfgShutdownSuggestion); // String shutdownLastPeriod = String(timeShutdownLastPeriod); //json vars: jsonDoc["success"] = isRefresh ? 2 : 1; jsonDoc["on"] = powerOutputActive?1:0;; jsonDoc["state"] = isUpsOn()?1:0; jsonDoc["v"] = String(batteryVoltage, 2); jsonDoc["batt"] = batteryLevel; jsonDoc["battP"] = batteryPercentage; jsonDoc["line"] = lineActiveState; jsonDoc["auto"] = cfgAutoOn; jsonDoc["battBlink"] = isUpsBatteryIndicatorChargingOrBlink()?1:0; jsonDoc["battCritical"] = isUpsBatteryCritical()?1:0; jsonDoc["chargeLv"] = getRawBatteryLevelFromPercentage(batteryPercentage); //real level jsonDoc["batt"] = batteryLevel; //staged level jsonDoc["battMax"] = 4; jsonDoc["timedShutdown"] = (persistentShutdown && powerOutputActive)?1:0; jsonDoc["shutdownRemain"] = getPersistentShutdownTick()/1000000; jsonDoc["shutdownSuggestMode"] = cfgShutdownSuggestion; jsonDoc["shutdownSuggest"] = getShutdownSuggestionFlag()?1:0; //internal flag raised if cfgShutdownSuggestion fullfill the condition jsonDoc["shutLastTimer"] = cfgShutdownInSeconds; //charge configuration: jsonDoc["chgError"] = chargeError; //error state jsonDoc["charging"] = getChargingState(); //on charging state jsonDoc["chgMode"] = cfgChargeMode; jsonDoc["chgOVP"] = cfgChargeOvervoltageProtection; //over voltage protection jsonDoc["chgReducer"] = cfgChargeAutomaticReducer; jsonDoc["chgFullTrig"] = cfgInputLostTriggerChargeMode; jsonDoc["chgOFC"] = needFullChargeOnce?1:0; //when input lost and "Ongoing Full Charge" flag triggered serializeJson(jsonDoc, jsonResponse); request->send(200, "application/json", jsonResponse); setNetworkIndicatorInTransmission(true, true); } void onRootRequest(AsyncWebServerRequest *request) { setNetworkIndicatorInTransmission(true, false); Serial.println("onRootRequest"); bool isOK = false; if (spiffsOk) { if (SPIFFS.exists("/index.html")) { isOK = true; request->send(SPIFFS, "/index.html", "text/html"); setNetworkIndicatorInTransmission(true, true); } } if (!isOK) { request->send(404, "text/text", "404: Not Found"); setNetworkIndicatorInTransmission(true, true); } } void upsServerInit() { bool b = initConfigs(); if (b){ loadCfg(); applyConfigs(); } //transfer configuration to ups library: if (cfgAutoOn >= AUTO_POWER_ON) { autoPowerOn = true; restartAutoPowerOn(); } else { autoPowerOn = false; } //for favicon server.serveStatic("/favicon.ico", SPIFFS, "/favicon.ico"); server.serveStatic("*/favicon.ico", SPIFFS, "/favicon.ico"); //for RESTapi server.on("/state", HTTP_GET, handleGetState); //root server.on("/", onRootRequest); server.on("/*", onRootRequest); //Redirect all not-found requests to the root server.onNotFound(onRootRequest); server.begin(); Serial.println("Server is running!"); } void upsServerRoutine() { WiFiManagerRoutine(); //updating wifi state: if (wifiCurrentState != wifiStatePrev) { wifiStatePrev = wifiCurrentState; setNetworkState(wifiStatePrev); } //update signal: indicatorNetworkSignalLevel = wifiSignalLevel; //manage auto on mode: if (cfgAutoOn == AUTO_POWER_PERSIST) { if (inputVoltageKeepup && inputVoltageOK) { if (restartAutoPowerOn()) Serial.println("Automatic ON restarted"); } } //if button long pressed ------------------------------------------- if (buttonLongPressIgnore && buttonLongPress){ buttonLongPressIgnore = true; Serial.println("Long Pressed Button Action is Reserved!"); //(not used) } //------------------------------------------------------------------ //saving configuration: if (configChanged) { configChanged = false; saveCfg(); applyConfigs(); } }