From 89393cee586bab2ce832bbcf335cacdd7fdb3cee Mon Sep 17 00:00:00 2001 From: iranl Date: Mon, 26 Aug 2024 23:41:54 +0200 Subject: [PATCH] PsychicHTTP --- lib/MqttLogger/src/MqttLogger.cpp | 4 +- lib/MqttLogger/src/MqttLogger.h | 2 +- lib/PsychicHttp/src/PsychicHttpServer.cpp | 8 + lib/PsychicHttp/src/PsychicRequest.cpp | 17 + lib/PsychicHttp/src/PsychicRequest.h | 4 +- lib/WiFiManager/WiFiManager.cpp | 225 +- lib/WiFiManager/WiFiManager.h | 44 +- sdkconfig.defaults | 11 +- src/Config.h | 2 +- src/NukiNetwork.h | 2 - src/NukiNetworkLock.h | 2 - src/WebCfgServer.cpp | 2513 +++++++++++---------- src/WebCfgServer.h | 85 +- src/main.cpp | 33 +- updater/platformio.ini | 6 +- updater/sdkconfig.defaults | 11 +- 16 files changed, 1522 insertions(+), 1447 deletions(-) diff --git a/lib/MqttLogger/src/MqttLogger.cpp b/lib/MqttLogger/src/MqttLogger.cpp index d1b1c71..d8d08f7 100644 --- a/lib/MqttLogger/src/MqttLogger.cpp +++ b/lib/MqttLogger/src/MqttLogger.cpp @@ -90,8 +90,8 @@ void MqttLogger::sendBuffer() } if (doWebSerial) { - WebSerial.write(this->buffer, this->bufferCnt); - WebSerial.println(); + //WebSerial.write(this->buffer, this->bufferCnt); + //WebSerial.println(); } this->bufferCnt=0; } diff --git a/lib/MqttLogger/src/MqttLogger.h b/lib/MqttLogger/src/MqttLogger.h index 6f30a6f..d82e62a 100644 --- a/lib/MqttLogger/src/MqttLogger.h +++ b/lib/MqttLogger/src/MqttLogger.h @@ -12,7 +12,7 @@ #include #include #include -#include "MycilaWebSerial.h" +//#include "MycilaWebSerial.h" #define MQTT_MAX_PACKET_SIZE 1024 diff --git a/lib/PsychicHttp/src/PsychicHttpServer.cpp b/lib/PsychicHttp/src/PsychicHttpServer.cpp index 628f38b..f6cbb7f 100644 --- a/lib/PsychicHttp/src/PsychicHttpServer.cpp +++ b/lib/PsychicHttp/src/PsychicHttpServer.cpp @@ -325,11 +325,19 @@ const std::list& PsychicHttpServer::getClientList() { } bool ON_STA_FILTER(PsychicRequest *request) { + #ifndef CONFIG_IDF_TARGET_ESP32H2 return WiFi.localIP() == request->client()->localIP(); + #else + return false; + #endif } bool ON_AP_FILTER(PsychicRequest *request) { + #ifndef CONFIG_IDF_TARGET_ESP32H2 return WiFi.softAPIP() == request->client()->localIP(); + #else + return false; + #endif } String urlDecode(const char* encoded) diff --git a/lib/PsychicHttp/src/PsychicRequest.cpp b/lib/PsychicHttp/src/PsychicRequest.cpp index 4244358..2005d91 100644 --- a/lib/PsychicHttp/src/PsychicRequest.cpp +++ b/lib/PsychicHttp/src/PsychicRequest.cpp @@ -318,6 +318,11 @@ PsychicWebParameter * PsychicRequest::addParam(PsychicWebParameter *param) { return param; } +int PsychicRequest::params() +{ + return _params.size(); +} + bool PsychicRequest::hasParam(const char *key) { return getParam(key) != NULL; @@ -332,6 +337,18 @@ PsychicWebParameter * PsychicRequest::getParam(const char *key) return NULL; } +PsychicWebParameter * PsychicRequest::getParam(int index) +{ + if (_params.size() > index){ + std::list::iterator it = _params.begin(); + for(int i=0; i_session->find(key) != this->_session->end(); diff --git a/lib/PsychicHttp/src/PsychicRequest.h b/lib/PsychicHttp/src/PsychicRequest.h index fe48a1b..5b54d6e 100644 --- a/lib/PsychicHttp/src/PsychicRequest.h +++ b/lib/PsychicHttp/src/PsychicRequest.h @@ -81,9 +81,11 @@ class PsychicRequest { void loadParams(); PsychicWebParameter * addParam(PsychicWebParameter *param); PsychicWebParameter * addParam(const String &name, const String &value, bool decode = true, bool post = false); + int params(); bool hasParam(const char *key); PsychicWebParameter * getParam(const char *name); - + PsychicWebParameter * getParam(int index); + const String getFilename(); bool authenticate(const char * username, const char * password); diff --git a/lib/WiFiManager/WiFiManager.cpp b/lib/WiFiManager/WiFiManager.cpp index d7aa82c..34c6868 100644 --- a/lib/WiFiManager/WiFiManager.cpp +++ b/lib/WiFiManager/WiFiManager.cpp @@ -627,30 +627,28 @@ boolean WiFiManager::configPortalHasTimeout(){ } void WiFiManager::setupHTTPServer(){ - - server.reset(new WM_WebServer(_httpPort)); + server->listen(_httpPort); /* Setup httpd callbacks, web pages: root, wifi config pages, SO captive portal detectors and not found. */ - server->on(String(FPSTR(R_wifi)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleWifi, this,std::placeholders::_1,true)); - server->on(String(FPSTR(R_wifinoscan)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleWifi, this,std::placeholders::_1,false)); - server->on(String(FPSTR(R_erase)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleErase, this,std::placeholders::_1,false)); + server->on(String(FPSTR(R_wifi)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleWifi, this,std::placeholders::_1,true)); + server->on(String(FPSTR(R_wifinoscan)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleWifi, this,std::placeholders::_1,false)); + server->on(String(FPSTR(R_erase)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleErase, this,std::placeholders::_1,false)); { using namespace std::placeholders; - server->on(String(FPSTR(R_root)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleRoot, this,_1)); - server->on(String(FPSTR(R_wifisave)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleWifiSave, this,_1)); - server->on(String(FPSTR(R_info)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleInfo, this,_1)); - server->on(String(FPSTR(R_param)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleParam, this,_1)); - server->on(String(FPSTR(R_paramsave)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleParamSave, this,_1)); - server->on(String(FPSTR(R_restart)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleReset, this,_1)); - server->on(String(FPSTR(R_exit)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleExit, this,_1)); - server->on(String(FPSTR(R_close)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleClose, this,_1)); - server->on(String(FPSTR(R_status)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleWiFiStatus, this,_1)); - server->onNotFound (std::bind(&WiFiManager::handleNotFound, this, _1)); + server->on(String(FPSTR(R_root)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleRoot, this,_1)); + server->on(String(FPSTR(R_wifisave)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleWifiSave, this,_1)); + server->on(String(FPSTR(R_info)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleInfo, this,_1)); + server->on(String(FPSTR(R_param)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleParam, this,_1)); + server->on(String(FPSTR(R_paramsave)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleParamSave, this,_1)); + server->on(String(FPSTR(R_restart)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleReset, this,_1)); + server->on(String(FPSTR(R_exit)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleExit, this,_1)); + server->on(String(FPSTR(R_close)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleClose, this,_1)); + server->on(String(FPSTR(R_status)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleWiFiStatus, this,_1)); + server->onNotFound ((PsychicHttpRequestCallback)std::bind(&WiFiManager::handleNotFound, this, _1)); - server->on(String(FPSTR(R_update)).c_str(), HTTP_ANY, std::bind(&WiFiManager::handleUpdate, this, _1)); - server->on(String(FPSTR(R_updatedone)).c_str(), HTTP_POST,std::bind(&WiFiManager::handleUpdateDone, this, _1), std::bind(&WiFiManager::handleUpdating, this, _1,_2,_3,_4,_5,_6)); + server->on(String(FPSTR(R_update)).c_str(), (PsychicHttpRequestCallback)std::bind(&WiFiManager::handleUpdate, this, _1)); + //server->on(String(FPSTR(R_updatedone)).c_str(), HTTP_POST, std::bind(&WiFiManager::handleUpdateDone, this, _1), std::bind(&WiFiManager::handleUpdating, this, _1,_2,_3,_4,_5,_6)); } - server->begin(); // Web server start } void WiFiManager::teardownHTTPServer(){ @@ -979,7 +977,7 @@ bool WiFiManager::shutdownConfigPortal(){ // @todo what is the proper way to shutdown and free the server up // debug - many open issues aobut port not clearing for use with other servers #ifdef WM_ASYNCWEBSERVER - server->end(); + server->stop(); #else server->stop(); #endif @@ -1423,10 +1421,13 @@ String WiFiManager::getHTTPHead(String title){ return page; } -void WiFiManager::HTTPSend(AsyncWebServerRequest *request, String page){ - AsyncWebServerResponse *response = request->beginResponse(200,FPSTR(HTTP_HEAD_CT), page); - response->addHeader(FPSTR(HTTP_HEAD_CL), String(page.length())); - request->send(response); +esp_err_t WiFiManager::HTTPSend(PsychicRequest *request, String page){ + PsychicResponse response(request); + response.addHeader(HTTP_HEAD_CL, ((String)page.length()).c_str()); + response.setCode(200); + response.setContentType(HTTP_HEAD_CT); + response.setContent(page.c_str()); + return response.send(); } /** @@ -1446,12 +1447,12 @@ void WiFiManager::handleRequest() { /** * HTTPD CALLBACK root or redirect to captive portal */ -void WiFiManager::handleRoot(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleRoot(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Root")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - if (captivePortal(request)) return; // If captive portal redirect instead of displaying the page + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + if (captivePortal(request)) return 0; // If captive portal redirect instead of displaying the page handleRequest(); String page = getHTTPHead(_title); // @token options @todo replace options with title String str = FPSTR(HTTP_ROOT_MAIN); // @todo custom title @@ -1463,28 +1464,29 @@ void WiFiManager::handleRoot(AsyncWebServerRequest *request) { reportStatus(page); page += FPSTR(HTTP_END); - HTTPSend(request,page); if(_preloadwifiscan) WiFi_scanNetworks(_scancachetime,true); // preload wifiscan throttled, async // @todo buggy, captive portals make a query on every page load, causing this to run every time in addition to the real page load // I dont understand why, when you are already in the captive portal, I guess they want to know that its still up and not done or gone // if we can detect these and ignore them that would be great, since they come from the captive portal redirect maybe there is a refferer + + return HTTPSend(request,page); } /** * HTTPD CALLBACK Wifi config page handler */ -void WiFiManager::handleWifi(AsyncWebServerRequest *request,bool scan = true) { +esp_err_t WiFiManager::handleWifi(PsychicRequest *request,bool scan = true) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Wifi")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); String page = getHTTPHead(FPSTR(S_titlewifi)); // @token titlewifi if (scan) { #ifdef WM_DEBUG_LEVEL // DEBUG_WM(WM_DEBUG_DEV,"refresh flag:",request->hasArg(F("refresh"))); #endif - WiFi_scanNetworks(request->hasArg(F("refresh")),true); //wifiscan, force if arg refresh + WiFi_scanNetworks(request->hasParam("refresh")); //wifiscan, force if arg refresh page += getScanItemOut(); } String pitem = ""; @@ -1520,21 +1522,21 @@ void WiFiManager::handleWifi(AsyncWebServerRequest *request,bool scan = true) { reportStatus(page); page += FPSTR(HTTP_END); - HTTPSend(request,page); - #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("Sent config page")); #endif + + return HTTPSend(request,page); } /** * HTTPD CALLBACK Wifi param page handler */ -void WiFiManager::handleParam(AsyncWebServerRequest *request){ +esp_err_t WiFiManager::handleParam(PsychicRequest *request){ #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Param")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); String page = getHTTPHead(FPSTR(S_titleparam)); // @token titlewifi @@ -1550,11 +1552,11 @@ void WiFiManager::handleParam(AsyncWebServerRequest *request){ reportStatus(page); page += FPSTR(HTTP_END); - HTTPSend(request,page); - #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("Sent param page")); #endif + + return HTTPSend(request,page); } @@ -1914,34 +1916,35 @@ String WiFiManager::getParamOut(){ return page; } -void WiFiManager::handleWiFiStatus(AsyncWebServerRequest *request){ +esp_err_t WiFiManager::handleWiFiStatus(PsychicRequest *request){ #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP WiFi status ")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); String page; // String page = "{\"result\":true,\"count\":1}"; #ifdef WM_JSTEST page = FPSTR(HTTP_JS); #endif - HTTPSend(request,page); + + return HTTPSend(request,page); } /** * HTTPD CALLBACK save form and redirect to WLAN config page again */ -void WiFiManager::handleWifiSave(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleWifiSave(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP WiFi save ")); DEBUG_WM(WM_DEBUG_DEV,F("Method:"),request->method() == HTTP_GET ? (String)FPSTR(S_GET) : (String)FPSTR(S_POST)); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); //SAVE/connect here - _ssid = request->arg(F("s")).c_str(); - _pass = request->arg(F("p")).c_str(); + _ssid = request->getParam("s")->value(); + _pass = request->getParam("p")->value(); if(_ssid == "" && _pass != ""){ _ssid = WiFi_SSID(true); // password change, placeholder ssid, @todo compare pass to old?, confirm ssid is clean @@ -1957,40 +1960,40 @@ void WiFiManager::handleWifiSave(AsyncWebServerRequest *request) { requestinfo += "\nMethod: "; requestinfo += (request->method() == HTTP_GET) ? "GET" : "POST"; requestinfo += "\nArguments: "; - requestinfo += request->args(); + requestinfo += request->params(); requestinfo += "\n"; - for (uint8_t i = 0; i < request->args(); i++) { - requestinfo += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + for (uint8_t i = 0; i < request->params(); i++) { + requestinfo += " " + request->getParam(i)->name() + ": " + request->getParam(i)->value() + "\n"; } DEBUG_WM(WM_DEBUG_MAX,requestinfo); #endif // set static ips from server args - if (request->arg(FPSTR(S_ip)) != "") { + if (request->getParam(S_ip)->value() != "") { //_sta_static_ip.fromString(request->arg(FPSTR(S_ip)); - String ip = request->arg(FPSTR(S_ip)); + String ip = request->getParam(S_ip)->value(); optionalIPFromString(&_sta_static_ip, ip.c_str()); #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("static ip:"),ip); #endif } - if (request->arg(FPSTR(S_gw)) != "") { - String gw = request->arg(FPSTR(S_gw)); + if (request->getParam(S_gw)->value() != "") { + String gw = request->getParam(S_gw)->value(); optionalIPFromString(&_sta_static_gw, gw.c_str()); #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("static gateway:"),gw); #endif } - if (request->arg(FPSTR(S_sn)) != "") { - String sn = request->arg(FPSTR(S_sn)); + if (request->getParam(S_sn)->value() != "") { + String sn = request->getParam(S_sn)->value(); optionalIPFromString(&_sta_static_sn, sn.c_str()); #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("static netmask:"),sn); #endif } - if (request->arg(FPSTR(S_dns)) != "") { - String dns = request->arg(FPSTR(S_dns)); + if (request->getParam(S_dns)->value() != "") { + String dns = request->getParam(S_dns)->value(); optionalIPFromString(&_sta_static_dns, dns.c_str()); #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("static DNS:"),dns); @@ -2018,16 +2021,17 @@ void WiFiManager::handleWifiSave(AsyncWebServerRequest *request) { page += FPSTR(HTTP_END); //server->sendHeader(FPSTR(HTTP_HEAD_CORS), FPSTR(HTTP_HEAD_CORS_ALLOW_ALL)); // @HTTPHEAD send cors - HTTPSend(request,page); #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("Sent wifi save page")); #endif connect = true; //signal ready to connect/reset process in processConfigPortal + + return HTTPSend(request,page); } -void WiFiManager::handleParamSave(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleParamSave(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Param save ")); @@ -2035,7 +2039,7 @@ void WiFiManager::handleParamSave(AsyncWebServerRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("Method:"),request->method() == HTTP_GET ? (String)FPSTR(S_GET) : (String)FPSTR(S_POST)); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); doParamSave(request); @@ -2045,14 +2049,14 @@ void WiFiManager::handleParamSave(AsyncWebServerRequest *request) { if(_showBack) page += FPSTR(HTTP_BACKBTN); page += FPSTR(HTTP_END); - HTTPSend(request,page); - #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("Sent param save page")); #endif + + return HTTPSend(request,page); } -void WiFiManager::doParamSave(AsyncWebServerRequest *request){ +void WiFiManager::doParamSave(PsychicRequest *request){ // @todo use new callback for before paramsaves, is this really needed? if ( _presaveparamscallback != NULL) { _presaveparamscallback(); // @CALLBACK @@ -2075,10 +2079,10 @@ void WiFiManager::doParamSave(AsyncWebServerRequest *request){ //read parameter from server String name = (String)FPSTR(S_parampre)+(String)i; String value; - if(request->hasArg(name.c_str())) { - value = request->arg(name); + if(request->hasParam(name.c_str())) { + value = request->getParam(name.c_str())->value(); } else { - value = request->arg(_params[i]->getID()); + value = request->getParam(_params[i]->getID())->value(); } //store it in params array @@ -2101,11 +2105,11 @@ void WiFiManager::doParamSave(AsyncWebServerRequest *request){ /** * HTTPD CALLBACK info page */ -void WiFiManager::handleInfo(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleInfo(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Info")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); //handleRequest(); String page = getHTTPHead(FPSTR(S_titleinfo)); // @token titleinfo reportStatus(page); @@ -2203,11 +2207,11 @@ void WiFiManager::handleInfo(AsyncWebServerRequest *request) { page += FPSTR(HTTP_HELP); page += FPSTR(HTTP_END); - HTTPSend(request,page); - #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_DEV,F("Sent info page")); #endif + + return HTTPSend(request,page); } String WiFiManager::getInfoData(String id){ @@ -2447,41 +2451,45 @@ String WiFiManager::getInfoData(String id){ /** * HTTPD CALLBACK exit, closes configportal if blocking, if non blocking undefined */ -void WiFiManager::handleExit(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleExit(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Exit")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); String page = getHTTPHead(FPSTR(S_titleexit)); // @token titleexit page += FPSTR(S_exiting); // @token exiting // ('Logout', 401, {'WWW-Authenticate': 'Basic realm="Login required"'}) - AsyncWebServerResponse *response = request->beginResponse(200,FPSTR(HTTP_HEAD_CT), page); - response->addHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); - request->send(response); delay(2000); abort = true; + + PsychicResponse response(request); + response.addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.setCode(200); + response.setContentType(HTTP_HEAD_CT); + response.setContent(page.c_str()); + return response.send(); } /** * HTTPD CALLBACK reset page */ -void WiFiManager::handleReset(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleReset(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP Reset")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); String page = getHTTPHead(FPSTR(S_titlereset)); //@token titlereset page += FPSTR(S_resetting); //@token resetting page += FPSTR(HTTP_END); - HTTPSend(request,page); - #ifdef WM_DEBUG_LEVEL DEBUG_WM(F("RESETTING ESP")); #endif _rebootNeeded = true; + + return HTTPSend(request,page); } /** @@ -2491,11 +2499,11 @@ void WiFiManager::handleReset(AsyncWebServerRequest *request) { // void WiFiManager::handleErase() { // handleErase(false); // } -void WiFiManager::handleErase(AsyncWebServerRequest *request,bool opt = false) { +esp_err_t WiFiManager::handleErase(PsychicRequest *request,bool opt = false) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_NOTIFY,F("<- HTTP Erase")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); handleRequest(); String page = getHTTPHead(FPSTR(S_titleerase)); // @token titleerase @@ -2510,18 +2518,19 @@ void WiFiManager::handleErase(AsyncWebServerRequest *request,bool opt = false) { } page += FPSTR(HTTP_END); - HTTPSend(request,page); if(ret){ _rebootNeeded = true; } + + return HTTPSend(request,page); } /** * HTTPD CALLBACK 404 */ -void WiFiManager::handleNotFound(AsyncWebServerRequest *request) { - if (captivePortal(request)) return; // If captive portal redirect instead of displaying the page +esp_err_t WiFiManager::handleNotFound(PsychicRequest *request) { + if (captivePortal(request)) return 0; // If captive portal redirect instead of displaying the page handleRequest(); String message = FPSTR(S_notfound); // @token notfound @@ -2532,18 +2541,22 @@ void WiFiManager::handleNotFound(AsyncWebServerRequest *request) { message += FPSTR(S_method); // @token method message += ( request->method() == HTTP_GET ) ? FPSTR(S_GET) : FPSTR(S_POST); message += FPSTR(S_args); // @token args - message += request->args(); + message += request->params(); message += F("\n"); - for ( uint8_t i = 0; i < request->args(); i++ ) { - message += " " + request->argName ( i ) + ": " + request->arg ( i ) + "\n"; + for ( uint8_t i = 0; i < request->params(); i++ ) { + message += " " + request->getParam(i)->name() + ": " + request->getParam(i)->value() + "\n"; } } - AsyncWebServerResponse *response = request->beginResponse(404,FPSTR(HTTP_HEAD_CT2), message); - response->addHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); - response->addHeader(F("Pragma"), F("no-cache")); - response->addHeader(F("Expires"), F("-1")); - request->send(response); + + PsychicResponse response(request); + response.addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + response.addHeader("Pragma", "no-cache"); + response.addHeader("Expires", "-1"); + response.setCode(404); + response.setContentType(HTTP_HEAD_CT2); + response.setContent(message.c_str()); + return response.send(); } /** @@ -2551,7 +2564,7 @@ void WiFiManager::handleNotFound(AsyncWebServerRequest *request) { * Redirect to captive portal if we got a request for another domain. * Return true in that case so the page handler do not try to handle the request again. */ -boolean WiFiManager::captivePortal(AsyncWebServerRequest *request) { +boolean WiFiManager::captivePortal(PsychicRequest *request) { if(!_enableCaptivePortal || !configPortalActive) return false; // skip redirections if cp not enabled or not in ap mode @@ -2578,9 +2591,11 @@ boolean WiFiManager::captivePortal(AsyncWebServerRequest *request) { DEBUG_WM(WM_DEBUG_VERBOSE,F("<- Request redirected to captive portal")); DEBUG_WM(WM_DEBUG_DEV,"serverLoc " + serverLoc); #endif - AsyncWebServerResponse *response = request->beginResponse(302,FPSTR(HTTP_HEAD_CT2), ""); - response->addHeader(F("Location"), (String)F("http://") + serverLoc); - request->send(response); + PsychicResponse response(request); + response.addHeader("Location", ((String)("http://") + serverLoc).c_str()); + response.setCode(302); + response.setContentType(HTTP_HEAD_CT2); + response.send(); return true; } return false; @@ -2592,9 +2607,9 @@ void WiFiManager::stopCaptivePortal(){ } // HTTPD CALLBACK, handle close, stop captive portal, if not enabled undefined -void WiFiManager::handleClose(AsyncWebServerRequest *request){ +esp_err_t WiFiManager::handleClose(PsychicRequest *request){ DEBUG_WM(WM_DEBUG_VERBOSE,F("Disabling Captive Portal")); - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); stopCaptivePortal(); #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- HTTP close")); @@ -2602,7 +2617,7 @@ void WiFiManager::handleClose(AsyncWebServerRequest *request){ handleRequest(); String page = getHTTPHead(FPSTR(S_titleclose)); // @token titleclose page += FPSTR(S_closing); // @token closing - HTTPSend(request,page); + return HTTPSend(request,page); } void WiFiManager::reportStatus(String &page){ @@ -4011,12 +4026,12 @@ void WiFiManager::WiFi_autoReconnect(){ } // Called when /update is requested -void WiFiManager::handleUpdate(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleUpdate(PsychicRequest *request) { #ifdef WM_DEBUG_LEVEL DEBUG_WM(WM_DEBUG_VERBOSE,F("<- Handle update")); #endif - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - if (captivePortal(request)) return; // If captive portal redirect instead of displaying the page + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + if (captivePortal(request)) return 0; // If captive portal redirect instead of displaying the page String page = getHTTPHead(_title); // @token options String str = FPSTR(HTTP_ROOT_MAIN); str.replace(FPSTR(T_t), _title); @@ -4026,12 +4041,12 @@ void WiFiManager::handleUpdate(AsyncWebServerRequest *request) { page += FPSTR(HTTP_UPDATE); page += FPSTR(HTTP_END); - HTTPSend(request,page); + return HTTPSend(request,page); } // upload via /u POST -void WiFiManager::handleUpdating(AsyncWebServerRequest *request,String filename, size_t index, uint8_t *data, size_t len, bool final){ +void WiFiManager::handleUpdating(String filename, size_t index, uint8_t *data, size_t len, bool final){ // @todo // cannot upload files in captive portal, file select is not allowed, show message with link or hide // cannot upload if softreset after upload, maybe check for hard reset at least for dev, ERROR[11]: Invalid bootstrapping state, reset ESP8266 before updating @@ -4123,10 +4138,10 @@ void WiFiManager::handleUpdating(AsyncWebServerRequest *request,String filename, } // upload and ota done, show status -void WiFiManager::handleUpdateDone(AsyncWebServerRequest *request) { +esp_err_t WiFiManager::handleUpdateDone(PsychicRequest *request) { DEBUG_WM(WM_DEBUG_VERBOSE, F("<- Handle update done")); // if (captivePortal(request)) return; // If captive portal redirect instead of displaying the page - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); String page = getHTTPHead(FPSTR(S_options)); // @token options String str = FPSTR(HTTP_ROOT_MAIN); str.replace(FPSTR(T_t),_title); @@ -4148,13 +4163,13 @@ void WiFiManager::handleUpdateDone(AsyncWebServerRequest *request) { } page += FPSTR(HTTP_END); - HTTPSend(request,page); - // delay(1000); // send page if (!Update.hasError()) { //ESP.restart(); _rebootNeeded = true; } + + return HTTPSend(request,page); } #endif diff --git a/lib/WiFiManager/WiFiManager.h b/lib/WiFiManager/WiFiManager.h index 9f0eac5..38f9bfb 100644 --- a/lib/WiFiManager/WiFiManager.h +++ b/lib/WiFiManager/WiFiManager.h @@ -97,8 +97,8 @@ #define WM_WIFIOPEN WIFI_AUTH_OPEN #ifdef WM_ASYNCWEBSERVER - #include - #include + #include + #include #else #ifndef WEBSERVER_H #ifdef WM_WEBSERVERSHIM @@ -521,7 +521,7 @@ class WiFiManager #if defined(ESP32) && defined(WM_WEBSERVERSHIM) #ifdef WM_ASYNCWEBSERVER - using WM_WebServer = AsyncWebServer; + using WM_WebServer = PsychicHttpServer; #else using WM_WebServer = WebServer; #endif @@ -694,32 +694,32 @@ public: bool WiFi_scanNetworks(bool force,bool async); protected: // webserver handlers - void handleRoot(AsyncWebServerRequest *request); - void handleWifi(AsyncWebServerRequest *request,bool scan); - void handleWifiSave(AsyncWebServerRequest *request); - void handleInfo(AsyncWebServerRequest *request); - void handleReset(AsyncWebServerRequest *request); - void handleNotFound(AsyncWebServerRequest *request); - void handleExit(AsyncWebServerRequest *request); - void handleClose(AsyncWebServerRequest *request); - // void handleErase(AsyncWebServerRequest *request); - void handleErase(AsyncWebServerRequest *request, bool opt); - void handleParam(AsyncWebServerRequest *request); - void handleWiFiStatus(AsyncWebServerRequest *request); - void handleParamSave(AsyncWebServerRequest *request); - void doParamSave(AsyncWebServerRequest *request); + esp_err_t handleRoot(PsychicRequest *request); + esp_err_t handleWifi(PsychicRequest *request,bool scan); + esp_err_t handleWifiSave(PsychicRequest *request); + esp_err_t handleInfo(PsychicRequest *request); + esp_err_t handleReset(PsychicRequest *request); + esp_err_t handleNotFound(PsychicRequest *request); + esp_err_t handleExit(PsychicRequest *request); + esp_err_t handleClose(PsychicRequest *request); + // esp_err_t handleErase(PsychicRequest *request); + esp_err_t handleErase(PsychicRequest *request, bool opt); + esp_err_t handleParam(PsychicRequest *request); + esp_err_t handleWiFiStatus(PsychicRequest *request); + esp_err_t handleParamSave(PsychicRequest *request); + void doParamSave(PsychicRequest *request); void handleRequest(); - void HTTPSend(AsyncWebServerRequest *request, String page); + esp_err_t HTTPSend(PsychicRequest *request, String page); - boolean captivePortal(AsyncWebServerRequest *request); + boolean captivePortal(PsychicRequest *request); boolean configPortalHasTimeout(); uint8_t processConfigPortal(); void stopCaptivePortal(); // OTA Update handler - void handleUpdate(AsyncWebServerRequest *request); - void handleUpdating(AsyncWebServerRequest *request,String filename, size_t index, uint8_t *data, size_t len, bool final); - void handleUpdateDone(AsyncWebServerRequest *request); + esp_err_t handleUpdate(PsychicRequest *request); + void handleUpdating(String filename, size_t index, uint8_t *data, size_t len, bool final); + esp_err_t handleUpdateDone(PsychicRequest *request); // wifi platform abstractions bool WiFi_Mode(WiFiMode_t m); diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 780d167..89c8474 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -82,4 +82,13 @@ CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE=y CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y -CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="resources/github_root_ca.pem" \ No newline at end of file +CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="resources/github_root_ca.pem" +CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 +CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_ERR_RESP_NO_DELAY=y +CONFIG_HTTPD_PURGE_BUF_LEN=32 +CONFIG_HTTPD_WS_SUPPORT=y +CONFIG_ESP_HTTPS_SERVER_ENABLE=y \ No newline at end of file diff --git a/src/Config.h b/src/Config.h index d621c64..3bd746c 100644 --- a/src/Config.h +++ b/src/Config.h @@ -4,7 +4,7 @@ #define NUKI_HUB_VERSION "9.01" #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2024-08-18" +#define NUKI_HUB_DATE "2024-08-26" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index 65d2921..afc5ca8 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -16,8 +16,6 @@ #include "NukiConstants.h" #endif -#define JSON_BUFFER_SIZE 1024 - class NukiNetwork { public: diff --git a/src/NukiNetworkLock.h b/src/NukiNetworkLock.h index 21f921c..d069ccf 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -13,8 +13,6 @@ #include "QueryCommand.h" #include "LockActionResult.h" -#define LOCK_LOG_JSON_BUFFER_SIZE 2048 - class NukiNetworkLock : public MqttReceiver { public: diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index a595508..a6dc65d 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -17,7 +17,7 @@ extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_ #include #include "ArduinoJson.h" -WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, AsyncWebServer* asyncServer) +WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer) : _nuki(nuki), _nukiOpener(nukiOpener), _network(network), @@ -25,14 +25,14 @@ WebCfgServer::WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, Nuk _preferences(preferences), _allowRestartToPortal(allowRestartToPortal), _partitionType(partitionType), - _asyncServer(asyncServer) + _psychicServer(psychicServer) #else -WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, AsyncWebServer* asyncServer) +WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer) : _network(network), _preferences(preferences), _allowRestartToPortal(allowRestartToPortal), _partitionType(partitionType), - _asyncServer(asyncServer) + _psychicServer(psychicServer) #endif { _hostname = _preferences->getString(preference_hostname, ""); @@ -72,206 +72,201 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, Preferences* preferences, bool void WebCfgServer::initialize() { - _response.reserve(8192); - - _asyncServer->on("/", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); #ifndef NUKI_HUB_UPDATER - buildHtml(request); + return buildHtml(request); #else - buildOtaHtml(request); + return buildOtaHtml(request); #endif }); - _asyncServer->on("/style.css", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - sendCss(request); + _psychicServer->on("/style.css", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return sendCss(request); }); - _asyncServer->on("/favicon.ico", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - sendFavicon(request); + _psychicServer->on("/favicon.ico", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return sendFavicon(request); }); #ifndef NUKI_HUB_UPDATER - _asyncServer->on("/import", HTTP_POST, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/import", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); String message = ""; bool restart = processImport(request, message); - buildConfirmHtml(request, message, 3, true); + return buildConfirmHtml(request, message, 3, true); }); - _asyncServer->on("/export", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - sendSettings(request); + _psychicServer->on("/export", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return sendSettings(request); }); - _asyncServer->on("/impexpcfg", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildImportExportHtml(request); + _psychicServer->on("/impexpcfg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildImportExportHtml(request); }); - _asyncServer->on("/status", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildStatusHtml(request); + _psychicServer->on("/status", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildStatusHtml(request); }); - _asyncServer->on("/acclvl", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildAccLvlHtml(request); + _psychicServer->on("/acclvl", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildAccLvlHtml(request); }); - _asyncServer->on("/custntw", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildCustomNetworkConfigHtml(request); + _psychicServer->on("/custntw", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildCustomNetworkConfigHtml(request); }); - _asyncServer->on("/advanced", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildAdvancedConfigHtml(request); + _psychicServer->on("/advanced", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildAdvancedConfigHtml(request); }); - _asyncServer->on("/cred", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildCredHtml(request); + _psychicServer->on("/cred", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildCredHtml(request); }); - _asyncServer->on("/mqttconfig", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildMqttConfigHtml(request); + _psychicServer->on("/mqttconfig", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildMqttConfigHtml(request); }); - _asyncServer->on("/nukicfg", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildNukiConfigHtml(request); + _psychicServer->on("/nukicfg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildNukiConfigHtml(request); }); - _asyncServer->on("/gpiocfg", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildGpioConfigHtml(request); + _psychicServer->on("/gpiocfg", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildGpioConfigHtml(request); }); #ifndef CONFIG_IDF_TARGET_ESP32H2 - _asyncServer->on("/wifi", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildConfigureWifiHtml(request); + _psychicServer->on("/wifi", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildConfigureWifiHtml(request); }); - _asyncServer->on("/wifimanager", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/wifimanager", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); if(_allowRestartToPortal) { - buildConfirmHtml(request, "Restarting. Connect to ESP access point to reconfigure Wi-Fi.", 0); + esp_err_t res = buildConfirmHtml(request, "Restarting. Connect to ESP access point to reconfigure Wi-Fi.", 0); waitAndProcess(false, 1000); _network->reconfigureDevice(); + return res; } }); #endif - _asyncServer->on("/unpairlock", HTTP_POST, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - processUnpair(request, false); + _psychicServer->on("/unpairlock", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return processUnpair(request, false); }); - _asyncServer->on("/unpairopener", HTTP_POST, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - processUnpair(request, true); + _psychicServer->on("/unpairopener", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return processUnpair(request, true); }); - _asyncServer->on("/factoryreset", HTTP_POST, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - processFactoryReset(request); + _psychicServer->on("/factoryreset", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return processFactoryReset(request); }); - _asyncServer->on("/info", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildInfoHtml(request); + _psychicServer->on("/info", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildInfoHtml(request); }); - _asyncServer->on("/debugon", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/debugon", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); _preferences->putBool(preference_publish_debug_info, true); - buildConfirmHtml(request, "Debug On", 3, true); + return buildConfirmHtml(request, "Debug On", 3, true); }); - _asyncServer->on("/debugoff", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/debugoff", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); _preferences->putBool(preference_publish_debug_info, false); - buildConfirmHtml(request, "Debug Off", 3, true); + return buildConfirmHtml(request, "Debug Off", 3, true); }); - _asyncServer->on("/savecfg", HTTP_POST, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/savecfg", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); String message = ""; bool restart = processArgs(request, message); - buildConfirmHtml(request, message, 3, true); + return buildConfirmHtml(request, message, 3, true); }); - _asyncServer->on("/savegpiocfg", HTTP_POST, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/savegpiocfg", HTTP_POST, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); processGpioArgs(request); - buildConfirmHtml(request, "Saving GPIO configuration. Restarting.", 3, true); + esp_err_t res = buildConfirmHtml(request, "Saving GPIO configuration. Restarting.", 3, true); Log->println(F("Restarting")); waitAndProcess(true, 1000); restartEsp(RestartReason::GpioConfigurationUpdated); + return res; }); #endif - _asyncServer->on("/ota", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildOtaHtml(request); + _psychicServer->on("/ota", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildOtaHtml(request); }); - _asyncServer->on("/otadebug", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildOtaHtml(request, true); + _psychicServer->on("/otadebug", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return buildOtaHtml(request, true); }); - _asyncServer->on("/reboottoota", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildConfirmHtml(request, "Rebooting to other partition", 2, true); + _psychicServer->on("/reboottoota", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + esp_err_t res = buildConfirmHtml(request, "Rebooting to other partition", 2, true); waitAndProcess(true, 1000); esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL)); restartEsp(RestartReason::OTAReboot); + return res; }); - _asyncServer->on("/reboot", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildConfirmHtml(request, "Rebooting", 2, true); + _psychicServer->on("/reboot", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + esp_err_t res = buildConfirmHtml(request, "Rebooting", 2, true); waitAndProcess(true, 1000); restartEsp(RestartReason::RequestedViaWebServer); + return res; }); - _asyncServer->on("/autoupdate", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); + _psychicServer->on("/autoupdate", HTTP_GET, [&](PsychicRequest *request){ + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); #ifndef NUKI_HUB_UPDATER - processUpdate(request); + return processUpdate(request); #else - request->redirect("/"); + return request->redirect("/"); #endif }); - _asyncServer->on("/uploadota", HTTP_POST, - [&](AsyncWebServerRequest *request) {}, - [&](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) + + /* + _psychicServer->on("/uploadota", HTTP_POST, + [&](PsychicRequest *request) {}, + [&](PsychicRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - handleOtaUpload(request, filename, index, data, len, final); + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(BASIC_AUTH, "Nuki Hub", "You must log in."); + return handleOtaUpload(request, filename, index, data, len, final); } ); + */ //Update.onProgress(printProgress); } -void WebCfgServer::sendResponse(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildOtaHtml(PsychicRequest *request, bool debug) { - AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", - [&](uint8_t *buffer, size_t maxlen, size_t index) -> size_t { - size_t len = min(maxlen, _response.length() - index); - memcpy(buffer, _response.c_str() + index, len); - return len; - }); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); - request->send(response); -} - -void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) -{ - _response = ""; - buildHtmlHeader(); + buildHtmlHeader(&response); bool errored = false; if(request->hasParam("errored")) { - const AsyncWebParameter* p = request->getParam("errored"); + const PsychicWebParameter* p = request->getParam("errored"); if(p->value() != "") errored = true; } - if(errored) _response.concat("
Over-the-air update errored. Please check the logs for more info

"); + if(errored) response.print("
Over-the-air update errored. Please check the logs for more info

"); if(_partitionType == 0) { - _response.concat("

You are currently running Nuki Hub with an outdated partition scheme. Because of this you cannot use OTA to update to 9.00 or higher. Please check GitHub for instructions on how to update to 9.00 and the new partition scheme

"); - _response.concat(""); - return; + response.print("

You are currently running Nuki Hub with an outdated partition scheme. Because of this you cannot use OTA to update to 9.00 or higher. Please check GitHub for instructions on how to update to 9.00 and the new partition scheme

"); + response.print(""); + return response.endSend(); } - _response.concat("
Initiating Over-the-air update. This will take about two minutes, please be patient.
You will be forwarded automatically when the update is complete.
"); - _response.concat("

Update Nuki Hub

"); - _response.concat("Click on the button to reboot and automatically update Nuki Hub and the Nuki Hub updater to the latest versions from GitHub"); - _response.concat("
"); + response.print("
Initiating Over-the-air update. This will take about two minutes, please be patient.
You will be forwarded automatically when the update is complete.
"); + response.print("

Update Nuki Hub

"); + response.print("Click on the button to reboot and automatically update Nuki Hub and the Nuki Hub updater to the latest versions from GitHub"); + response.print("
"); String release_type; @@ -283,18 +278,18 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) #else String build_type = "debug"; #endif - _response.concat("

"); - _response.concat("

"); - _response.concat("

"); - _response.concat("

"); + response.print("

"); + response.print("

"); + response.print("

"); + response.print("

"); - _response.concat("Current version: "); - _response.concat(NUKI_HUB_VERSION); - _response.concat(" ("); - _response.concat(NUKI_HUB_BUILD); - _response.concat("), "); - _response.concat(NUKI_HUB_DATE); - _response.concat("
"); + response.print("Current version: "); + response.print(NUKI_HUB_VERSION); + response.print(" ("); + response.print(NUKI_HUB_BUILD); + response.print("), "); + response.print(NUKI_HUB_DATE); + response.print("
"); #ifndef NUKI_HUB_UPDATER bool manifestSuccess = false; @@ -325,39 +320,39 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) if(!manifestSuccess) { - _response.concat("currentverlatestverdevverbetaver"); + response.print("currentverlatestverdevverbetaver"); } else { - _response.concat("Latest release version: "); - _response.concat(doc["release"]["fullversion"].as()); - _response.concat(" ("); - _response.concat(doc["release"]["build"].as()); - _response.concat("), "); - _response.concat(doc["release"]["time"].as()); - _response.concat("
"); - _response.concat("Latest beta version: "); + response.print("Latest release version: "); + response.print(doc["release"]["fullversion"].as()); + response.print(" ("); + response.print(doc["release"]["build"].as()); + response.print("), "); + response.print(doc["release"]["time"].as()); + response.print("
"); + response.print("Latest beta version: "); if(doc["beta"]["fullversion"] != "No beta available") { - _response.concat(doc["beta"]["fullversion"].as()); - _response.concat(" ("); - _response.concat(doc["beta"]["build"].as()); - _response.concat("), "); - _response.concat(doc["beta"]["time"].as()); + response.print(doc["beta"]["fullversion"].as()); + response.print(" ("); + response.print(doc["beta"]["build"].as()); + response.print(")
, "); + response.print(doc["beta"]["time"].as()); } else { - _response.concat(doc["beta"]["fullversion"].as()); - _response.concat(""); + response.print(doc["beta"]["fullversion"].as()); + response.print(""); } - _response.concat("
"); - _response.concat("Latest development version: "); - _response.concat(doc["master"]["fullversion"].as()); - _response.concat(" ("); - _response.concat(doc["master"]["build"].as()); - _response.concat("), "); - _response.concat(doc["master"]["time"].as()); - _response.concat("
"); + response.print("
"); + response.print("Latest development version: "); + response.print(doc["master"]["fullversion"].as()); + response.print(" ("); + response.print(doc["master"]["build"].as()); + response.print("), "); + response.print(doc["master"]["time"].as()); + response.print("
"); String currentVersion = NUKI_HUB_VERSION; const char* latestVersion; @@ -370,87 +365,88 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) if(strcmp(latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, latestVersion); } #endif - _response.concat("
"); + response.print("
"); if(_partitionType == 1) { - _response.concat("

Manually update Nuki Hub

"); - _response.concat("

Reboot to Nuki Hub Updater

"); - _response.concat("Click on the button to reboot to the Nuki Hub updater, where you can select the latest Nuki Hub binary to update"); - _response.concat("



"); - _response.concat("

Update Nuki Hub Updater

"); - _response.concat("Select the latest Nuki Hub updater binary to update the Nuki Hub updater"); - _response.concat("
Choose the nuki_hub_updater.bin file to upload:
"); + response.print("

Manually update Nuki Hub

"); + response.print("

Reboot to Nuki Hub Updater

"); + response.print("Click on the button to reboot to the Nuki Hub updater, where you can select the latest Nuki Hub binary to update"); + response.print("


"); + response.print("

Update Nuki Hub Updater

"); + response.print("Select the latest Nuki Hub updater binary to update the Nuki Hub updater"); + response.print("
Choose the nuki_hub_updater.bin file to upload:
"); } else { - _response.concat("
"); - _response.concat("

Reboot to Nuki Hub

"); - _response.concat("Click on the button to reboot to Nuki Hub"); - _response.concat("


"); - _response.concat("

Update Nuki Hub

"); - _response.concat("Select the latest Nuki Hub binary to update Nuki Hub"); - _response.concat("
Choose the nuki_hub.bin file to upload:
"); + response.print("
"); + response.print("

Reboot to Nuki Hub

"); + response.print("Click on the button to reboot to Nuki Hub"); + response.print("


"); + response.print("

Update Nuki Hub

"); + response.print("Select the latest Nuki Hub binary to update Nuki Hub"); + response.print("
Choose the nuki_hub.bin file to upload:
"); } - _response.concat("


"); - _response.concat("
"); - _response.concat("

GitHub


"); - _response.concat(""); - _response.concat("

"); - _response.concat("

"); - _response.concat(""); - _response.concat(""); - sendResponse(request); + response.print("


"); + response.print("
"); + response.print("

GitHub


"); + response.print(""); + response.print("

"); + response.print("

"); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildOtaCompletedHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildOtaCompletedHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); - _response.concat("
Over-the-air update completed.
You will be forwarded automatically.
"); - _response.concat(""); - _response.concat(""); - sendResponse(request); + response.print("
Over-the-air update completed.
You will be forwarded automatically.
"); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildHtmlHeader(String additionalHeader) +void WebCfgServer::buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader) { - _response.concat(""); - _response.concat(""); - if(strcmp(additionalHeader.c_str(), "") != 0) _response.concat(additionalHeader); - _response.concat(""); - _response.concat("Nuki Hub"); + response->print(""); + response->print(""); + if(strcmp(additionalHeader.c_str(), "") != 0) response->print(additionalHeader); + response->print(""); + response->print("Nuki Hub"); } void WebCfgServer::waitAndProcess(const bool blocking, const uint32_t duration) @@ -473,7 +469,7 @@ void WebCfgServer::printProgress(size_t prg, size_t sz) { Log->printf("Progress: %d%%\n", (prg*100)/_otaContentLen); } -void WebCfgServer::handleOtaUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) +void WebCfgServer::handleOtaUpload(PsychicRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { if(!request->url().endsWith("/uploadota")) return; @@ -535,6 +531,7 @@ void WebCfgServer::handleOtaUpload(AsyncWebServerRequest *request, String filena restartEsp(RestartReason::OTAAborted); } + /* if (final) { AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots"); response->addHeader("Refresh", "20"); @@ -553,11 +550,13 @@ void WebCfgServer::handleOtaUpload(AsyncWebServerRequest *request, String filena restartEsp(RestartReason::OTACompleted); } } + */ } -void WebCfgServer::buildConfirmHtml(AsyncWebServerRequest *request, const String &message, uint32_t redirectDelay, bool redirect) +esp_err_t WebCfgServer::buildConfirmHtml(PsychicRequest *request, const String &message, uint32_t redirectDelay, bool redirect) { - _response = ""; + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); String header; if(!redirect) @@ -570,25 +569,31 @@ void WebCfgServer::buildConfirmHtml(AsyncWebServerRequest *request, const String String delay(redirectDelay * 1000); header = ""; } - buildHtmlHeader(header); - _response.concat(message); - _response.concat(""); - sendResponse(request); + buildHtmlHeader(&response, header); + response.print(message); + response.print(""); + return response.endSend(); } -void WebCfgServer::sendCss(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::sendCss(PsychicRequest *request) { // escaped by https://www.cescaper.com/ - AsyncWebServerResponse *asyncResponse = request->beginResponse(200, "text/css", (const uint8_t*)stylecss, sizeof(stylecss)); - asyncResponse ->addHeader("Cache-Control", "public, max-age=3600"); - request->send(asyncResponse); + PsychicResponse response(request); + response.addHeader("Cache-Control", "public, max-age=3600"); + response.setCode(200); + response.setContentType("text/css"); + response.setContent(stylecss); + return response.send(); } -void WebCfgServer::sendFavicon(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::sendFavicon(PsychicRequest *request) { - AsyncWebServerResponse *asyncResponse = request->beginResponse(200, "image/png", (const uint8_t*)favicon_32x32, sizeof(favicon_32x32)); - asyncResponse->addHeader("Cache-Control", "public, max-age=604800"); - request->send(asyncResponse); + PsychicResponse response(request); + response.addHeader("Cache-Control", "public, max-age=604800"); + response.setCode(200); + response.setContentType("image/png"); + response.setContent((const char*)favicon_32x32); + return response.send(); } String WebCfgServer::generateConfirmCode() @@ -598,19 +603,19 @@ String WebCfgServer::generateConfirmCode() } #ifndef NUKI_HUB_UPDATER -void WebCfgServer::sendSettings(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::sendSettings(PsychicRequest *request) { bool redacted = false; bool pairing = false; if(request->hasParam("redacted")) { - const AsyncWebParameter* p = request->getParam("redacted"); + const PsychicWebParameter* p = request->getParam("redacted"); if(p->value() == "1") redacted = true; } if(request->hasParam("pairing")) { - const AsyncWebParameter* p = request->getParam("pairing"); + const PsychicWebParameter* p = request->getParam("pairing"); if(p->value() == "1") pairing = true; } @@ -769,17 +774,10 @@ void WebCfgServer::sendSettings(AsyncWebServerRequest *request) serializeJsonPretty(json, jsonPretty); - AsyncWebServerResponse *response = request->beginChunkedResponse("application/json", - [&](uint8_t *buffer, size_t maxlen, size_t index) -> size_t { - size_t len = min(maxlen, jsonPretty.length() - index); - memcpy(buffer, jsonPretty.c_str() + index, len); - return len; - }); - - request->send(response); + return request->reply(200, "application/json", jsonPretty.c_str()); } -bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) +bool WebCfgServer::processArgs(PsychicRequest *request, String& message) { bool configChanged = false; bool aclLvlChanged = false; @@ -809,13 +807,13 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) for(int index = 0; index < params; index++) { - const AsyncWebParameter* p = request->getParam(index); + const PsychicWebParameter* p = request->getParam(index); String key = p->name(); String value = p->value(); if(index < params -1) { - const AsyncWebParameter* next = request->getParam(index+1); + const PsychicWebParameter* next = request->getParam(index+1); if(key == next->name()) continue; } @@ -2323,7 +2321,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) return configChanged; } -bool WebCfgServer::processImport(AsyncWebServerRequest *request, String& message) +bool WebCfgServer::processImport(PsychicRequest *request, String& message) { bool configChanged = false; unsigned char currentBleAddress[6]; @@ -2337,7 +2335,7 @@ bool WebCfgServer::processImport(AsyncWebServerRequest *request, String& message for(int index = 0; index < params; index++) { - const AsyncWebParameter* p = request->getParam(index); + const PsychicWebParameter* p = request->getParam(index); if(p->name() == "importjson") { JsonDocument doc; @@ -2481,14 +2479,14 @@ bool WebCfgServer::processImport(AsyncWebServerRequest *request, String& message return configChanged; } -void WebCfgServer::processGpioArgs(AsyncWebServerRequest *request) +void WebCfgServer::processGpioArgs(PsychicRequest *request) { int params = request->params(); std::vector pinConfiguration; for(int index = 0; index < params; index++) { - const AsyncWebParameter* p = request->getParam(index); + const PsychicWebParameter* p = request->getParam(index); PinRole role = (PinRole)p->value().toInt(); if(role != PinRole::Disabled) { @@ -2502,87 +2500,90 @@ void WebCfgServer::processGpioArgs(AsyncWebServerRequest *request) _gpio->savePinConfiguration(pinConfiguration); } -void WebCfgServer::buildImportExportHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildImportExportHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - - _response.concat("

Import configuration

"); - _response.concat("

"); - _response.concat("


"); - _response.concat("
"); - _response.concat("

Export configuration


"); - _response.concat(""); - _response.concat("

"); - _response.concat("

"); - _response.concat("
"); - sendResponse(request); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print("

Import configuration

"); + response.print("

"); + response.print("


"); + response.print("
"); + response.print("

Export configuration


"); + response.print(""); + response.print("

"); + response.print("

"); + response.print("
"); + return response.endSend(); } -void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildCustomNetworkConfigHtml(PsychicRequest *request) { String header = ""; - _response = ""; - buildHtmlHeader(header); - _response.concat("
"); - _response.concat("

Custom Ethernet Configuration

"); - _response.concat(""); - printDropDown("NWCUSTPHY", "PHY", String(_preferences->getInt(preference_network_custom_phy)), getNetworkCustomPHYOptions(), ""); - printInputField("NWCUSTADDR", "ADDR", _preferences->getInt(preference_network_custom_addr, 1), 6, ""); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response, header); + response.print(""); + response.print("

Custom Ethernet Configuration

"); + response.print("
"); + printDropDown(&response, "NWCUSTPHY", "PHY", String(_preferences->getInt(preference_network_custom_phy)), getNetworkCustomPHYOptions(), ""); + printInputField(&response, "NWCUSTADDR", "ADDR", _preferences->getInt(preference_network_custom_addr, 1), 6, ""); #if defined(CONFIG_IDF_TARGET_ESP32) - printDropDown("NWCUSTCLK", "CLK", String(_preferences->getInt(preference_network_custom_clk, 0)), getNetworkCustomCLKOptions(), "internalopt"); - printInputField("NWCUSTPWR", "PWR", _preferences->getInt(preference_network_custom_pwr, 12), 6, "class=\"internalopt\""); - printInputField("NWCUSTMDIO", "MDIO", _preferences->getInt(preference_network_custom_mdio), 6, "class=\"internalopt\""); - printInputField("NWCUSTMDC", "MDC", _preferences->getInt(preference_network_custom_mdc), 6, "class=\"internalopt\""); + printDropDown(&response, "NWCUSTCLK", "CLK", String(_preferences->getInt(preference_network_custom_clk, 0)), getNetworkCustomCLKOptions(), "internalopt"); + printInputField(&response, "NWCUSTPWR", "PWR", _preferences->getInt(preference_network_custom_pwr, 12), 6, "class=\"internalopt\""); + printInputField(&response, "NWCUSTMDIO", "MDIO", _preferences->getInt(preference_network_custom_mdio), 6, "class=\"internalopt\""); + printInputField(&response, "NWCUSTMDC", "MDC", _preferences->getInt(preference_network_custom_mdc), 6, "class=\"internalopt\""); #endif - printInputField("NWCUSTIRQ", "IRQ", _preferences->getInt(preference_network_custom_irq, -1), 6, "class=\"externalopt\""); - printInputField("NWCUSTRST", "RST", _preferences->getInt(preference_network_custom_rst, -1), 6, "class=\"externalopt\""); - printInputField("NWCUSTCS", "CS", _preferences->getInt(preference_network_custom_cs, -1), 6, "class=\"externalopt\""); - printInputField("NWCUSTSCK", "SCK", _preferences->getInt(preference_network_custom_sck, -1), 6, "class=\"externalopt\""); - printInputField("NWCUSTMISO", "MISO", _preferences->getInt(preference_network_custom_miso, -1), 6, "class=\"externalopt\""); - printInputField("NWCUSTMOSI", "MOSI", _preferences->getInt(preference_network_custom_mosi, -1), 6, "class=\"externalopt\""); - - _response.concat("
"); - - _response.concat("
"); - _response.concat("
"); - _response.concat(""); - sendResponse(request); + printInputField(&response, "NWCUSTIRQ", "IRQ", _preferences->getInt(preference_network_custom_irq, -1), 6, "class=\"externalopt\""); + printInputField(&response, "NWCUSTRST", "RST", _preferences->getInt(preference_network_custom_rst, -1), 6, "class=\"externalopt\""); + printInputField(&response, "NWCUSTCS", "CS", _preferences->getInt(preference_network_custom_cs, -1), 6, "class=\"externalopt\""); + printInputField(&response, "NWCUSTSCK", "SCK", _preferences->getInt(preference_network_custom_sck, -1), 6, "class=\"externalopt\""); + printInputField(&response, "NWCUSTMISO", "MISO", _preferences->getInt(preference_network_custom_miso, -1), 6, "class=\"externalopt\""); + printInputField(&response, "NWCUSTMOSI", "MOSI", _preferences->getInt(preference_network_custom_mosi, -1), 6, "class=\"externalopt\""); + response.print(""); + response.print("
"); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildHtml(PsychicRequest *request) { String header = ""; - _response = ""; - buildHtmlHeader(header); - - if(_rebootRequired) _response.concat("
REBOOT REQUIRED TO APPLY SETTINGS
"); - if(_preferences->getBool(preference_webserial_enabled, false)) _response.concat("
WEBSERIAL IS ENABLED, ONLY ENABLE WHEN DEBUGGING AND DISABLE ASAP
"); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response, header); + if(_rebootRequired) + { + response.print("
REBOOT REQUIRED TO APPLY SETTINGS
"); + } + if(_preferences->getBool(preference_webserial_enabled, false)) + { + response.print("
WEBSERIAL IS ENABLED, ONLY ENABLE WHEN DEBUGGING AND DISABLE ASAP
"); + } #ifdef DEBUG_NUKIHUB - _response.concat("
RUNNING DEBUG BUILD, SWITCH TO RELEASE BUILD ASAP
"); + response.print("
RUNNING DEBUG BUILD, SWITCH TO RELEASE BUILD ASAP
"); #endif - - _response.concat("

Info


"); - _response.concat(""); - - printParameter("Hostname", _hostname.c_str(), "", "hostname"); - printParameter("MQTT Connected", _network->mqttConnectionState() > 0 ? "Yes" : "No", "", "mqttState"); + response.print("

Info


"); + response.print("
"); + printParameter(&response, "Hostname", _hostname.c_str(), "", "hostname"); + printParameter(&response, "MQTT Connected", _network->mqttConnectionState() > 0 ? "Yes" : "No", "", "mqttState"); if(_nuki != nullptr) { char lockStateArr[20]; NukiLock::lockstateToString(_nuki->keyTurnerState().lockState, lockStateArr); - printParameter("Nuki Lock paired", _nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No", "", "lockPaired"); - printParameter("Nuki Lock state", lockStateArr, "", "lockState"); + printParameter(&response, "Nuki Lock paired", _nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No", "", "lockPaired"); + printParameter(&response, "Nuki Lock state", lockStateArr, "", "lockState"); if(_nuki->isPaired()) { String lockState = pinStateToString(_preferences->getInt(preference_lock_pin_status, 4)); - printParameter("Nuki Lock PIN status", lockState.c_str(), "", "lockPin"); + printParameter(&response, "Nuki Lock PIN status", lockState.c_str(), "", "lockPin"); if(_preferences->getBool(preference_official_hybrid, false)) { String offConnected = _nuki->offConnected() ? "Yes": "No"; - printParameter("Nuki Lock hybrid mode connected", offConnected.c_str(), "", "lockHybrid"); + printParameter(&response, "Nuki Lock hybrid mode connected", offConnected.c_str(), "", "lockHybrid"); } } } @@ -2590,234 +2591,247 @@ void WebCfgServer::buildHtml(AsyncWebServerRequest *request) { char openerStateArr[20]; NukiOpener::lockstateToString(_nukiOpener->keyTurnerState().lockState, openerStateArr); - printParameter("Nuki Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No", "", "openerPaired"); - - if(_nukiOpener->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode) printParameter("Nuki Opener state", "Open (Continuous Mode)", "", "openerState"); - else printParameter("Nuki Opener state", openerStateArr, "", "openerState"); + printParameter(&response, "Nuki Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No", "", "openerPaired"); + if(_nukiOpener->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode) + { + printParameter(&response, "Nuki Opener state", "Open (Continuous Mode)", "", "openerState"); + } + else + { + printParameter(&response, "Nuki Opener state", openerStateArr, "", "openerState"); + } if(_nukiOpener->isPaired()) { String openerState = pinStateToString(_preferences->getInt(preference_opener_pin_status, 4)); - printParameter("Nuki Opener PIN status", openerState.c_str(), "", "openerPin"); + printParameter(&response, "Nuki Opener PIN status", openerState.c_str(), "", "openerPin"); } } - printParameter("Firmware", NUKI_HUB_VERSION, "/info", "firmware"); - if(_preferences->getBool(preference_check_updates)) printParameter("Latest Firmware", _preferences->getString(preference_latest_version).c_str(), "/ota", "ota"); - _response.concat("

"); - _response.concat("
    "); - buildNavigationMenuEntry("MQTT and Network Configuration", "/mqttconfig", _brokerConfigured ? "" : "Please configure MQTT broker"); - buildNavigationMenuEntry("Nuki Configuration", "/nukicfg"); - buildNavigationMenuEntry("Access Level Configuration", "/acclvl"); - buildNavigationMenuEntry("Credentials", "/cred", _pinsConfigured ? "" : "Please configure PIN"); - buildNavigationMenuEntry("GPIO Configuration", "/gpiocfg"); - buildNavigationMenuEntry("Firmware update", "/ota"); - buildNavigationMenuEntry("Import/Export Configuration", "/impexpcfg"); + printParameter(&response, "Firmware", NUKI_HUB_VERSION, "/info", "firmware"); + if(_preferences->getBool(preference_check_updates)) + { + printParameter(&response, "Latest Firmware", _preferences->getString(preference_latest_version).c_str(), "/ota", "ota"); + } + response.print("
    "); + response.print("
      "); + buildNavigationMenuEntry(&response, "MQTT and Network Configuration", "/mqttconfig", _brokerConfigured ? "" : "Please configure MQTT broker"); + buildNavigationMenuEntry(&response, "Nuki Configuration", "/nukicfg"); + buildNavigationMenuEntry(&response, "Access Level Configuration", "/acclvl"); + buildNavigationMenuEntry(&response, "Credentials", "/cred", _pinsConfigured ? "" : "Please configure PIN"); + buildNavigationMenuEntry(&response, "GPIO Configuration", "/gpiocfg"); + buildNavigationMenuEntry(&response, "Firmware update", "/ota"); + buildNavigationMenuEntry(&response, "Import/Export Configuration", "/impexpcfg"); if(_preferences->getInt(preference_network_hardware, 0) == 11) { - buildNavigationMenuEntry("Custom Ethernet Configuration", "/custntw"); + buildNavigationMenuEntry(&response, "Custom Ethernet Configuration", "/custntw"); } if (_preferences->getBool(preference_publish_debug_info, false)) { - buildNavigationMenuEntry("Advanced Configuration", "/advanced"); + buildNavigationMenuEntry(&response, "Advanced Configuration", "/advanced"); } if(_preferences->getBool(preference_webserial_enabled, false)) { - buildNavigationMenuEntry("Open Webserial", "/webserial"); + buildNavigationMenuEntry(&response, "Open Webserial", "/webserial"); } #ifndef CONFIG_IDF_TARGET_ESP32H2 - if(_allowRestartToPortal) buildNavigationMenuEntry("Configure Wi-Fi", "/wifi"); + if(_allowRestartToPortal) + { + buildNavigationMenuEntry(&response, "Configure Wi-Fi", "/wifi"); + } #endif - buildNavigationMenuEntry("Reboot Nuki Hub", "/reboot"); - _response.concat("
    "); - sendResponse(request); + buildNavigationMenuEntry(&response, "Reboot Nuki Hub", "/reboot"); + response.print("
"); + return response.endSend(); } -void WebCfgServer::buildCredHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildCredHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - _response.concat("
"); - _response.concat("

Credentials

"); - _response.concat(""); - printInputField("CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "", false, true); - printInputField("CREDPASS", "Password", "*", 30, "", true, true); - printInputField("CREDPASSRE", "Retype password", "*", 30, "", true); - _response.concat("
"); - _response.concat("
"); - _response.concat("
"); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print("
"); + response.print("

Credentials

"); + response.print(""); + printInputField(&response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "", false, true); + printInputField(&response, "CREDPASS", "Password", "*", 30, "", true, true); + printInputField(&response, "CREDPASSRE", "Retype password", "*", 30, "", true); + response.print("
"); + response.print("
"); + response.print("
"); if(_nuki != nullptr) { - _response.concat("

"); - _response.concat("

Nuki Lock PIN

"); - _response.concat(""); - printInputField("NUKIPIN", "PIN Code (# to clear)", "*", 20, "", true); - _response.concat("
"); - _response.concat("
"); - _response.concat("
"); + response.print("

"); + response.print("

Nuki Lock PIN

"); + response.print(""); + printInputField(&response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, "", true); + response.print("
"); + response.print("
"); + response.print("
"); } if(_nukiOpener != nullptr) { - _response.concat("

"); - _response.concat("

Nuki Opener PIN

"); - _response.concat(""); - printInputField("NUKIOPPIN", "PIN Code (# to clear)", "*", 20, "", true); - _response.concat("
"); - _response.concat("
"); - _response.concat("
"); + response.print("

"); + response.print("

Nuki Opener PIN

"); + response.print(""); + printInputField(&response, "NUKIOPPIN", "PIN Code (# to clear)", "*", 20, "", true); + response.print("
"); + response.print("
"); + response.print("
"); } if(_nuki != nullptr) { - _response.concat("

Unpair Nuki Lock

"); - _response.concat("
"); - _response.concat(""); + response.print("

Unpair Nuki Lock

"); + response.print(""); + response.print("
"); String message = "Type "; message.concat(_confirmCode); message.concat(" to confirm unpair"); - printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); - _response.concat("
"); - _response.concat("
"); + printInputField(&response, "CONFIRMTOKEN", message.c_str(), "", 10, ""); + response.print(""); + response.print("
"); } if(_nukiOpener != nullptr) { - _response.concat("

Unpair Nuki Opener

"); - _response.concat("
"); - _response.concat(""); + response.print("

Unpair Nuki Opener

"); + response.print(""); + response.print("
"); String message = "Type "; message.concat(_confirmCode); message.concat(" to confirm unpair"); - printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); - _response.concat("
"); - _response.concat("
"); + printInputField(&response, "CONFIRMTOKEN", message.c_str(), "", 10, ""); + response.print(""); + response.print("
"); } - _response.concat("

Factory reset Nuki Hub

"); - _response.concat("

This will reset all settings to default and unpair Nuki Lock and/or Opener."); + response.print("

Factory reset Nuki Hub

"); + response.print("

This will reset all settings to default and unpair Nuki Lock and/or Opener."); #ifndef CONFIG_IDF_TARGET_ESP32H2 - _response.concat("Optionally will also reset WiFi settings and reopen WiFi manager portal."); + response.print("Optionally will also reset WiFi settings and reopen WiFi manager portal."); #endif - _response.concat("

"); - _response.concat("
"); - _response.concat(""); + response.print(""); + response.print(""); + response.print("
"); String message = "Type "; message.concat(_confirmCode); message.concat(" to confirm factory reset"); - printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); + printInputField(&response, "CONFIRMTOKEN", message.c_str(), "", 10, ""); #ifndef CONFIG_IDF_TARGET_ESP32H2 - printCheckBox("WIFI", "Also reset WiFi settings", false, ""); + printCheckBox(&response, "WIFI", "Also reset WiFi settings", false, ""); #endif - _response.concat("
"); - _response.concat("
"); - _response.concat(""); - sendResponse(request); + response.print(""); + response.print("
"); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildMqttConfigHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildMqttConfigHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - _response.concat("
"); - _response.concat("

Basic MQTT and Network Configuration

"); - _response.concat(""); - printInputField("HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100, ""); - printInputField("MQTTSERVER", "MQTT Broker", _preferences->getString(preference_mqtt_broker).c_str(), 100, ""); - printInputField("MQTTPORT", "MQTT Broker port", _preferences->getInt(preference_mqtt_broker_port), 5, ""); - printInputField("MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30, "", false, true); - printInputField("MQTTPASS", "MQTT Password", "*", 30, "", true, true); - _response.concat("

"); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print(""); + response.print("

Basic MQTT and Network Configuration

"); + response.print(""); + printInputField(&response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100, ""); + printInputField(&response, "MQTTSERVER", "MQTT Broker", _preferences->getString(preference_mqtt_broker).c_str(), 100, ""); + printInputField(&response, "MQTTPORT", "MQTT Broker port", _preferences->getInt(preference_mqtt_broker_port), 5, ""); + printInputField(&response, "MQTTUSER", "MQTT User (# to clear)", _preferences->getString(preference_mqtt_user).c_str(), 30, "", false, true); + printInputField(&response, "MQTTPASS", "MQTT Password", "*", 30, "", true, true); + response.print("

"); - _response.concat("

Advanced MQTT and Network Configuration

"); - _response.concat(""); - printInputField("HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30, ""); - printInputField("HASSCUURL", "Home Assistant device configuration URL (empty to use http://LOCALIP; fill when using a reverse proxy for example)", _preferences->getString(preference_mqtt_hass_cu_url).c_str(), 261, ""); - if(_preferences->getBool(preference_opener_enabled, false)) printCheckBox("OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode), ""); - printTextarea("MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, _network->encryptionSupported(), true); - printTextarea("MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, _network->encryptionSupported(), true); - printTextarea("MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, _network->encryptionSupported(), true); - printDropDown("NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions(), ""); + response.print("

Advanced MQTT and Network Configuration

"); + response.print("
"); + printInputField(&response, "HASSDISCOVERY", "Home Assistant discovery topic (empty to disable; usually homeassistant)", _preferences->getString(preference_mqtt_hass_discovery).c_str(), 30, ""); + printInputField(&response, "HASSCUURL", "Home Assistant device configuration URL (empty to use http://LOCALIP; fill when using a reverse proxy for example)", _preferences->getString(preference_mqtt_hass_cu_url).c_str(), 261, ""); + if(_preferences->getBool(preference_opener_enabled, false)) printCheckBox(&response, "OPENERCONT", "Set Nuki Opener Lock/Unlock action in Home Assistant to Continuous mode", _preferences->getBool(preference_opener_continuous_mode), ""); + printTextarea(&response, "MQTTCA", "MQTT SSL CA Certificate (*, optional)", _preferences->getString(preference_mqtt_ca).c_str(), TLS_CA_MAX_SIZE, _network->encryptionSupported(), true); + printTextarea(&response, "MQTTCRT", "MQTT SSL Client Certificate (*, optional)", _preferences->getString(preference_mqtt_crt).c_str(), TLS_CERT_MAX_SIZE, _network->encryptionSupported(), true); + printTextarea(&response, "MQTTKEY", "MQTT SSL Client Key (*, optional)", _preferences->getString(preference_mqtt_key).c_str(), TLS_KEY_MAX_SIZE, _network->encryptionSupported(), true); + printDropDown(&response, "NWHW", "Network hardware", String(_preferences->getInt(preference_network_hardware)), getNetworkDetectionOptions(), ""); #ifndef CONFIG_IDF_TARGET_ESP32H2 - printCheckBox("NWHWWIFIFB", "Disable fallback to Wi-Fi / Wi-Fi config portal", _preferences->getBool(preference_network_wifi_fallback_disabled), ""); - printCheckBox("BESTRSSI", "Connect to AP with the best signal in an environment with multiple APs with the same SSID", _preferences->getBool(preference_find_best_rssi), ""); - printInputField("RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6, ""); + printCheckBox(&response, "NWHWWIFIFB", "Disable fallback to Wi-Fi / Wi-Fi config portal", _preferences->getBool(preference_network_wifi_fallback_disabled), ""); + printCheckBox(&response, "BESTRSSI", "Connect to AP with the best signal in an environment with multiple APs with the same SSID", _preferences->getBool(preference_find_best_rssi), ""); + printInputField(&response, "RSSI", "RSSI Publish interval (seconds; -1 to disable)", _preferences->getInt(preference_rssi_publish_interval), 6, ""); #endif - printInputField("NETTIMEOUT", "MQTT Timeout until restart (seconds; -1 to disable)", _preferences->getInt(preference_network_timeout), 5, ""); - printCheckBox("RSTDISC", "Restart on disconnect", _preferences->getBool(preference_restart_on_disconnect), ""); - printCheckBox("RECNWTMQTTDIS", "Reconnect network on MQTT connection failure", _preferences->getBool(preference_recon_netw_on_mqtt_discon), ""); - printCheckBox("MQTTLOG", "Enable MQTT logging", _preferences->getBool(preference_mqtt_log_enabled), ""); - printCheckBox("CHECKUPDATE", "Check for Firmware Updates every 24h", _preferences->getBool(preference_check_updates), ""); - printCheckBox("UPDATEMQTT", "Allow updating using MQTT", _preferences->getBool(preference_update_from_mqtt), ""); - printCheckBox("DISNONJSON", "Disable some extraneous non-JSON topics", _preferences->getBool(preference_disable_non_json), ""); - printCheckBox("OFFHYBRID", "Enable hybrid official MQTT and Nuki Hub setup", _preferences->getBool(preference_official_hybrid), ""); - printCheckBox("HYBRIDACT", "Enable sending actions through official MQTT", _preferences->getBool(preference_official_hybrid_actions), ""); - printInputField("HYBRIDTIMER", "Time between status updates when official MQTT is offline (seconds)", _preferences->getInt(preference_query_interval_hybrid_lockstate), 5, ""); - // printCheckBox("HYBRIDRETRY", "Retry command sent using official MQTT over BLE if failed", _preferences->getBool(preference_official_hybrid_retry), ""); // NOT IMPLEMENTED (YET?) - _response.concat("
"); - _response.concat("* If no encryption is configured for the MQTT broker, leave empty.

"); + printInputField(&response, "NETTIMEOUT", "MQTT Timeout until restart (seconds; -1 to disable)", _preferences->getInt(preference_network_timeout), 5, ""); + printCheckBox(&response, "RSTDISC", "Restart on disconnect", _preferences->getBool(preference_restart_on_disconnect), ""); + printCheckBox(&response, "RECNWTMQTTDIS", "Reconnect network on MQTT connection failure", _preferences->getBool(preference_recon_netw_on_mqtt_discon), ""); + printCheckBox(&response, "MQTTLOG", "Enable MQTT logging", _preferences->getBool(preference_mqtt_log_enabled), ""); + printCheckBox(&response, "CHECKUPDATE", "Check for Firmware Updates every 24h", _preferences->getBool(preference_check_updates), ""); + printCheckBox(&response, "UPDATEMQTT", "Allow updating using MQTT", _preferences->getBool(preference_update_from_mqtt), ""); + printCheckBox(&response, "DISNONJSON", "Disable some extraneous non-JSON topics", _preferences->getBool(preference_disable_non_json), ""); + printCheckBox(&response, "OFFHYBRID", "Enable hybrid official MQTT and Nuki Hub setup", _preferences->getBool(preference_official_hybrid), ""); + printCheckBox(&response, "HYBRIDACT", "Enable sending actions through official MQTT", _preferences->getBool(preference_official_hybrid_actions), ""); + printInputField(&response, "HYBRIDTIMER", "Time between status updates when official MQTT is offline (seconds)", _preferences->getInt(preference_query_interval_hybrid_lockstate), 5, ""); + // printCheckBox(&response, "HYBRIDRETRY", "Retry command sent using official MQTT over BLE if failed", _preferences->getBool(preference_official_hybrid_retry), ""); // NOT IMPLEMENTED (YET?) + response.print(""); + response.print("* If no encryption is configured for the MQTT broker, leave empty.

"); - _response.concat("

IP Address assignment

"); - _response.concat(""); - printCheckBox("DHCPENA", "Enable DHCP", _preferences->getBool(preference_ip_dhcp_enabled), ""); - printInputField("IPADDR", "Static IP address", _preferences->getString(preference_ip_address).c_str(), 15, ""); - printInputField("IPSUB", "Subnet", _preferences->getString(preference_ip_subnet).c_str(), 15, ""); - printInputField("IPGTW", "Default gateway", _preferences->getString(preference_ip_gateway).c_str(), 15, ""); - printInputField("DNSSRV", "DNS Server", _preferences->getString(preference_ip_dns_server).c_str(), 15, ""); - _response.concat("
"); - _response.concat("
"); - _response.concat("
"); - _response.concat(""); - sendResponse(request); + response.print("

IP Address assignment

"); + response.print(""); + printCheckBox(&response, "DHCPENA", "Enable DHCP", _preferences->getBool(preference_ip_dhcp_enabled), ""); + printInputField(&response, "IPADDR", "Static IP address", _preferences->getString(preference_ip_address).c_str(), 15, ""); + printInputField(&response, "IPSUB", "Subnet", _preferences->getString(preference_ip_subnet).c_str(), 15, ""); + printInputField(&response, "IPGTW", "Default gateway", _preferences->getString(preference_ip_gateway).c_str(), 15, ""); + printInputField(&response, "DNSSRV", "DNS Server", _preferences->getString(preference_ip_dns_server).c_str(), 15, ""); + response.print("
"); + response.print("
"); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildAdvancedConfigHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - _response.concat("
"); - _response.concat("

Advanced Configuration

"); - _response.concat("

Warning: Changing these settings can lead to bootloops that might require you to erase the ESP32 and reflash nukihub using USB/serial

"); - _response.concat(""); - _response.concat(""); - printCheckBox("WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_enabled), ""); - printCheckBox("BTLPRST", "Enable Bootloop prevention (Try to reset these settings to default on bootloop)", true, ""); - printInputField("BUFFSIZE", "Char buffer size (min 4096, max 32768)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6, ""); - _response.concat(""); - printInputField("TSKNTWK", "Task size Network (min 12288, max 32768)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6, ""); - _response.concat(""); - printInputField("TSKNUKI", "Task size Nuki (min 8192, max 32768)", _preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), 6, ""); - printInputField("ALMAX", "Max auth log entries (min 1, max 50)", _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 3, "id=\"inputmaxauthlog\""); - printInputField("KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "id=\"inputmaxkeypad\""); - printInputField("TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "id=\"inputmaxtimecontrol\""); - printInputField("AUTHMAX", "Max authorization entries (min 1, max 50)", _preferences->getInt(preference_auth_max_entries, MAX_AUTH), 3, "id=\"inputmaxauth\""); - printCheckBox("SHOWSECRETS", "Show Pairing secrets on Info page", _preferences->getBool(preference_show_secrets), ""); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print(""); + response.print("

Advanced Configuration

"); + response.print("

Warning: Changing these settings can lead to bootloops that might require you to erase the ESP32 and reflash nukihub using USB/serial

"); + response.print("
Current bootloop prevention state"); - _response.concat(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Enabled" : "Disabled"); - _response.concat("
Advised minimum char buffer size based on current settings
Advised minimum network task size based on current settings
"); + response.print(""); + printCheckBox(&response, "WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_enabled), ""); + printCheckBox(&response, "BTLPRST", "Enable Bootloop prevention (Try to reset these settings to default on bootloop)", true, ""); + printInputField(&response, "BUFFSIZE", "Char buffer size (min 4096, max 32768)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6, ""); + response.print(""); + printInputField(&response, "TSKNTWK", "Task size Network (min 12288, max 32768)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6, ""); + response.print(""); + printInputField(&response, "TSKNUKI", "Task size Nuki (min 8192, max 32768)", _preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), 6, ""); + printInputField(&response, "ALMAX", "Max auth log entries (min 1, max 50)", _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 3, "id=\"inputmaxauthlog\""); + printInputField(&response, "KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "id=\"inputmaxkeypad\""); + printInputField(&response, "TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "id=\"inputmaxtimecontrol\""); + printInputField(&response, "AUTHMAX", "Max authorization entries (min 1, max 50)", _preferences->getInt(preference_auth_max_entries, MAX_AUTH), 3, "id=\"inputmaxauth\""); + printCheckBox(&response, "SHOWSECRETS", "Show Pairing secrets on Info page", _preferences->getBool(preference_show_secrets), ""); if(_preferences->getBool(preference_lock_enabled, true)) { - printCheckBox("LCKMANPAIR", "Manually set lock pairing data (enable to save values below)", false, ""); - printInputField("LCKBLEADDR", "currentBleAddress", "", 12, ""); - printInputField("LCKSECRETK", "secretKeyK", "", 64, ""); - printInputField("LCKAUTHID", "authorizationId", "", 8, ""); + printCheckBox(&response, "LCKMANPAIR", "Manually set lock pairing data (enable to save values below)", false, ""); + printInputField(&response, "LCKBLEADDR", "currentBleAddress", "", 12, ""); + printInputField(&response, "LCKSECRETK", "secretKeyK", "", 64, ""); + printInputField(&response, "LCKAUTHID", "authorizationId", "", 8, ""); } if(_preferences->getBool(preference_opener_enabled, false)) { - printCheckBox("OPNMANPAIR", "Manually set opener pairing data (enable to save values below)", false, ""); - printInputField("OPNBLEADDR", "currentBleAddress", "", 12, ""); - printInputField("OPNSECRETK", "secretKeyK", "", 64, ""); - printInputField("OPNAUTHID", "authorizationId", "", 8, ""); + printCheckBox(&response, "OPNMANPAIR", "Manually set opener pairing data (enable to save values below)", false, ""); + printInputField(&response, "OPNBLEADDR", "currentBleAddress", "", 12, ""); + printInputField(&response, "OPNSECRETK", "secretKeyK", "", 64, ""); + printInputField(&response, "OPNAUTHID", "authorizationId", "", 8, ""); } - printInputField("OTAUPD", "Custom URL to update Nuki Hub updater", "", 255, ""); - printInputField("OTAMAIN", "Custom URL to update Nuki Hub", "", 255, ""); - _response.concat("
Current bootloop prevention state"); + response.print(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Enabled" : "Disabled"); + response.print("
Advised minimum char buffer size based on current settings
Advised minimum network task size based on current settings
"); + printInputField(&response, "OTAUPD", "Custom URL to update Nuki Hub updater", "", 255, ""); + printInputField(&response, "OTAMAIN", "Custom URL to update Nuki Hub", "", 255, ""); + response.print(""); - _response.concat("
"); - _response.concat("
"); - _response.concat(""); - sendResponse(request); + response.print("
"); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildStatusHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildStatusHtml(PsychicRequest *request) { - _response = ""; JsonDocument json; - char _resbuf[2048]; + String jsonStr; bool mqttDone = false; bool lockDone = false; bool openerDone = false; @@ -2878,9 +2892,8 @@ void WebCfgServer::buildStatusHtml(AsyncWebServerRequest *request) if(mqttDone && lockDone && openerDone && latestDone) json["stop"] = 1; - serializeJson(json, _resbuf, sizeof(_resbuf)); - _response.concat(_resbuf); - sendResponse(request); + serializeJson(json, jsonStr); + return request->reply(200, "application/json", jsonStr.c_str()); } String WebCfgServer::pinStateToString(uint8_t value) { @@ -2897,36 +2910,37 @@ String WebCfgServer::pinStateToString(uint8_t value) { } } -void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildAccLvlHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); - _response.concat("
"); - _response.concat(""); - _response.concat("

Nuki General Access Control

"); - _response.concat(""); - printCheckBox("CONFPUB", "Publish Nuki configuration information", _preferences->getBool(preference_conf_info_enabled, true), ""); + response.print(""); + response.print(""); + response.print("

Nuki General Access Control

"); + response.print("
SettingEnabled
"); + printCheckBox(&response, "CONFPUB", "Publish Nuki configuration information", _preferences->getBool(preference_conf_info_enabled, true), ""); if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) { - printCheckBox("KPPUB", "Publish keypad entries information", _preferences->getBool(preference_keypad_info_enabled), ""); - printCheckBox("KPPER", "Publish a topic per keypad entry and create HA sensor", _preferences->getBool(preference_keypad_topic_per_entry), ""); - printCheckBox("KPCODE", "Also publish keypad codes (Disadvised for security reasons)", _preferences->getBool(preference_keypad_publish_code, false), ""); - printCheckBox("KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), ""); + printCheckBox(&response, "KPPUB", "Publish keypad entries information", _preferences->getBool(preference_keypad_info_enabled), ""); + printCheckBox(&response, "KPPER", "Publish a topic per keypad entry and create HA sensor", _preferences->getBool(preference_keypad_topic_per_entry), ""); + printCheckBox(&response, "KPCODE", "Also publish keypad codes (Disadvised for security reasons)", _preferences->getBool(preference_keypad_publish_code, false), ""); + printCheckBox(&response, "KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), ""); } - printCheckBox("TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), ""); - printCheckBox("TCPER", "Publish a topic per time control entry and create HA sensor", _preferences->getBool(preference_timecontrol_topic_per_entry), ""); - printCheckBox("TCENA", "Add, modify and delete time control entries", _preferences->getBool(preference_timecontrol_control_enabled), ""); - printCheckBox("AUTHPUB", "Publish authorization entries information", _preferences->getBool(preference_auth_info_enabled), ""); - printCheckBox("AUTHPER", "Publish a topic per authorization entry and create HA sensor", _preferences->getBool(preference_auth_topic_per_entry), ""); - printCheckBox("AUTHENA", "Modify and delete authorization entries", _preferences->getBool(preference_auth_control_enabled), ""); - printCheckBox("PUBAUTH", "Publish authorization log", _preferences->getBool(preference_publish_authdata), ""); - _response.concat("
SettingEnabled

"); - _response.concat("
"); + printCheckBox(&response, "TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), ""); + printCheckBox(&response, "TCPER", "Publish a topic per time control entry and create HA sensor", _preferences->getBool(preference_timecontrol_topic_per_entry), ""); + printCheckBox(&response, "TCENA", "Add, modify and delete time control entries", _preferences->getBool(preference_timecontrol_control_enabled), ""); + printCheckBox(&response, "AUTHPUB", "Publish authorization entries information", _preferences->getBool(preference_auth_info_enabled), ""); + printCheckBox(&response, "AUTHPER", "Publish a topic per authorization entry and create HA sensor", _preferences->getBool(preference_auth_topic_per_entry), ""); + printCheckBox(&response, "AUTHENA", "Modify and delete authorization entries", _preferences->getBool(preference_auth_control_enabled), ""); + printCheckBox(&response, "PUBAUTH", "Publish authorization log", _preferences->getBool(preference_publish_authdata), ""); + response.print("
"); + response.print("
"); if(_nuki != nullptr) { @@ -2935,72 +2949,72 @@ void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request) uint32_t advancedLockConfigAclPrefs[22]; _preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs)); - _response.concat("

Nuki Lock Access Control

"); - _response.concat(""); - _response.concat(""); - _response.concat(""); + response.print("

Nuki Lock Access Control

"); + response.print(""); + response.print(""); + response.print("
ActionAllowed
"); - printCheckBox("ACLLCKLCK", "Lock", ((int)aclPrefs[0] == 1), "chk_access_lock"); - printCheckBox("ACLLCKUNLCK", "Unlock", ((int)aclPrefs[1] == 1), "chk_access_lock"); - printCheckBox("ACLLCKUNLTCH", "Unlatch", ((int)aclPrefs[2] == 1), "chk_access_lock"); - printCheckBox("ACLLCKLNG", "Lock N Go", ((int)aclPrefs[3] == 1), "chk_access_lock"); - printCheckBox("ACLLCKLNGU", "Lock N Go Unlatch", ((int)aclPrefs[4] == 1), "chk_access_lock"); - printCheckBox("ACLLCKFLLCK", "Full Lock", ((int)aclPrefs[5] == 1), "chk_access_lock"); - printCheckBox("ACLLCKFOB1", "Fob Action 1", ((int)aclPrefs[6] == 1), "chk_access_lock"); - printCheckBox("ACLLCKFOB2", "Fob Action 2", ((int)aclPrefs[7] == 1), "chk_access_lock"); - printCheckBox("ACLLCKFOB3", "Fob Action 3", ((int)aclPrefs[8] == 1), "chk_access_lock"); - _response.concat("
ActionAllowed

"); + printCheckBox(&response, "ACLLCKLCK", "Lock", ((int)aclPrefs[0] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKUNLCK", "Unlock", ((int)aclPrefs[1] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKUNLTCH", "Unlatch", ((int)aclPrefs[2] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKLNG", "Lock N Go", ((int)aclPrefs[3] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKLNGU", "Lock N Go Unlatch", ((int)aclPrefs[4] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKFLLCK", "Full Lock", ((int)aclPrefs[5] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKFOB1", "Fob Action 1", ((int)aclPrefs[6] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKFOB2", "Fob Action 2", ((int)aclPrefs[7] == 1), "chk_access_lock"); + printCheckBox(&response, "ACLLCKFOB3", "Fob Action 3", ((int)aclPrefs[8] == 1), "chk_access_lock"); + response.print("
"); - _response.concat("

Nuki Lock Config Control (Requires PIN to be set)

"); - _response.concat(""); - _response.concat(""); - _response.concat(""); + response.print("

Nuki Lock Config Control (Requires PIN to be set)

"); + response.print(""); + response.print(""); + response.print("
ChangeAllowed
"); - printCheckBox("CONFLCKNAME", "Name", ((int)basicLockConfigAclPrefs[0] == 1), "chk_config_lock"); - printCheckBox("CONFLCKLAT", "Latitude", ((int)basicLockConfigAclPrefs[1] == 1), "chk_config_lock"); - printCheckBox("CONFLCKLONG", "Longitude", ((int)basicLockConfigAclPrefs[2] == 1), "chk_config_lock"); - printCheckBox("CONFLCKAUNL", "Auto unlatch", ((int)basicLockConfigAclPrefs[3] == 1), "chk_config_lock"); - printCheckBox("CONFLCKPRENA", "Pairing enabled", ((int)basicLockConfigAclPrefs[4] == 1), "chk_config_lock"); - printCheckBox("CONFLCKBTENA", "Button enabled", ((int)basicLockConfigAclPrefs[5] == 1), "chk_config_lock"); - printCheckBox("CONFLCKLEDENA", "LED flash enabled", ((int)basicLockConfigAclPrefs[6] == 1), "chk_config_lock"); - printCheckBox("CONFLCKLEDBR", "LED brightness", ((int)basicLockConfigAclPrefs[7] == 1), "chk_config_lock"); - printCheckBox("CONFLCKTZOFF", "Timezone offset", ((int)basicLockConfigAclPrefs[8] == 1), "chk_config_lock"); - printCheckBox("CONFLCKDSTM", "DST mode", ((int)basicLockConfigAclPrefs[9] == 1), "chk_config_lock"); - printCheckBox("CONFLCKFOB1", "Fob Action 1", ((int)basicLockConfigAclPrefs[10] == 1), "chk_config_lock"); - printCheckBox("CONFLCKFOB2", "Fob Action 2", ((int)basicLockConfigAclPrefs[11] == 1), "chk_config_lock"); - printCheckBox("CONFLCKFOB3", "Fob Action 3", ((int)basicLockConfigAclPrefs[12] == 1), "chk_config_lock"); - printCheckBox("CONFLCKSGLLCK", "Single Lock", ((int)basicLockConfigAclPrefs[13] == 1), "chk_config_lock"); - printCheckBox("CONFLCKADVM", "Advertising Mode", ((int)basicLockConfigAclPrefs[14] == 1), "chk_config_lock"); - printCheckBox("CONFLCKTZID", "Timezone ID", ((int)basicLockConfigAclPrefs[15] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNAME", "Name", ((int)basicLockConfigAclPrefs[0] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKLAT", "Latitude", ((int)basicLockConfigAclPrefs[1] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKLONG", "Longitude", ((int)basicLockConfigAclPrefs[2] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKAUNL", "Auto unlatch", ((int)basicLockConfigAclPrefs[3] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKPRENA", "Pairing enabled", ((int)basicLockConfigAclPrefs[4] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKBTENA", "Button enabled", ((int)basicLockConfigAclPrefs[5] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKLEDENA", "LED flash enabled", ((int)basicLockConfigAclPrefs[6] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKLEDBR", "LED brightness", ((int)basicLockConfigAclPrefs[7] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKTZOFF", "Timezone offset", ((int)basicLockConfigAclPrefs[8] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKDSTM", "DST mode", ((int)basicLockConfigAclPrefs[9] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKFOB1", "Fob Action 1", ((int)basicLockConfigAclPrefs[10] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKFOB2", "Fob Action 2", ((int)basicLockConfigAclPrefs[11] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKFOB3", "Fob Action 3", ((int)basicLockConfigAclPrefs[12] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKSGLLCK", "Single Lock", ((int)basicLockConfigAclPrefs[13] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKADVM", "Advertising Mode", ((int)basicLockConfigAclPrefs[14] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKTZID", "Timezone ID", ((int)basicLockConfigAclPrefs[15] == 1), "chk_config_lock"); - printCheckBox("CONFLCKUPOD", "Unlocked Position Offset Degrees", ((int)advancedLockConfigAclPrefs[0] == 1), "chk_config_lock"); - printCheckBox("CONFLCKLPOD", "Locked Position Offset Degrees", ((int)advancedLockConfigAclPrefs[1] == 1), "chk_config_lock"); - printCheckBox("CONFLCKSLPOD", "Single Locked Position Offset Degrees", ((int)advancedLockConfigAclPrefs[2] == 1), "chk_config_lock"); - printCheckBox("CONFLCKUTLTOD", "Unlocked To Locked Transition Offset Degrees", ((int)advancedLockConfigAclPrefs[3] == 1), "chk_config_lock"); - printCheckBox("CONFLCKLNGT", "Lock n Go timeout", ((int)advancedLockConfigAclPrefs[4] == 1), "chk_config_lock"); - printCheckBox("CONFLCKSBPA", "Single button press action", ((int)advancedLockConfigAclPrefs[5] == 1), "chk_config_lock"); - printCheckBox("CONFLCKDBPA", "Double button press action", ((int)advancedLockConfigAclPrefs[6] == 1), "chk_config_lock"); - printCheckBox("CONFLCKDC", "Detached cylinder", ((int)advancedLockConfigAclPrefs[7] == 1), "chk_config_lock"); - printCheckBox("CONFLCKBATT", "Battery type", ((int)advancedLockConfigAclPrefs[8] == 1), "chk_config_lock"); - printCheckBox("CONFLCKABTD", "Automatic battery type detection", ((int)advancedLockConfigAclPrefs[9] == 1), "chk_config_lock"); - printCheckBox("CONFLCKUNLD", "Unlatch duration", ((int)advancedLockConfigAclPrefs[10] == 1), "chk_config_lock"); - printCheckBox("CONFLCKALT", "Auto lock timeout", ((int)advancedLockConfigAclPrefs[11] == 1), "chk_config_lock"); - printCheckBox("CONFLCKAUNLD", "Auto unlock disabled", ((int)advancedLockConfigAclPrefs[12] == 1), "chk_config_lock"); - printCheckBox("CONFLCKNMENA", "Nightmode enabled", ((int)advancedLockConfigAclPrefs[13] == 1), "chk_config_lock"); - printCheckBox("CONFLCKNMST", "Nightmode start time", ((int)advancedLockConfigAclPrefs[14] == 1), "chk_config_lock"); - printCheckBox("CONFLCKNMET", "Nightmode end time", ((int)advancedLockConfigAclPrefs[15] == 1), "chk_config_lock"); - printCheckBox("CONFLCKNMALENA", "Nightmode auto lock enabled", ((int)advancedLockConfigAclPrefs[16] == 1), "chk_config_lock"); - printCheckBox("CONFLCKNMAULD", "Nightmode auto unlock disabled", ((int)advancedLockConfigAclPrefs[17] == 1), "chk_config_lock"); - printCheckBox("CONFLCKNMLOS", "Nightmode immediate lock on start", ((int)advancedLockConfigAclPrefs[18] == 1), "chk_config_lock"); - printCheckBox("CONFLCKALENA", "Auto lock enabled", ((int)advancedLockConfigAclPrefs[19] == 1), "chk_config_lock"); - printCheckBox("CONFLCKIALENA", "Immediate auto lock enabled", ((int)advancedLockConfigAclPrefs[20] == 1), "chk_config_lock"); - printCheckBox("CONFLCKAUENA", "Auto update enabled", ((int)advancedLockConfigAclPrefs[21] == 1), "chk_config_lock"); - _response.concat("
ChangeAllowed

"); - _response.concat("
"); + printCheckBox(&response, "CONFLCKUPOD", "Unlocked Position Offset Degrees", ((int)advancedLockConfigAclPrefs[0] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKLPOD", "Locked Position Offset Degrees", ((int)advancedLockConfigAclPrefs[1] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKSLPOD", "Single Locked Position Offset Degrees", ((int)advancedLockConfigAclPrefs[2] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKUTLTOD", "Unlocked To Locked Transition Offset Degrees", ((int)advancedLockConfigAclPrefs[3] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKLNGT", "Lock n Go timeout", ((int)advancedLockConfigAclPrefs[4] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKSBPA", "Single button press action", ((int)advancedLockConfigAclPrefs[5] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKDBPA", "Double button press action", ((int)advancedLockConfigAclPrefs[6] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKDC", "Detached cylinder", ((int)advancedLockConfigAclPrefs[7] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKBATT", "Battery type", ((int)advancedLockConfigAclPrefs[8] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKABTD", "Automatic battery type detection", ((int)advancedLockConfigAclPrefs[9] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKUNLD", "Unlatch duration", ((int)advancedLockConfigAclPrefs[10] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKALT", "Auto lock timeout", ((int)advancedLockConfigAclPrefs[11] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKAUNLD", "Auto unlock disabled", ((int)advancedLockConfigAclPrefs[12] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNMENA", "Nightmode enabled", ((int)advancedLockConfigAclPrefs[13] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNMST", "Nightmode start time", ((int)advancedLockConfigAclPrefs[14] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNMET", "Nightmode end time", ((int)advancedLockConfigAclPrefs[15] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNMALENA", "Nightmode auto lock enabled", ((int)advancedLockConfigAclPrefs[16] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNMAULD", "Nightmode auto unlock disabled", ((int)advancedLockConfigAclPrefs[17] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKNMLOS", "Nightmode immediate lock on start", ((int)advancedLockConfigAclPrefs[18] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKALENA", "Auto lock enabled", ((int)advancedLockConfigAclPrefs[19] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKIALENA", "Immediate auto lock enabled", ((int)advancedLockConfigAclPrefs[20] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKAUENA", "Auto update enabled", ((int)advancedLockConfigAclPrefs[21] == 1), "chk_config_lock"); + response.print("
"); + response.print("
"); } if(_nukiOpener != nullptr) { @@ -3009,117 +3023,119 @@ void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request) uint32_t advancedOpenerConfigAclPrefs[20]; _preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs)); - _response.concat("

Nuki Opener Access Control

"); - _response.concat(""); - _response.concat(""); - _response.concat(""); + response.print("

Nuki Opener Access Control

"); + response.print(""); + response.print(""); + response.print("
ActionAllowed
"); - printCheckBox("ACLOPNUNLCK", "Activate Ring-to-Open", ((int)aclPrefs[9] == 1), "chk_access_opener"); - printCheckBox("ACLOPNLCK", "Deactivate Ring-to-Open", ((int)aclPrefs[10] == 1), "chk_access_opener"); - printCheckBox("ACLOPNUNLTCH", "Electric Strike Actuation", ((int)aclPrefs[11] == 1), "chk_access_opener"); - printCheckBox("ACLOPNUNLCKCM", "Activate Continuous Mode", ((int)aclPrefs[12] == 1), "chk_access_opener"); - printCheckBox("ACLOPNLCKCM", "Deactivate Continuous Mode", ((int)aclPrefs[13] == 1), "chk_access_opener"); - printCheckBox("ACLOPNFOB1", "Fob Action 1", ((int)aclPrefs[14] == 1), "chk_access_opener"); - printCheckBox("ACLOPNFOB2", "Fob Action 2", ((int)aclPrefs[15] == 1), "chk_access_opener"); - printCheckBox("ACLOPNFOB3", "Fob Action 3", ((int)aclPrefs[16] == 1), "chk_access_opener"); - _response.concat("
ActionAllowed

"); + printCheckBox(&response, "ACLOPNUNLCK", "Activate Ring-to-Open", ((int)aclPrefs[9] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNLCK", "Deactivate Ring-to-Open", ((int)aclPrefs[10] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNUNLTCH", "Electric Strike Actuation", ((int)aclPrefs[11] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNUNLCKCM", "Activate Continuous Mode", ((int)aclPrefs[12] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNLCKCM", "Deactivate Continuous Mode", ((int)aclPrefs[13] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNFOB1", "Fob Action 1", ((int)aclPrefs[14] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNFOB2", "Fob Action 2", ((int)aclPrefs[15] == 1), "chk_access_opener"); + printCheckBox(&response, "ACLOPNFOB3", "Fob Action 3", ((int)aclPrefs[16] == 1), "chk_access_opener"); + response.print("
"); - _response.concat("

Nuki Opener Config Control (Requires PIN to be set)

"); - _response.concat(""); - _response.concat(""); - _response.concat(""); + response.print("

Nuki Opener Config Control (Requires PIN to be set)

"); + response.print(""); + response.print(""); + response.print("
ChangeAllowed
"); - printCheckBox("CONFOPNNAME", "Name", ((int)basicOpenerConfigAclPrefs[0] == 1), "chk_config_opener"); - printCheckBox("CONFOPNLAT", "Latitude", ((int)basicOpenerConfigAclPrefs[1] == 1), "chk_config_opener"); - printCheckBox("CONFOPNLONG", "Longitude", ((int)basicOpenerConfigAclPrefs[2] == 1), "chk_config_opener"); - printCheckBox("CONFOPNPRENA", "Pairing enabled", ((int)basicOpenerConfigAclPrefs[3] == 1), "chk_config_opener"); - printCheckBox("CONFOPNBTENA", "Button enabled", ((int)basicOpenerConfigAclPrefs[4] == 1), "chk_config_opener"); - printCheckBox("CONFOPNLEDENA", "LED flash enabled", ((int)basicOpenerConfigAclPrefs[5] == 1), "chk_config_opener"); - printCheckBox("CONFOPNTZOFF", "Timezone offset", ((int)basicOpenerConfigAclPrefs[6] == 1), "chk_config_opener"); - printCheckBox("CONFOPNDSTM", "DST mode", ((int)basicOpenerConfigAclPrefs[7] == 1), "chk_config_opener"); - printCheckBox("CONFOPNFOB1", "Fob Action 1", ((int)basicOpenerConfigAclPrefs[8] == 1), "chk_config_opener"); - printCheckBox("CONFOPNFOB2", "Fob Action 2", ((int)basicOpenerConfigAclPrefs[9] == 1), "chk_config_opener"); - printCheckBox("CONFOPNFOB3", "Fob Action 3", ((int)basicOpenerConfigAclPrefs[10] == 1), "chk_config_opener"); - printCheckBox("CONFOPNOPM", "Operating Mode", ((int)basicOpenerConfigAclPrefs[11] == 1), "chk_config_opener"); - printCheckBox("CONFOPNADVM", "Advertising Mode", ((int)basicOpenerConfigAclPrefs[12] == 1), "chk_config_opener"); - printCheckBox("CONFOPNTZID", "Timezone ID", ((int)basicOpenerConfigAclPrefs[13] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNNAME", "Name", ((int)basicOpenerConfigAclPrefs[0] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNLAT", "Latitude", ((int)basicOpenerConfigAclPrefs[1] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNLONG", "Longitude", ((int)basicOpenerConfigAclPrefs[2] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNPRENA", "Pairing enabled", ((int)basicOpenerConfigAclPrefs[3] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNBTENA", "Button enabled", ((int)basicOpenerConfigAclPrefs[4] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNLEDENA", "LED flash enabled", ((int)basicOpenerConfigAclPrefs[5] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNTZOFF", "Timezone offset", ((int)basicOpenerConfigAclPrefs[6] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNDSTM", "DST mode", ((int)basicOpenerConfigAclPrefs[7] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNFOB1", "Fob Action 1", ((int)basicOpenerConfigAclPrefs[8] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNFOB2", "Fob Action 2", ((int)basicOpenerConfigAclPrefs[9] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNFOB3", "Fob Action 3", ((int)basicOpenerConfigAclPrefs[10] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNOPM", "Operating Mode", ((int)basicOpenerConfigAclPrefs[11] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNADVM", "Advertising Mode", ((int)basicOpenerConfigAclPrefs[12] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNTZID", "Timezone ID", ((int)basicOpenerConfigAclPrefs[13] == 1), "chk_config_opener"); - printCheckBox("CONFOPNICID", "Intercom ID", ((int)advancedOpenerConfigAclPrefs[0] == 1), "chk_config_opener"); - printCheckBox("CONFOPNBUSMS", "BUS mode Switch", ((int)advancedOpenerConfigAclPrefs[1] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSCDUR", "Short Circuit Duration", ((int)advancedOpenerConfigAclPrefs[2] == 1), "chk_config_opener"); - printCheckBox("CONFOPNESD", "Eletric Strike Delay", ((int)advancedOpenerConfigAclPrefs[3] == 1), "chk_config_opener"); - printCheckBox("CONFOPNRESD", "Random Electric Strike Delay", ((int)advancedOpenerConfigAclPrefs[4] == 1), "chk_config_opener"); - printCheckBox("CONFOPNESDUR", "Electric Strike Duration", ((int)advancedOpenerConfigAclPrefs[5] == 1), "chk_config_opener"); - printCheckBox("CONFOPNDRTOAR", "Disable RTO after ring", ((int)advancedOpenerConfigAclPrefs[6] == 1), "chk_config_opener"); - printCheckBox("CONFOPNRTOT", "RTO timeout", ((int)advancedOpenerConfigAclPrefs[7] == 1), "chk_config_opener"); - printCheckBox("CONFOPNDRBSUP", "Doorbell suppression", ((int)advancedOpenerConfigAclPrefs[8] == 1), "chk_config_opener"); - printCheckBox("CONFOPNDRBSUPDUR", "Doorbell suppression duration", ((int)advancedOpenerConfigAclPrefs[9] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSRING", "Sound Ring", ((int)advancedOpenerConfigAclPrefs[10] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSOPN", "Sound Open", ((int)advancedOpenerConfigAclPrefs[11] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSRTO", "Sound RTO", ((int)advancedOpenerConfigAclPrefs[12] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSCM", "Sound CM", ((int)advancedOpenerConfigAclPrefs[13] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSCFRM", "Sound confirmation", ((int)advancedOpenerConfigAclPrefs[14] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSLVL", "Sound level", ((int)advancedOpenerConfigAclPrefs[15] == 1), "chk_config_opener"); - printCheckBox("CONFOPNSBPA", "Single button press action", ((int)advancedOpenerConfigAclPrefs[16] == 1), "chk_config_opener"); - printCheckBox("CONFOPNDBPA", "Double button press action", ((int)advancedOpenerConfigAclPrefs[17] == 1), "chk_config_opener"); - printCheckBox("CONFOPNBATT", "Battery type", ((int)advancedOpenerConfigAclPrefs[18] == 1), "chk_config_opener"); - printCheckBox("CONFOPNABTD", "Automatic battery type detection", ((int)advancedOpenerConfigAclPrefs[19] == 1), "chk_config_opener"); - _response.concat("
ChangeAllowed

"); - _response.concat("
"); + printCheckBox(&response, "CONFOPNICID", "Intercom ID", ((int)advancedOpenerConfigAclPrefs[0] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNBUSMS", "BUS mode Switch", ((int)advancedOpenerConfigAclPrefs[1] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSCDUR", "Short Circuit Duration", ((int)advancedOpenerConfigAclPrefs[2] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNESD", "Eletric Strike Delay", ((int)advancedOpenerConfigAclPrefs[3] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNRESD", "Random Electric Strike Delay", ((int)advancedOpenerConfigAclPrefs[4] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNESDUR", "Electric Strike Duration", ((int)advancedOpenerConfigAclPrefs[5] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNDRTOAR", "Disable RTO after ring", ((int)advancedOpenerConfigAclPrefs[6] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNRTOT", "RTO timeout", ((int)advancedOpenerConfigAclPrefs[7] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNDRBSUP", "Doorbell suppression", ((int)advancedOpenerConfigAclPrefs[8] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNDRBSUPDUR", "Doorbell suppression duration", ((int)advancedOpenerConfigAclPrefs[9] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSRING", "Sound Ring", ((int)advancedOpenerConfigAclPrefs[10] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSOPN", "Sound Open", ((int)advancedOpenerConfigAclPrefs[11] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSRTO", "Sound RTO", ((int)advancedOpenerConfigAclPrefs[12] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSCM", "Sound CM", ((int)advancedOpenerConfigAclPrefs[13] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSCFRM", "Sound confirmation", ((int)advancedOpenerConfigAclPrefs[14] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSLVL", "Sound level", ((int)advancedOpenerConfigAclPrefs[15] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNSBPA", "Single button press action", ((int)advancedOpenerConfigAclPrefs[16] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNDBPA", "Double button press action", ((int)advancedOpenerConfigAclPrefs[17] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNBATT", "Battery type", ((int)advancedOpenerConfigAclPrefs[18] == 1), "chk_config_opener"); + printCheckBox(&response, "CONFOPNABTD", "Automatic battery type detection", ((int)advancedOpenerConfigAclPrefs[19] == 1), "chk_config_opener"); + response.print("
"); + response.print("
"); } - _response.concat("
"); - _response.concat(""); - sendResponse(request); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildNukiConfigHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - _response.concat("
"); - _response.concat("

Basic Nuki Configuration

"); - _response.concat(""); - printCheckBox("LOCKENA", "Nuki Lock enabled", _preferences->getBool(preference_lock_enabled), ""); - if(_preferences->getBool(preference_lock_enabled)) printInputField("MQTTPATH", "MQTT Nuki Lock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180, ""); - printCheckBox("OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled), ""); - if(_preferences->getBool(preference_opener_enabled)) printInputField("MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180, ""); - _response.concat("

"); - _response.concat("

Advanced Nuki Configuration

"); - _response.concat(""); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print(""); + response.print("

Basic Nuki Configuration

"); + response.print("
"); + printCheckBox(&response, "LOCKENA", "Nuki Lock enabled", _preferences->getBool(preference_lock_enabled), ""); + if(_preferences->getBool(preference_lock_enabled)) printInputField(&response, "MQTTPATH", "MQTT Nuki Lock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180, ""); + printCheckBox(&response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled), ""); + if(_preferences->getBool(preference_opener_enabled)) printInputField(&response, "MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180, ""); + response.print("

"); + response.print("

Advanced Nuki Configuration

"); + response.print(""); - printInputField("LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10, ""); - printInputField("CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10, ""); - printInputField("BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10, ""); + printInputField(&response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10, ""); + printInputField(&response, "CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10, ""); + printInputField(&response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10, ""); if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) { - printInputField("KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10, ""); + printInputField(&response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10, ""); } - printInputField("NRTRY", "Number of retries if command failed", _preferences->getInt(preference_command_nr_of_retries), 10, ""); - printInputField("TRYDLY", "Delay between retries (milliseconds)", _preferences->getInt(preference_command_retry_delay), 10, ""); - if(_preferences->getBool(preference_lock_enabled, true)) printCheckBox("REGAPP", "Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), ""); - if(_preferences->getBool(preference_opener_enabled, false)) printCheckBox("REGAPPOPN", "Opener: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_opener_as_app), ""); - printInputField("RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10, ""); - printInputField("TXPWR", "BLE transmit power in dB (minimum -12, maximum 9)", _preferences->getInt(preference_ble_tx_power, 9), 10, ""); + printInputField(&response, "NRTRY", "Number of retries if command failed", _preferences->getInt(preference_command_nr_of_retries), 10, ""); + printInputField(&response, "TRYDLY", "Delay between retries (milliseconds)", _preferences->getInt(preference_command_retry_delay), 10, ""); + if(_preferences->getBool(preference_lock_enabled, true)) printCheckBox(&response, "REGAPP", "Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), ""); + if(_preferences->getBool(preference_opener_enabled, false)) printCheckBox(&response, "REGAPPOPN", "Opener: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_opener_as_app), ""); + printInputField(&response, "RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10, ""); + printInputField(&response, "TXPWR", "BLE transmit power in dB (minimum -12, maximum 9)", _preferences->getInt(preference_ble_tx_power, 9), 10, ""); - _response.concat("
"); - _response.concat("
"); - _response.concat("
"); - _response.concat(""); - sendResponse(request); + response.print(""); + response.print("
"); + response.print(""); + response.print(""); + return response.endSend(); } -void WebCfgServer::buildGpioConfigHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildGpioConfigHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - _response.concat("
"); - _response.concat("

GPIO Configuration

"); - _response.concat(""); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print(""); + response.print("

GPIO Configuration

"); + response.print("
"); std::vector> options; String gpiopreselects = "var gpio = []; "; @@ -3130,129 +3146,131 @@ void WebCfgServer::buildGpioConfigHtml(AsyncWebServerRequest *request) { String pinStr = String(pin); String pinDesc = "Gpio " + pinStr; - printDropDown(pinStr.c_str(), pinDesc.c_str(), "", options, "gpioselect"); + printDropDown(&response, pinStr.c_str(), pinDesc.c_str(), "", options, "gpioselect"); if(std::find(disabledPins.begin(), disabledPins.end(), pin) != disabledPins.end()) gpiopreselects.concat("gpio[" + pinStr + "] = '21';"); else gpiopreselects.concat("gpio[" + pinStr + "] = '" + getPreselectionForGpio(pin) + "';"); } - _response.concat("
"); - _response.concat("
"); - _response.concat("
"); + response.print(""); + response.print("
"); + response.print(""); options = getGpioOptions(); - _response.concat(""); - _response.concat(""); - sendResponse(request); + response.print("'; var gpioselects = document.getElementsByClassName('gpioselect'); for (let i = 0; i < gpioselects.length; i++) { gpioselects[i].options.length = 0; gpioselects[i].innerHTML = gpiooptions; gpioselects[i].value = gpio[gpioselects[i].name]; if(gpioselects[i].value == 21) { gpioselects[i].disabled = true; } }"); + response.print(""); + return response.endSend(); } #ifndef CONFIG_IDF_TARGET_ESP32H2 -void WebCfgServer::buildConfigureWifiHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildConfigureWifiHtml(PsychicRequest *request) { - _response = ""; - buildHtmlHeader(); - _response.concat("

Wi-Fi

"); - _response.concat("Click confirm to restart ESP into Wi-Fi configuration mode. After restart, connect to ESP access point to reconfigure Wi-Fi.

"); - buildNavigationButton("Confirm", "/wifimanager"); - _response.concat(""); - sendResponse(request); + PsychicStreamResponse response(request, "text/plain"); + response.beginSend(); + buildHtmlHeader(&response); + response.print("

Wi-Fi

"); + response.print("Click confirm to restart ESP into Wi-Fi configuration mode. After restart, connect to ESP access point to reconfigure Wi-Fi.

"); + buildNavigationButton(&response, "Confirm", "/wifimanager"); + response.print(""); + return response.endSend(); } #endif -void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request) { - _response = ""; uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); - buildHtmlHeader(); - _response.concat("

System Information

");
-    _response.concat("------------ NUKI HUB ------------");
-    _response.concat("\nVersion: ");
-    _response.concat(NUKI_HUB_VERSION);
-    _response.concat("\nBuild: ");
-    _response.concat(NUKI_HUB_BUILD);
+    PsychicStreamResponse response(request, "text/plain");
+    response.beginSend();
+    buildHtmlHeader(&response);
+    response.print("

System Information

");
+    response.print("------------ NUKI HUB ------------");
+    response.print("\nVersion: ");
+    response.print(NUKI_HUB_VERSION);
+    response.print("\nBuild: ");
+    response.print(NUKI_HUB_BUILD);
     #ifndef DEBUG_NUKIHUB
-    _response.concat("\nBuild type: Release");
+    response.print("\nBuild type: Release");
     #else
-    _response.concat("\nBuild type: Debug");
+    response.print("\nBuild type: Debug");
     #endif
-    _response.concat("\nBuild date: ");
-    _response.concat(NUKI_HUB_DATE);
-    _response.concat("\nUpdater version: ");
-    _response.concat(_preferences->getString(preference_updater_version, ""));
-    _response.concat("\nUpdater build: ");
-    _response.concat(_preferences->getString(preference_updater_build, ""));
-    _response.concat("\nUpdater build date: ");
-    _response.concat(_preferences->getString(preference_updater_date, ""));
-    _response.concat("\nUptime (min): ");
-    _response.concat(esp_timer_get_time() / 1000 / 1000 / 60);
-    _response.concat("\nConfig version: ");
-    _response.concat(_preferences->getInt(preference_config_version));
-    _response.concat("\nLast restart reason FW: ");
-    _response.concat(getRestartReason());
-    _response.concat("\nLast restart reason ESP: ");
-    _response.concat(getEspRestartReason());
-    _response.concat("\nFree heap: ");
-    _response.concat(esp_get_free_heap_size());
-    _response.concat("\nNetwork task stack high watermark: ");
-    _response.concat(uxTaskGetStackHighWaterMark(networkTaskHandle));
-    _response.concat("\nNuki task stack high watermark: ");
-    _response.concat(uxTaskGetStackHighWaterMark(nukiTaskHandle));
-    _response.concat("\n\n------------ GENERAL SETTINGS ------------");
-    _response.concat("\nNetwork task stack size: ");
-    _response.concat(_preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE));
-    _response.concat("\nNuki task stack size: ");
-    _response.concat(_preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE));
-    _response.concat("\nCheck for updates: ");
-    _response.concat(_preferences->getBool(preference_check_updates, false) ? "Yes" : "No");
-    _response.concat("\nLatest version: ");
-    _response.concat(_preferences->getString(preference_latest_version, ""));
-    _response.concat("\nAllow update from MQTT: ");
-    _response.concat(_preferences->getBool(preference_update_from_mqtt, false) ? "Yes" : "No");
-    _response.concat("\nWeb configurator username: ");
-    _response.concat(_preferences->getString(preference_cred_user, "").length() > 0 ? "***" : "Not set");
-    _response.concat("\nWeb configurator password: ");
-    _response.concat(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set");
-    _response.concat("\nWeb configurator enabled: ");
-    _response.concat(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No");
-    _response.concat("\nPublish debug information enabled: ");
-    _response.concat(_preferences->getBool(preference_publish_debug_info, false) ? "Yes" : "No");
-    _response.concat("\nMQTT log enabled: ");
-    _response.concat(_preferences->getBool(preference_mqtt_log_enabled, false) ? "Yes" : "No");
-    _response.concat("\nWebserial enabled: ");
-    _response.concat(_preferences->getBool(preference_webserial_enabled, false) ? "Yes" : "No");
-    _response.concat("\nBootloop protection enabled: ");
-    _response.concat(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Yes" : "No");
-    _response.concat("\n\n------------ NETWORK ------------");
-    _response.concat("\nNetwork device: ");
-    _response.concat(_network->networkDeviceName());
-    _response.concat("\nNetwork connected: ");
-    _response.concat(_network->isConnected() ? "Yes" : "No");
+    response.print("\nBuild date: ");
+    response.print(NUKI_HUB_DATE);
+    response.print("\nUpdater version: ");
+    response.print(_preferences->getString(preference_updater_version, ""));
+    response.print("\nUpdater build: ");
+    response.print(_preferences->getString(preference_updater_build, ""));
+    response.print("\nUpdater build date: ");
+    response.print(_preferences->getString(preference_updater_date, ""));
+    response.print("\nUptime (min): ");
+    response.print(esp_timer_get_time() / 1000 / 1000 / 60);
+    response.print("\nConfig version: ");
+    response.print(_preferences->getInt(preference_config_version));
+    response.print("\nLast restart reason FW: ");
+    response.print(getRestartReason());
+    response.print("\nLast restart reason ESP: ");
+    response.print(getEspRestartReason());
+    response.print("\nFree heap: ");
+    response.print(esp_get_free_heap_size());
+    response.print("\nNetwork task stack high watermark: ");
+    response.print(uxTaskGetStackHighWaterMark(networkTaskHandle));
+    response.print("\nNuki task stack high watermark: ");
+    response.print(uxTaskGetStackHighWaterMark(nukiTaskHandle));
+    response.print("\n\n------------ GENERAL SETTINGS ------------");
+    response.print("\nNetwork task stack size: ");
+    response.print(_preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE));
+    response.print("\nNuki task stack size: ");
+    response.print(_preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE));
+    response.print("\nCheck for updates: ");
+    response.print(_preferences->getBool(preference_check_updates, false) ? "Yes" : "No");
+    response.print("\nLatest version: ");
+    response.print(_preferences->getString(preference_latest_version, ""));
+    response.print("\nAllow update from MQTT: ");
+    response.print(_preferences->getBool(preference_update_from_mqtt, false) ? "Yes" : "No");
+    response.print("\nWeb configurator username: ");
+    response.print(_preferences->getString(preference_cred_user, "").length() > 0 ? "***" : "Not set");
+    response.print("\nWeb configurator password: ");
+    response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set");
+    response.print("\nWeb configurator enabled: ");
+    response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No");
+    response.print("\nPublish debug information enabled: ");
+    response.print(_preferences->getBool(preference_publish_debug_info, false) ? "Yes" : "No");
+    response.print("\nMQTT log enabled: ");
+    response.print(_preferences->getBool(preference_mqtt_log_enabled, false) ? "Yes" : "No");
+    response.print("\nWebserial enabled: ");
+    response.print(_preferences->getBool(preference_webserial_enabled, false) ? "Yes" : "No");
+    response.print("\nBootloop protection enabled: ");
+    response.print(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Yes" : "No");
+    response.print("\n\n------------ NETWORK ------------");
+    response.print("\nNetwork device: ");
+    response.print(_network->networkDeviceName());
+    response.print("\nNetwork connected: ");
+    response.print(_network->isConnected() ? "Yes" : "No");
     if(_network->isConnected())
     {
-        _response.concat("\nIP Address: ");
-        _response.concat(_network->localIP());
+        response.print("\nIP Address: ");
+        response.print(_network->localIP());
 
         if(_network->networkDeviceName() == "Built-in Wi-Fi")
         {
             #ifndef CONFIG_IDF_TARGET_ESP32H2
-            _response.concat("\nSSID: ");
-            _response.concat(WiFi.SSID());
-            _response.concat("\nBSSID of AP: ");
-            _response.concat(_network->networkBSSID());
-            _response.concat("\nESP32 MAC address: ");
-            _response.concat(WiFi.macAddress());
+            response.print("\nSSID: ");
+            response.print(WiFi.SSID());
+            response.print("\nBSSID of AP: ");
+            response.print(_network->networkBSSID());
+            response.print("\nESP32 MAC address: ");
+            response.print(WiFi.macAddress());
             #endif
         }
         else
@@ -3260,276 +3278,276 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
             //Ethernet info
         }
     }
-    _response.concat("\n\n------------ NETWORK SETTINGS ------------");
-    _response.concat("\nNuki Hub hostname: ");
-    _response.concat(_preferences->getString(preference_hostname, ""));
-    if(_preferences->getBool(preference_ip_dhcp_enabled, true)) _response.concat("\nDHCP enabled: Yes");
+    response.print("\n\n------------ NETWORK SETTINGS ------------");
+    response.print("\nNuki Hub hostname: ");
+    response.print(_preferences->getString(preference_hostname, ""));
+    if(_preferences->getBool(preference_ip_dhcp_enabled, true)) response.print("\nDHCP enabled: Yes");
     else
     {
-        _response.concat("\nDHCP enabled: No");
-        _response.concat("\nStatic IP address: ");
-        _response.concat(_preferences->getString(preference_ip_address, ""));
-        _response.concat("\nStatic IP subnet: ");
-        _response.concat(_preferences->getString(preference_ip_subnet, ""));
-        _response.concat("\nStatic IP gateway: ");
-        _response.concat(_preferences->getString(preference_ip_gateway, ""));
-        _response.concat("\nStatic IP DNS server: ");
-        _response.concat(_preferences->getString(preference_ip_dns_server, ""));
+        response.print("\nDHCP enabled: No");
+        response.print("\nStatic IP address: ");
+        response.print(_preferences->getString(preference_ip_address, ""));
+        response.print("\nStatic IP subnet: ");
+        response.print(_preferences->getString(preference_ip_subnet, ""));
+        response.print("\nStatic IP gateway: ");
+        response.print(_preferences->getString(preference_ip_gateway, ""));
+        response.print("\nStatic IP DNS server: ");
+        response.print(_preferences->getString(preference_ip_dns_server, ""));
     }
 
     #ifndef CONFIG_IDF_TARGET_ESP32H2
-    _response.concat("\nFallback to Wi-Fi / Wi-Fi config portal disabled: ");
-    _response.concat(_preferences->getBool(preference_network_wifi_fallback_disabled, false) ? "Yes" : "No");
+    response.print("\nFallback to Wi-Fi / Wi-Fi config portal disabled: ");
+    response.print(_preferences->getBool(preference_network_wifi_fallback_disabled, false) ? "Yes" : "No");
     if(_network->networkDeviceName() == "Built-in Wi-Fi")
     {
-        _response.concat("\nConnect to AP with the best signal enabled: ");
-        _response.concat(_preferences->getBool(preference_find_best_rssi, false) ? "Yes" : "No");
-        _response.concat("\nRSSI Publish interval (s): ");
+        response.print("\nConnect to AP with the best signal enabled: ");
+        response.print(_preferences->getBool(preference_find_best_rssi, false) ? "Yes" : "No");
+        response.print("\nRSSI Publish interval (s): ");
 
-        if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) _response.concat("Disabled");
-        else _response.concat(_preferences->getInt(preference_rssi_publish_interval, 60));
+        if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) response.print("Disabled");
+        else response.print(_preferences->getInt(preference_rssi_publish_interval, 60));
     }
     #endif
-    _response.concat("\nRestart ESP32 on network disconnect enabled: ");
-    _response.concat(_preferences->getBool(preference_restart_on_disconnect, false) ? "Yes" : "No");
-    _response.concat("\nReconnect network on MQTT connection failure enabled: ");
-    _response.concat(_preferences->getBool(preference_recon_netw_on_mqtt_discon, false) ? "Yes" : "No");
-    _response.concat("\nMQTT Timeout until restart (s): ");
-    if(_preferences->getInt(preference_network_timeout, 60) < 0) _response.concat("Disabled");
-    else _response.concat(_preferences->getInt(preference_network_timeout, 60));
-    _response.concat("\n\n------------ MQTT ------------");
-    _response.concat("\nMQTT connected: ");
-    _response.concat(_network->mqttConnectionState() > 0 ? "Yes" : "No");
-    _response.concat("\nMQTT broker address: ");
-    _response.concat(_preferences->getString(preference_mqtt_broker, ""));
-    _response.concat("\nMQTT broker port: ");
-    _response.concat(_preferences->getInt(preference_mqtt_broker_port, 1883));
-    _response.concat("\nMQTT username: ");
-    _response.concat(_preferences->getString(preference_mqtt_user, "").length() > 0 ? "***" : "Not set");
-    _response.concat("\nMQTT password: ");
-    _response.concat(_preferences->getString(preference_mqtt_password, "").length() > 0 ? "***" : "Not set");
+    response.print("\nRestart ESP32 on network disconnect enabled: ");
+    response.print(_preferences->getBool(preference_restart_on_disconnect, false) ? "Yes" : "No");
+    response.print("\nReconnect network on MQTT connection failure enabled: ");
+    response.print(_preferences->getBool(preference_recon_netw_on_mqtt_discon, false) ? "Yes" : "No");
+    response.print("\nMQTT Timeout until restart (s): ");
+    if(_preferences->getInt(preference_network_timeout, 60) < 0) response.print("Disabled");
+    else response.print(_preferences->getInt(preference_network_timeout, 60));
+    response.print("\n\n------------ MQTT ------------");
+    response.print("\nMQTT connected: ");
+    response.print(_network->mqttConnectionState() > 0 ? "Yes" : "No");
+    response.print("\nMQTT broker address: ");
+    response.print(_preferences->getString(preference_mqtt_broker, ""));
+    response.print("\nMQTT broker port: ");
+    response.print(_preferences->getInt(preference_mqtt_broker_port, 1883));
+    response.print("\nMQTT username: ");
+    response.print(_preferences->getString(preference_mqtt_user, "").length() > 0 ? "***" : "Not set");
+    response.print("\nMQTT password: ");
+    response.print(_preferences->getString(preference_mqtt_password, "").length() > 0 ? "***" : "Not set");
     if(_preferences->getBool(preference_lock_enabled, true))
     {
-        _response.concat("\nMQTT lock base topic: ");
-        _response.concat(_preferences->getString(preference_mqtt_lock_path, ""));
+        response.print("\nMQTT lock base topic: ");
+        response.print(_preferences->getString(preference_mqtt_lock_path, ""));
     }
     if(_preferences->getBool(preference_opener_enabled, false))
     {
-        _response.concat("\nMQTT opener base topic: ");
-        _response.concat(_preferences->getString(preference_mqtt_lock_path, ""));
+        response.print("\nMQTT opener base topic: ");
+        response.print(_preferences->getString(preference_mqtt_lock_path, ""));
     }
-    _response.concat("\nMQTT SSL CA: ");
-    _response.concat(_preferences->getString(preference_mqtt_ca, "").length() > 0 ? "***" : "Not set");
-    _response.concat("\nMQTT SSL CRT: ");
-    _response.concat(_preferences->getString(preference_mqtt_crt, "").length() > 0 ? "***" : "Not set");
-    _response.concat("\nMQTT SSL Key: ");
-    _response.concat(_preferences->getString(preference_mqtt_key, "").length() > 0 ? "***" : "Not set");
-    _response.concat("\n\n------------ BLUETOOTH ------------");
-    _response.concat("\nBluetooth TX power (dB): ");
-    _response.concat(_preferences->getInt(preference_ble_tx_power, 9));
-    _response.concat("\nBluetooth command nr of retries: ");
-    _response.concat(_preferences->getInt(preference_command_nr_of_retries, 3));
-    _response.concat("\nBluetooth command retry delay (ms): ");
-    _response.concat(_preferences->getInt(preference_command_retry_delay, 100));
-    _response.concat("\nSeconds until reboot when no BLE beacons recieved: ");
-    _response.concat(_preferences->getInt(preference_restart_ble_beacon_lost, 60));
-    _response.concat("\n\n------------ QUERY / PUBLISH SETTINGS ------------");
-    _response.concat("\nLock/Opener state query interval (s): ");
-    _response.concat(_preferences->getInt(preference_query_interval_lockstate, 1800));
-    _response.concat("\nPublish Nuki device authorization log: ");
-    _response.concat(_preferences->getBool(preference_publish_authdata, false) ? "Yes" : "No");
-    _response.concat("\nMax authorization log entries to retrieve: ");
-    _response.concat(_preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG));
-    _response.concat("\nBattery state query interval (s): ");
-    _response.concat(_preferences->getInt(preference_query_interval_battery, 1800));
-    _response.concat("\nMost non-JSON MQTT topics disabled: ");
-    _response.concat(_preferences->getBool(preference_disable_non_json, false) ? "Yes" : "No");
-    _response.concat("\nPublish Nuki device config: ");
-    _response.concat(_preferences->getBool(preference_conf_info_enabled, false) ? "Yes" : "No");
-    _response.concat("\nConfig query interval (s): ");
-    _response.concat(_preferences->getInt(preference_query_interval_configuration, 3600));
-    _response.concat("\nPublish Keypad info: ");
-    _response.concat(_preferences->getBool(preference_keypad_info_enabled, false) ? "Yes" : "No");
-    _response.concat("\nKeypad query interval (s): ");
-    _response.concat(_preferences->getInt(preference_query_interval_keypad, 1800));
-    _response.concat("\nEnable Keypad control: ");
-    _response.concat(_preferences->getBool(preference_keypad_control_enabled, false) ? "Yes" : "No");
-    _response.concat("\nPublish Keypad topic per entry: ");
-    _response.concat(_preferences->getBool(preference_keypad_topic_per_entry, false) ? "Yes" : "No");
-    _response.concat("\nPublish Keypad codes: ");
-    _response.concat(_preferences->getBool(preference_keypad_publish_code, false) ? "Yes" : "No");
-    _response.concat("\nMax keypad entries to retrieve: ");
-    _response.concat(_preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
-    _response.concat("\nPublish timecontrol info: ");
-    _response.concat(_preferences->getBool(preference_timecontrol_info_enabled, false) ? "Yes" : "No");
-    _response.concat("\nKeypad query interval (s): ");
-    _response.concat(_preferences->getInt(preference_query_interval_keypad, 1800));
-    _response.concat("\nEnable timecontrol control: ");
-    _response.concat(_preferences->getBool(preference_timecontrol_control_enabled, false) ? "Yes" : "No");
-    _response.concat("\nPublish timecontrol topic per entry: ");
-    _response.concat(_preferences->getBool(preference_timecontrol_topic_per_entry, false) ? "Yes" : "No");
-    _response.concat("\nMax timecontrol entries to retrieve: ");
-    _response.concat(_preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL));
-    _response.concat("\n\n------------ HOME ASSISTANT ------------");
-    _response.concat("\nHome Assistant auto discovery enabled: ");
+    response.print("\nMQTT SSL CA: ");
+    response.print(_preferences->getString(preference_mqtt_ca, "").length() > 0 ? "***" : "Not set");
+    response.print("\nMQTT SSL CRT: ");
+    response.print(_preferences->getString(preference_mqtt_crt, "").length() > 0 ? "***" : "Not set");
+    response.print("\nMQTT SSL Key: ");
+    response.print(_preferences->getString(preference_mqtt_key, "").length() > 0 ? "***" : "Not set");
+    response.print("\n\n------------ BLUETOOTH ------------");
+    response.print("\nBluetooth TX power (dB): ");
+    response.print(_preferences->getInt(preference_ble_tx_power, 9));
+    response.print("\nBluetooth command nr of retries: ");
+    response.print(_preferences->getInt(preference_command_nr_of_retries, 3));
+    response.print("\nBluetooth command retry delay (ms): ");
+    response.print(_preferences->getInt(preference_command_retry_delay, 100));
+    response.print("\nSeconds until reboot when no BLE beacons recieved: ");
+    response.print(_preferences->getInt(preference_restart_ble_beacon_lost, 60));
+    response.print("\n\n------------ QUERY / PUBLISH SETTINGS ------------");
+    response.print("\nLock/Opener state query interval (s): ");
+    response.print(_preferences->getInt(preference_query_interval_lockstate, 1800));
+    response.print("\nPublish Nuki device authorization log: ");
+    response.print(_preferences->getBool(preference_publish_authdata, false) ? "Yes" : "No");
+    response.print("\nMax authorization log entries to retrieve: ");
+    response.print(_preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG));
+    response.print("\nBattery state query interval (s): ");
+    response.print(_preferences->getInt(preference_query_interval_battery, 1800));
+    response.print("\nMost non-JSON MQTT topics disabled: ");
+    response.print(_preferences->getBool(preference_disable_non_json, false) ? "Yes" : "No");
+    response.print("\nPublish Nuki device config: ");
+    response.print(_preferences->getBool(preference_conf_info_enabled, false) ? "Yes" : "No");
+    response.print("\nConfig query interval (s): ");
+    response.print(_preferences->getInt(preference_query_interval_configuration, 3600));
+    response.print("\nPublish Keypad info: ");
+    response.print(_preferences->getBool(preference_keypad_info_enabled, false) ? "Yes" : "No");
+    response.print("\nKeypad query interval (s): ");
+    response.print(_preferences->getInt(preference_query_interval_keypad, 1800));
+    response.print("\nEnable Keypad control: ");
+    response.print(_preferences->getBool(preference_keypad_control_enabled, false) ? "Yes" : "No");
+    response.print("\nPublish Keypad topic per entry: ");
+    response.print(_preferences->getBool(preference_keypad_topic_per_entry, false) ? "Yes" : "No");
+    response.print("\nPublish Keypad codes: ");
+    response.print(_preferences->getBool(preference_keypad_publish_code, false) ? "Yes" : "No");
+    response.print("\nMax keypad entries to retrieve: ");
+    response.print(_preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
+    response.print("\nPublish timecontrol info: ");
+    response.print(_preferences->getBool(preference_timecontrol_info_enabled, false) ? "Yes" : "No");
+    response.print("\nKeypad query interval (s): ");
+    response.print(_preferences->getInt(preference_query_interval_keypad, 1800));
+    response.print("\nEnable timecontrol control: ");
+    response.print(_preferences->getBool(preference_timecontrol_control_enabled, false) ? "Yes" : "No");
+    response.print("\nPublish timecontrol topic per entry: ");
+    response.print(_preferences->getBool(preference_timecontrol_topic_per_entry, false) ? "Yes" : "No");
+    response.print("\nMax timecontrol entries to retrieve: ");
+    response.print(_preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL));
+    response.print("\n\n------------ HOME ASSISTANT ------------");
+    response.print("\nHome Assistant auto discovery enabled: ");
     if(_preferences->getString(preference_mqtt_hass_discovery, "").length() > 0)
     {
-        _response.concat("Yes");
-        _response.concat("\nHome Assistant auto discovery topic: ");
-        _response.concat(_preferences->getString(preference_mqtt_hass_discovery, "") + "/");
-        _response.concat("\nNuki Hub configuration URL for HA: ");
-        _response.concat(_preferences->getString(preference_mqtt_hass_cu_url, "").length() > 0 ? _preferences->getString(preference_mqtt_hass_cu_url, "") : "http://" + _network->localIP());
+        response.print("Yes");
+        response.print("\nHome Assistant auto discovery topic: ");
+        response.print(_preferences->getString(preference_mqtt_hass_discovery, "") + "/");
+        response.print("\nNuki Hub configuration URL for HA: ");
+        response.print(_preferences->getString(preference_mqtt_hass_cu_url, "").length() > 0 ? _preferences->getString(preference_mqtt_hass_cu_url, "") : "http://" + _network->localIP());
     }
-    else _response.concat("No");
-    _response.concat("\n\n------------ NUKI LOCK ------------");
-    if(_nuki == nullptr || !_preferences->getBool(preference_lock_enabled, true)) _response.concat("\nLock enabled: No");
+    else response.print("No");
+    response.print("\n\n------------ NUKI LOCK ------------");
+    if(_nuki == nullptr || !_preferences->getBool(preference_lock_enabled, true)) response.print("\nLock enabled: No");
     else
     {
-        _response.concat("\nLock enabled: Yes");
-        _response.concat("\nPaired: ");
-        _response.concat(_nuki->isPaired() ? "Yes" : "No");
-        _response.concat("\nNuki Hub device ID: ");
-        _response.concat(_preferences->getUInt(preference_device_id_lock, 0));
-        _response.concat("\nNuki device ID: ");
-        _response.concat(_preferences->getUInt(preference_nuki_id_lock, 0) > 0 ? "***" : "Not set");
-        _response.concat("\nFirmware version: ");
-        _response.concat(_nuki->firmwareVersion().c_str());
-        _response.concat("\nHardware version: ");
-        _response.concat(_nuki->hardwareVersion().c_str());
-        _response.concat("\nValid PIN set: ");
-        _response.concat(_nuki->isPaired() ? _nuki->isPinValid() ? "Yes" : "No" : "-");
-        _response.concat("\nHas door sensor: ");
-        _response.concat(_nuki->hasDoorSensor() ? "Yes" : "No");
-        _response.concat("\nHas keypad: ");
-        _response.concat(_nuki->hasKeypad() ? "Yes" : "No");
+        response.print("\nLock enabled: Yes");
+        response.print("\nPaired: ");
+        response.print(_nuki->isPaired() ? "Yes" : "No");
+        response.print("\nNuki Hub device ID: ");
+        response.print(_preferences->getUInt(preference_device_id_lock, 0));
+        response.print("\nNuki device ID: ");
+        response.print(_preferences->getUInt(preference_nuki_id_lock, 0) > 0 ? "***" : "Not set");
+        response.print("\nFirmware version: ");
+        response.print(_nuki->firmwareVersion().c_str());
+        response.print("\nHardware version: ");
+        response.print(_nuki->hardwareVersion().c_str());
+        response.print("\nValid PIN set: ");
+        response.print(_nuki->isPaired() ? _nuki->isPinValid() ? "Yes" : "No" : "-");
+        response.print("\nHas door sensor: ");
+        response.print(_nuki->hasDoorSensor() ? "Yes" : "No");
+        response.print("\nHas keypad: ");
+        response.print(_nuki->hasKeypad() ? "Yes" : "No");
         if(_nuki->hasKeypad())
         {
-            _response.concat("\nKeypad highest entries count: ");
-            _response.concat(_preferences->getInt(preference_lock_max_keypad_code_count, 0));
+            response.print("\nKeypad highest entries count: ");
+            response.print(_preferences->getInt(preference_lock_max_keypad_code_count, 0));
         }
-        _response.concat("\nTimecontrol highest entries count: ");
-        _response.concat(_preferences->getInt(preference_lock_max_timecontrol_entry_count, 0));
-        _response.concat("\nRegister as: ");
-        _response.concat(_preferences->getBool(preference_register_as_app, false) ? "App" : "Bridge");
-        _response.concat("\n\n------------ HYBRID MODE ------------");
-        if(!_preferences->getBool(preference_official_hybrid, false)) _response.concat("\nHybrid mode enabled: No");
+        response.print("\nTimecontrol highest entries count: ");
+        response.print(_preferences->getInt(preference_lock_max_timecontrol_entry_count, 0));
+        response.print("\nRegister as: ");
+        response.print(_preferences->getBool(preference_register_as_app, false) ? "App" : "Bridge");
+        response.print("\n\n------------ HYBRID MODE ------------");
+        if(!_preferences->getBool(preference_official_hybrid, false)) response.print("\nHybrid mode enabled: No");
         else
         {
-            _response.concat("\nHybrid mode enabled: Yes");
-            _response.concat("\nHybrid mode connected: ");
-            _response.concat(_nuki->offConnected() ? "Yes": "No");
-            _response.concat("\nSending actions through official MQTT enabled: ");
-            _response.concat(_preferences->getBool(preference_official_hybrid_actions, false) ? "Yes" : "No");
+            response.print("\nHybrid mode enabled: Yes");
+            response.print("\nHybrid mode connected: ");
+            response.print(_nuki->offConnected() ? "Yes": "No");
+            response.print("\nSending actions through official MQTT enabled: ");
+            response.print(_preferences->getBool(preference_official_hybrid_actions, false) ? "Yes" : "No");
             /* NOT IMPLEMENTED (YET?)
             if(_preferences->getBool(preference_official_hybrid_actions, false))
             {
-                _response.concat("\nRetry actions through BLE enabled: ");
-                _response.concat(_preferences->getBool(preference_official_hybrid_retry, false) ? "Yes" : "No");
+                response.print("\nRetry actions through BLE enabled: ");
+                response.print(_preferences->getBool(preference_official_hybrid_retry, false) ? "Yes" : "No");
             }
             */
-            _response.concat("\nTime between status updates when official MQTT is offline (s): ");
-            _response.concat(_preferences->getInt(preference_query_interval_hybrid_lockstate, 600));
+            response.print("\nTime between status updates when official MQTT is offline (s): ");
+            response.print(_preferences->getInt(preference_query_interval_hybrid_lockstate, 600));
         }
         uint32_t basicLockConfigAclPrefs[16];
         _preferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs));
         uint32_t advancedLockConfigAclPrefs[22];
         _preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs));
-        _response.concat("\n\n------------ NUKI LOCK ACL ------------");
-        _response.concat("\nLock: ");
-        _response.concat((int)aclPrefs[0] ? "Allowed" : "Disallowed");
-        _response.concat("\nUnlock: ");
-        _response.concat((int)aclPrefs[1] ? "Allowed" : "Disallowed");
-        _response.concat("\nUnlatch: ");
-        _response.concat((int)aclPrefs[2] ? "Allowed" : "Disallowed");
-        _response.concat("\nLock N Go: ");
-        _response.concat((int)aclPrefs[3] ? "Allowed" : "Disallowed");
-        _response.concat("\nLock N Go Unlatch: ");
-        _response.concat((int)aclPrefs[4] ? "Allowed" : "Disallowed");
-        _response.concat("\nFull Lock: ");
-        _response.concat((int)aclPrefs[5] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 1: ");
-        _response.concat((int)aclPrefs[6] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 2: ");
-        _response.concat((int)aclPrefs[7] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 3: ");
-        _response.concat((int)aclPrefs[8] ? "Allowed" : "Disallowed");
-        _response.concat("\n\n------------ NUKI LOCK CONFIG ACL ------------");
-        _response.concat("\nName: ");
-        _response.concat((int)basicLockConfigAclPrefs[0] ? "Allowed" : "Disallowed");
-        _response.concat("\nLatitude: ");
-        _response.concat((int)basicLockConfigAclPrefs[1] ? "Allowed" : "Disallowed");
-        _response.concat("\nLongitude: ");
-        _response.concat((int)basicLockConfigAclPrefs[2] ? "Allowed" : "Disallowed");
-        _response.concat("\nAuto Unlatch: ");
-        _response.concat((int)basicLockConfigAclPrefs[3] ? "Allowed" : "Disallowed");
-        _response.concat("\nPairing enabled: ");
-        _response.concat((int)basicLockConfigAclPrefs[4] ? "Allowed" : "Disallowed");
-        _response.concat("\nButton enabled: ");
-        _response.concat((int)basicLockConfigAclPrefs[5] ? "Allowed" : "Disallowed");
-        _response.concat("\nLED flash enabled: ");
-        _response.concat((int)basicLockConfigAclPrefs[6] ? "Allowed" : "Disallowed");
-        _response.concat("\nLED brightness: ");
-        _response.concat((int)basicLockConfigAclPrefs[7] ? "Allowed" : "Disallowed");
-        _response.concat("\nTimezone offset: ");
-        _response.concat((int)basicLockConfigAclPrefs[8] ? "Allowed" : "Disallowed");
-        _response.concat("\nDST mode: ");
-        _response.concat((int)basicLockConfigAclPrefs[9] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 1: ");
-        _response.concat((int)basicLockConfigAclPrefs[10] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 2: ");
-        _response.concat((int)basicLockConfigAclPrefs[11] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 3: ");
-        _response.concat((int)basicLockConfigAclPrefs[12] ? "Allowed" : "Disallowed");
-        _response.concat("\nSingle Lock: ");
-        _response.concat((int)basicLockConfigAclPrefs[13] ? "Allowed" : "Disallowed");
-        _response.concat("\nAdvertising Mode: ");
-        _response.concat((int)basicLockConfigAclPrefs[14] ? "Allowed" : "Disallowed");
-        _response.concat("\nTimezone ID: ");
-        _response.concat((int)basicLockConfigAclPrefs[15] ? "Allowed" : "Disallowed");
-        _response.concat("\nUnlocked Position Offset Degrees: ");
-        _response.concat((int)advancedLockConfigAclPrefs[0] ? "Allowed" : "Disallowed");
-        _response.concat("\nLocked Position Offset Degrees: ");
-        _response.concat((int)advancedLockConfigAclPrefs[1] ? "Allowed" : "Disallowed");
-        _response.concat("\nSingle Locked Position Offset Degrees: ");
-        _response.concat((int)advancedLockConfigAclPrefs[2] ? "Allowed" : "Disallowed");
-        _response.concat("\nUnlocked To Locked Transition Offset Degrees: ");
-        _response.concat((int)advancedLockConfigAclPrefs[3] ? "Allowed" : "Disallowed");
-        _response.concat("\nLock n Go timeout: ");
-        _response.concat((int)advancedLockConfigAclPrefs[4] ? "Allowed" : "Disallowed");
-        _response.concat("\nSingle button press action: ");
-        _response.concat((int)advancedLockConfigAclPrefs[5] ? "Allowed" : "Disallowed");
-        _response.concat("\nDouble button press action: ");
-        _response.concat((int)advancedLockConfigAclPrefs[6] ? "Allowed" : "Disallowed");
-        _response.concat("\nDetached cylinder: ");
-        _response.concat((int)advancedLockConfigAclPrefs[7] ? "Allowed" : "Disallowed");
-        _response.concat("\nBattery type: ");
-        _response.concat((int)advancedLockConfigAclPrefs[8] ? "Allowed" : "Disallowed");
-        _response.concat("\nAutomatic battery type detection: ");
-        _response.concat((int)advancedLockConfigAclPrefs[9] ? "Allowed" : "Disallowed");
-        _response.concat("\nUnlatch duration: ");
-        _response.concat((int)advancedLockConfigAclPrefs[10] ? "Allowed" : "Disallowed");
-        _response.concat("\nAuto lock timeout: ");
-        _response.concat((int)advancedLockConfigAclPrefs[11] ? "Allowed" : "Disallowed");
-        _response.concat("\nAuto unlock disabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[12] ? "Allowed" : "Disallowed");
-        _response.concat("\nNightmode enabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[13] ? "Allowed" : "Disallowed");
-        _response.concat("\nNightmode start time: ");
-        _response.concat((int)advancedLockConfigAclPrefs[14] ? "Allowed" : "Disallowed");
-        _response.concat("\nNightmode end time: ");
-        _response.concat((int)advancedLockConfigAclPrefs[15] ? "Allowed" : "Disallowed");
-        _response.concat("\nNightmode auto lock enabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[16] ? "Allowed" : "Disallowed");
-        _response.concat("\nNightmode auto unlock disabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[17] ? "Allowed" : "Disallowed");
-        _response.concat("\nNightmode immediate lock on start: ");
-        _response.concat((int)advancedLockConfigAclPrefs[18] ? "Allowed" : "Disallowed");
-        _response.concat("\nAuto lock enabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[19] ? "Allowed" : "Disallowed");
-        _response.concat("\nImmediate auto lock enabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[20] ? "Allowed" : "Disallowed");
-        _response.concat("\nAuto update enabled: ");
-        _response.concat((int)advancedLockConfigAclPrefs[21] ? "Allowed" : "Disallowed");
+        response.print("\n\n------------ NUKI LOCK ACL ------------");
+        response.print("\nLock: ");
+        response.print((int)aclPrefs[0] ? "Allowed" : "Disallowed");
+        response.print("\nUnlock: ");
+        response.print((int)aclPrefs[1] ? "Allowed" : "Disallowed");
+        response.print("\nUnlatch: ");
+        response.print((int)aclPrefs[2] ? "Allowed" : "Disallowed");
+        response.print("\nLock N Go: ");
+        response.print((int)aclPrefs[3] ? "Allowed" : "Disallowed");
+        response.print("\nLock N Go Unlatch: ");
+        response.print((int)aclPrefs[4] ? "Allowed" : "Disallowed");
+        response.print("\nFull Lock: ");
+        response.print((int)aclPrefs[5] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 1: ");
+        response.print((int)aclPrefs[6] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 2: ");
+        response.print((int)aclPrefs[7] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 3: ");
+        response.print((int)aclPrefs[8] ? "Allowed" : "Disallowed");
+        response.print("\n\n------------ NUKI LOCK CONFIG ACL ------------");
+        response.print("\nName: ");
+        response.print((int)basicLockConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        response.print("\nLatitude: ");
+        response.print((int)basicLockConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        response.print("\nLongitude: ");
+        response.print((int)basicLockConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        response.print("\nAuto Unlatch: ");
+        response.print((int)basicLockConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        response.print("\nPairing enabled: ");
+        response.print((int)basicLockConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        response.print("\nButton enabled: ");
+        response.print((int)basicLockConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        response.print("\nLED flash enabled: ");
+        response.print((int)basicLockConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        response.print("\nLED brightness: ");
+        response.print((int)basicLockConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        response.print("\nTimezone offset: ");
+        response.print((int)basicLockConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        response.print("\nDST mode: ");
+        response.print((int)basicLockConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 1: ");
+        response.print((int)basicLockConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 2: ");
+        response.print((int)basicLockConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 3: ");
+        response.print((int)basicLockConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        response.print("\nSingle Lock: ");
+        response.print((int)basicLockConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        response.print("\nAdvertising Mode: ");
+        response.print((int)basicLockConfigAclPrefs[14] ? "Allowed" : "Disallowed");
+        response.print("\nTimezone ID: ");
+        response.print((int)basicLockConfigAclPrefs[15] ? "Allowed" : "Disallowed");
+        response.print("\nUnlocked Position Offset Degrees: ");
+        response.print((int)advancedLockConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        response.print("\nLocked Position Offset Degrees: ");
+        response.print((int)advancedLockConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        response.print("\nSingle Locked Position Offset Degrees: ");
+        response.print((int)advancedLockConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        response.print("\nUnlocked To Locked Transition Offset Degrees: ");
+        response.print((int)advancedLockConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        response.print("\nLock n Go timeout: ");
+        response.print((int)advancedLockConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        response.print("\nSingle button press action: ");
+        response.print((int)advancedLockConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        response.print("\nDouble button press action: ");
+        response.print((int)advancedLockConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        response.print("\nDetached cylinder: ");
+        response.print((int)advancedLockConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        response.print("\nBattery type: ");
+        response.print((int)advancedLockConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        response.print("\nAutomatic battery type detection: ");
+        response.print((int)advancedLockConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        response.print("\nUnlatch duration: ");
+        response.print((int)advancedLockConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        response.print("\nAuto lock timeout: ");
+        response.print((int)advancedLockConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        response.print("\nAuto unlock disabled: ");
+        response.print((int)advancedLockConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        response.print("\nNightmode enabled: ");
+        response.print((int)advancedLockConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        response.print("\nNightmode start time: ");
+        response.print((int)advancedLockConfigAclPrefs[14] ? "Allowed" : "Disallowed");
+        response.print("\nNightmode end time: ");
+        response.print((int)advancedLockConfigAclPrefs[15] ? "Allowed" : "Disallowed");
+        response.print("\nNightmode auto lock enabled: ");
+        response.print((int)advancedLockConfigAclPrefs[16] ? "Allowed" : "Disallowed");
+        response.print("\nNightmode auto unlock disabled: ");
+        response.print((int)advancedLockConfigAclPrefs[17] ? "Allowed" : "Disallowed");
+        response.print("\nNightmode immediate lock on start: ");
+        response.print((int)advancedLockConfigAclPrefs[18] ? "Allowed" : "Disallowed");
+        response.print("\nAuto lock enabled: ");
+        response.print((int)advancedLockConfigAclPrefs[19] ? "Allowed" : "Disallowed");
+        response.print("\nImmediate auto lock enabled: ");
+        response.print((int)advancedLockConfigAclPrefs[20] ? "Allowed" : "Disallowed");
+        response.print("\nAuto update enabled: ");
+        response.print((int)advancedLockConfigAclPrefs[21] ? "Allowed" : "Disallowed");
 
         if(_preferences->getBool(preference_show_secrets))
         {
@@ -3543,151 +3561,151 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
             nukiBlePref.getBytes("secretKeyK", secretKeyK, 32);
             nukiBlePref.getBytes("authorizationId", authorizationId, 4);
             nukiBlePref.end();
-            _response.concat("\n\n------------ NUKI LOCK PAIRING ------------");
-            _response.concat("\nBLE Address: ");
+            response.print("\n\n------------ NUKI LOCK PAIRING ------------");
+            response.print("\nBLE Address: ");
             for (int i = 0; i < 6; i++)
             {
                 sprintf(tmp, "%02x", currentBleAddress[i]);
-                _response.concat(tmp);
+                response.print(tmp);
             }
-            _response.concat("\nSecretKeyK: ");
+            response.print("\nSecretKeyK: ");
             for (int i = 0; i < 32; i++)
             {
                 sprintf(tmp, "%02x", secretKeyK[i]);
-                _response.concat(tmp);
+                response.print(tmp);
             }
-            _response.concat("\nAuthorizationId: ");
+            response.print("\nAuthorizationId: ");
             for (int i = 0; i < 4; i++)
             {
                 sprintf(tmp, "%02x", authorizationId[i]);
-                _response.concat(tmp);
+                response.print(tmp);
             }
             uint32_t authorizationIdInt = authorizationId[0] + 256U*authorizationId[1] + 65536U*authorizationId[2] + 16777216U*authorizationId[3];
-            _response.concat("\nAuthorizationId (UINT32_T): ");
-            _response.concat(authorizationIdInt);
+            response.print("\nAuthorizationId (UINT32_T): ");
+            response.print(authorizationIdInt);
         }
     }
 
-    _response.concat("\n\n------------ NUKI OPENER ------------");
-    if(_nukiOpener == nullptr || !_preferences->getBool(preference_opener_enabled, false)) _response.concat("\nOpener enabled: No");
+    response.print("\n\n------------ NUKI OPENER ------------");
+    if(_nukiOpener == nullptr || !_preferences->getBool(preference_opener_enabled, false)) response.print("\nOpener enabled: No");
     else
     {
-        _response.concat("\nOpener enabled: Yes");
-        _response.concat("\nPaired: ");
-        _response.concat(_nukiOpener->isPaired() ? "Yes" : "No");
-        _response.concat("\nNuki Hub device ID: ");
-        _response.concat(_preferences->getUInt(preference_device_id_opener, 0));
-        _response.concat("\nNuki device ID: ");
-        _response.concat(_preferences->getUInt(preference_nuki_id_opener, 0) > 0 ? "***" : "Not set");
-        _response.concat("\nFirmware version: ");
-        _response.concat(_nukiOpener->firmwareVersion().c_str());
-        _response.concat("\nHardware version: ");
-        _response.concat(_nukiOpener->hardwareVersion().c_str());
-        _response.concat("\nOpener valid PIN set: ");
-        _response.concat(_nukiOpener->isPaired() ? _nukiOpener->isPinValid() ? "Yes" : "No" : "-");
-        _response.concat("\nOpener has keypad: ");
-        _response.concat(_nukiOpener->hasKeypad() ? "Yes" : "No");
+        response.print("\nOpener enabled: Yes");
+        response.print("\nPaired: ");
+        response.print(_nukiOpener->isPaired() ? "Yes" : "No");
+        response.print("\nNuki Hub device ID: ");
+        response.print(_preferences->getUInt(preference_device_id_opener, 0));
+        response.print("\nNuki device ID: ");
+        response.print(_preferences->getUInt(preference_nuki_id_opener, 0) > 0 ? "***" : "Not set");
+        response.print("\nFirmware version: ");
+        response.print(_nukiOpener->firmwareVersion().c_str());
+        response.print("\nHardware version: ");
+        response.print(_nukiOpener->hardwareVersion().c_str());
+        response.print("\nOpener valid PIN set: ");
+        response.print(_nukiOpener->isPaired() ? _nukiOpener->isPinValid() ? "Yes" : "No" : "-");
+        response.print("\nOpener has keypad: ");
+        response.print(_nukiOpener->hasKeypad() ? "Yes" : "No");
         if(_nuki->hasKeypad())
         {
-            _response.concat("\nKeypad highest entries count: ");
-            _response.concat(_preferences->getInt(preference_opener_max_keypad_code_count, 0));
+            response.print("\nKeypad highest entries count: ");
+            response.print(_preferences->getInt(preference_opener_max_keypad_code_count, 0));
         }
-        _response.concat("\nTimecontrol highest entries count: ");
-        _response.concat(_preferences->getInt(preference_opener_max_timecontrol_entry_count, 0));
-        _response.concat("\nRegister as: ");
-        _response.concat(_preferences->getBool(preference_register_opener_as_app, false) ? "App" : "Bridge");
-        _response.concat("\nNuki Opener Lock/Unlock action set to Continuous mode in Home Assistant: ");
-        _response.concat(_preferences->getBool(preference_opener_continuous_mode, false) ? "Yes" : "No");
+        response.print("\nTimecontrol highest entries count: ");
+        response.print(_preferences->getInt(preference_opener_max_timecontrol_entry_count, 0));
+        response.print("\nRegister as: ");
+        response.print(_preferences->getBool(preference_register_opener_as_app, false) ? "App" : "Bridge");
+        response.print("\nNuki Opener Lock/Unlock action set to Continuous mode in Home Assistant: ");
+        response.print(_preferences->getBool(preference_opener_continuous_mode, false) ? "Yes" : "No");
         uint32_t basicOpenerConfigAclPrefs[14];
         _preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs));
         uint32_t advancedOpenerConfigAclPrefs[20];
         _preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs));
-        _response.concat("\n\n------------ NUKI OPENER ACL ------------");
-        _response.concat("\nActivate Ring-to-Open: ");
-        _response.concat((int)aclPrefs[9] ? "Allowed" : "Disallowed");
-        _response.concat("\nDeactivate Ring-to-Open: ");
-        _response.concat((int)aclPrefs[10] ? "Allowed" : "Disallowed");
-        _response.concat("\nElectric Strike Actuation: ");
-        _response.concat((int)aclPrefs[11] ? "Allowed" : "Disallowed");
-        _response.concat("\nActivate Continuous Mode: ");
-        _response.concat((int)aclPrefs[12] ? "Allowed" : "Disallowed");
-        _response.concat("\nDeactivate Continuous Mode: ");
-        _response.concat((int)aclPrefs[13] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 1: ");
-        _response.concat((int)aclPrefs[14] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 2: ");
-        _response.concat((int)aclPrefs[15] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 3: ");
-        _response.concat((int)aclPrefs[16] ? "Allowed" : "Disallowed");
-        _response.concat("\n\n------------ NUKI OPENER CONFIG ACL ------------");
-        _response.concat("\nName: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[0] ? "Allowed" : "Disallowed");
-        _response.concat("\nLatitude: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[1] ? "Allowed" : "Disallowed");
-        _response.concat("\nLongitude: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[2] ? "Allowed" : "Disallowed");
-        _response.concat("\nPairing enabled: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[3] ? "Allowed" : "Disallowed");
-        _response.concat("\nButton enabled: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[4] ? "Allowed" : "Disallowed");
-        _response.concat("\nLED flash enabled: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[5] ? "Allowed" : "Disallowed");
-        _response.concat("\nTimezone offset: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[6] ? "Allowed" : "Disallowed");
-        _response.concat("\nDST mode: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[7] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 1: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[8] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 2: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[9] ? "Allowed" : "Disallowed");
-        _response.concat("\nFob Action 3: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[10] ? "Allowed" : "Disallowed");
-        _response.concat("\nOperating Mode: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[11] ? "Allowed" : "Disallowed");
-        _response.concat("\nAdvertising Mode: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[12] ? "Allowed" : "Disallowed");
-        _response.concat("\nTimezone ID: ");
-        _response.concat((int)basicOpenerConfigAclPrefs[13] ? "Allowed" : "Disallowed");
-        _response.concat("\nIntercom ID: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[0] ? "Allowed" : "Disallowed");
-        _response.concat("\nBUS mode Switch: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[1] ? "Allowed" : "Disallowed");
-        _response.concat("\nShort Circuit Duration: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[2] ? "Allowed" : "Disallowed");
-        _response.concat("\nEletric Strike Delay: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[3] ? "Allowed" : "Disallowed");
-        _response.concat("\nRandom Electric Strike Delay: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[4] ? "Allowed" : "Disallowed");
-        _response.concat("\nElectric Strike Duration: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[5] ? "Allowed" : "Disallowed");
-        _response.concat("\nDisable RTO after ring: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[6] ? "Allowed" : "Disallowed");
-        _response.concat("\nRTO timeout: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[7] ? "Allowed" : "Disallowed");
-        _response.concat("\nDoorbell suppression: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[8] ? "Allowed" : "Disallowed");
-        _response.concat("\nDoorbell suppression duration: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[9] ? "Allowed" : "Disallowed");
-        _response.concat("\nSound Ring: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[10] ? "Allowed" : "Disallowed");
-        _response.concat("\nSound Open: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[11] ? "Allowed" : "Disallowed");
-        _response.concat("\nSound RTO: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[12] ? "Allowed" : "Disallowed");
-        _response.concat("\nSound CM: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[13] ? "Allowed" : "Disallowed");
-        _response.concat("\nSound confirmation: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[14] ? "Allowed" : "Disallowed");
-        _response.concat("\nSound level: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[15] ? "Allowed" : "Disallowed");
-        _response.concat("\nSingle button press action: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[16] ? "Allowed" : "Disallowed");
-        _response.concat("\nDouble button press action: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[17] ? "Allowed" : "Disallowed");
-        _response.concat("\nBattery type: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[18] ? "Allowed" : "Disallowed");
-        _response.concat("\nAutomatic battery type detection: ");
-        _response.concat((int)advancedOpenerConfigAclPrefs[19] ? "Allowed" : "Disallowed");
+        response.print("\n\n------------ NUKI OPENER ACL ------------");
+        response.print("\nActivate Ring-to-Open: ");
+        response.print((int)aclPrefs[9] ? "Allowed" : "Disallowed");
+        response.print("\nDeactivate Ring-to-Open: ");
+        response.print((int)aclPrefs[10] ? "Allowed" : "Disallowed");
+        response.print("\nElectric Strike Actuation: ");
+        response.print((int)aclPrefs[11] ? "Allowed" : "Disallowed");
+        response.print("\nActivate Continuous Mode: ");
+        response.print((int)aclPrefs[12] ? "Allowed" : "Disallowed");
+        response.print("\nDeactivate Continuous Mode: ");
+        response.print((int)aclPrefs[13] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 1: ");
+        response.print((int)aclPrefs[14] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 2: ");
+        response.print((int)aclPrefs[15] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 3: ");
+        response.print((int)aclPrefs[16] ? "Allowed" : "Disallowed");
+        response.print("\n\n------------ NUKI OPENER CONFIG ACL ------------");
+        response.print("\nName: ");
+        response.print((int)basicOpenerConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        response.print("\nLatitude: ");
+        response.print((int)basicOpenerConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        response.print("\nLongitude: ");
+        response.print((int)basicOpenerConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        response.print("\nPairing enabled: ");
+        response.print((int)basicOpenerConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        response.print("\nButton enabled: ");
+        response.print((int)basicOpenerConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        response.print("\nLED flash enabled: ");
+        response.print((int)basicOpenerConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        response.print("\nTimezone offset: ");
+        response.print((int)basicOpenerConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        response.print("\nDST mode: ");
+        response.print((int)basicOpenerConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 1: ");
+        response.print((int)basicOpenerConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 2: ");
+        response.print((int)basicOpenerConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        response.print("\nFob Action 3: ");
+        response.print((int)basicOpenerConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        response.print("\nOperating Mode: ");
+        response.print((int)basicOpenerConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        response.print("\nAdvertising Mode: ");
+        response.print((int)basicOpenerConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        response.print("\nTimezone ID: ");
+        response.print((int)basicOpenerConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        response.print("\nIntercom ID: ");
+        response.print((int)advancedOpenerConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        response.print("\nBUS mode Switch: ");
+        response.print((int)advancedOpenerConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        response.print("\nShort Circuit Duration: ");
+        response.print((int)advancedOpenerConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        response.print("\nEletric Strike Delay: ");
+        response.print((int)advancedOpenerConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        response.print("\nRandom Electric Strike Delay: ");
+        response.print((int)advancedOpenerConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        response.print("\nElectric Strike Duration: ");
+        response.print((int)advancedOpenerConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        response.print("\nDisable RTO after ring: ");
+        response.print((int)advancedOpenerConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        response.print("\nRTO timeout: ");
+        response.print((int)advancedOpenerConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        response.print("\nDoorbell suppression: ");
+        response.print((int)advancedOpenerConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        response.print("\nDoorbell suppression duration: ");
+        response.print((int)advancedOpenerConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        response.print("\nSound Ring: ");
+        response.print((int)advancedOpenerConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        response.print("\nSound Open: ");
+        response.print((int)advancedOpenerConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        response.print("\nSound RTO: ");
+        response.print((int)advancedOpenerConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        response.print("\nSound CM: ");
+        response.print((int)advancedOpenerConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        response.print("\nSound confirmation: ");
+        response.print((int)advancedOpenerConfigAclPrefs[14] ? "Allowed" : "Disallowed");
+        response.print("\nSound level: ");
+        response.print((int)advancedOpenerConfigAclPrefs[15] ? "Allowed" : "Disallowed");
+        response.print("\nSingle button press action: ");
+        response.print((int)advancedOpenerConfigAclPrefs[16] ? "Allowed" : "Disallowed");
+        response.print("\nDouble button press action: ");
+        response.print((int)advancedOpenerConfigAclPrefs[17] ? "Allowed" : "Disallowed");
+        response.print("\nBattery type: ");
+        response.print((int)advancedOpenerConfigAclPrefs[18] ? "Allowed" : "Disallowed");
+        response.print("\nAutomatic battery type detection: ");
+        response.print((int)advancedOpenerConfigAclPrefs[19] ? "Allowed" : "Disallowed");
         if(_preferences->getBool(preference_show_secrets))
         {
             char tmp[16];
@@ -3700,52 +3718,51 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
             nukiBlePref.getBytes("secretKeyK", secretKeyKOpn, 32);
             nukiBlePref.getBytes("authorizationId", authorizationIdOpn, 4);
             nukiBlePref.end();
-            _response.concat("\n\n------------ NUKI OPENER PAIRING ------------");
-            _response.concat("\nBLE Address: ");
+            response.print("\n\n------------ NUKI OPENER PAIRING ------------");
+            response.print("\nBLE Address: ");
             for (int i = 0; i < 6; i++)
             {
                 sprintf(tmp, "%02x", currentBleAddressOpn[i]);
-                _response.concat(tmp);
+                response.print(tmp);
             }
-            _response.concat("\nSecretKeyK: ");
+            response.print("\nSecretKeyK: ");
             for (int i = 0; i < 32; i++)
             {
                 sprintf(tmp, "%02x", secretKeyKOpn[i]);
-                _response.concat(tmp);
+                response.print(tmp);
             }
-            _response.concat("\nAuthorizationId: ");
+            response.print("\nAuthorizationId: ");
             for (int i = 0; i < 4; i++)
             {
                 sprintf(tmp, "%02x", authorizationIdOpn[i]);
-                _response.concat(tmp);
+                response.print(tmp);
             }
         }
     }
 
-    _response.concat("\n\n------------ GPIO ------------\n");
+    response.print("\n\n------------ GPIO ------------\n");
     String gpioStr = "";
     _gpio->getConfigurationText(gpioStr, _gpio->pinConfiguration());
-    _response.concat(gpioStr);
-    _response.concat("
"); - sendResponse(request); + response.print(gpioStr); + response.print("
"); + return response.endSend(); } -void WebCfgServer::processUnpair(AsyncWebServerRequest *request, bool opener) +esp_err_t WebCfgServer::processUnpair(PsychicRequest *request, bool opener) { String value = ""; - if(request->hasParam("CONFIRMTOKEN", true)) + if(request->hasParam("CONFIRMTOKEN")) { - const AsyncWebParameter* p = request->getParam("CONFIRMTOKEN", true); + const PsychicWebParameter* p = request->getParam("CONFIRMTOKEN"); if(p->value() != "") value = p->value(); } if(value != _confirmCode) { - buildConfirmHtml(request, "Confirm code is invalid.", 3, true); - return; + return buildConfirmHtml(request, "Confirm code is invalid.", 3, true); } - buildConfirmHtml(request, opener ? "Unpairing Nuki Opener and restarting." : "Unpairing Nuki Lock and restarting.", 3, true); + esp_err_t res = buildConfirmHtml(request, opener ? "Unpairing Nuki Opener and restarting." : "Unpairing Nuki Lock and restarting.", 3, true); if(!opener && _nuki != nullptr) { @@ -3759,34 +3776,35 @@ void WebCfgServer::processUnpair(AsyncWebServerRequest *request, bool opener) } waitAndProcess(false, 1000); restartEsp(RestartReason::DeviceUnpaired); + return res; } -void WebCfgServer::processUpdate(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::processUpdate(PsychicRequest *request) { + esp_err_t res; String value = ""; if(request->hasParam("token")) { - const AsyncWebParameter* p = request->getParam("token"); + const PsychicWebParameter* p = request->getParam("token"); if(p->value() != "") value = p->value(); } if(value != _confirmCode) { - buildConfirmHtml(request, "Confirm code is invalid.", 3, true); - return; + return buildConfirmHtml(request, "Confirm code is invalid.", 3, true); } if(request->hasParam("beta")) { if(request->hasParam("debug")) { - buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG BETA version", 2, true); + res = buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG BETA version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL_DBG); _preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL_DBG); } else { - buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest BETA version", 2, true); + res = buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest BETA version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL); } @@ -3795,13 +3813,13 @@ void WebCfgServer::processUpdate(AsyncWebServerRequest *request) { if(request->hasParam("debug")) { - buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG DEVELOPMENT version", 2, true); + res = buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG DEVELOPMENT version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL_DBG); _preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL_DBG); } else { - buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEVELOPMENT version", 2, true); + res = buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEVELOPMENT version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL); } @@ -3810,53 +3828,54 @@ void WebCfgServer::processUpdate(AsyncWebServerRequest *request) { if(request->hasParam("debug")) { - buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG RELEASE version", 2, true); + res = buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG RELEASE version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL_DBG); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_UPDATER_BINARY_URL_DBG); } else { - buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest RELEASE version", 2, true); + res = buildConfirmHtml(request, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest RELEASE version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); } } waitAndProcess(true, 1000); restartEsp(RestartReason::OTAReboot); + return res; } -void WebCfgServer::processFactoryReset(AsyncWebServerRequest *request) +esp_err_t WebCfgServer::processFactoryReset(PsychicRequest *request) { + esp_err_t res; String value = ""; - if(request->hasParam("CONFIRMTOKEN", true)) + if(request->hasParam("CONFIRMTOKEN")) { - const AsyncWebParameter* p = request->getParam("CONFIRMTOKEN", true); + const PsychicWebParameter* p = request->getParam("CONFIRMTOKEN"); if(p->value() != "") value = p->value(); } bool resetWifi = false; if(value.length() == 0 || value != _confirmCode) { - buildConfirmHtml(request, "Confirm code is invalid.", 3, true); - return; + return buildConfirmHtml(request, "Confirm code is invalid.", 3, true); } else { String value2 = ""; - if(request->hasParam("WIFI", true)) + if(request->hasParam("WIFI")) { - const AsyncWebParameter* p = request->getParam("WIFI", true); + const PsychicWebParameter* p = request->getParam("WIFI"); if(p->value() != "") value = p->value(); } if(value2 == "1") { resetWifi = true; - buildConfirmHtml(request, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener and resetting WiFi.", 3, true); + res = buildConfirmHtml(request, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener and resetting WiFi.", 3, true); } else { - buildConfirmHtml(request, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener.", 3, true); + res = buildConfirmHtml(request, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener.", 3, true); } } @@ -3889,9 +3908,11 @@ void WebCfgServer::processFactoryReset(AsyncWebServerRequest *request) waitAndProcess(false, 3000); restartEsp(RestartReason::NukiHubReset); + return res; } -void WebCfgServer::printInputField(const char *token, +void WebCfgServer::printInputField(PsychicStreamResponse *response, + const char *token, const char *description, const char *value, const size_t& maxLength, @@ -3903,38 +3924,39 @@ void WebCfgServer::printInputField(const char *token, itoa(maxLength, maxLengthStr, 10); - _response.concat(""); - _response.concat(description); + response->print(""); + response->print(description); if(showLengthRestriction) { - _response.concat(" (Max. "); - _response.concat(maxLength); - _response.concat(" characters)"); + response->print(" (Max. "); + response->print(maxLength); + response->print(" characters)"); } - _response.concat(""); - _response.concat("print(""); + response->print("print(" "); + response->print(args); } if(strcmp(value, "") != 0) { - _response.concat(" value=\""); - _response.concat(value); + response->print(" value=\""); + response->print(value); } - _response.concat("\" name=\""); - _response.concat(token); - _response.concat("\" size=\"25\" maxlength=\""); - _response.concat(maxLengthStr); - _response.concat("\"/>"); - _response.concat(""); + response->print("\" name=\""); + response->print(token); + response->print("\" size=\"25\" maxlength=\""); + response->print(maxLengthStr); + response->print("\"/>"); + response->print(""); } -void WebCfgServer::printInputField(const char *token, +void WebCfgServer::printInputField(PsychicStreamResponse *response, + const char *token, const char *description, const int value, size_t maxLength, @@ -3942,32 +3964,33 @@ void WebCfgServer::printInputField(const char *token, { char valueStr[20]; itoa(value, valueStr, 10); - printInputField(token, description, valueStr, maxLength, args); + printInputField(response, token, description, valueStr, maxLength, args); } -void WebCfgServer::printCheckBox(const char *token, const char *description, const bool value, const char *htmlClass) +void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass) { - _response.concat(""); - _response.concat(description); - _response.concat(""); + response->print(""); + response->print(description); + response->print(""); - _response.concat(""); + response->print("print(token); + response->print("\" value=\"0\""); + response->print("/>"); - _response.concat("print("print(token); - _response.concat("\" class=\""); - _response.concat(htmlClass); + response->print("\" class=\""); + response->print(htmlClass); - _response.concat("\" value=\"1\""); - _response.concat(value ? " checked=\"checked\"" : ""); - _response.concat("/>"); + response->print("\" value=\"1\""); + response->print(value ? " checked=\"checked\"" : ""); + response->print("/>"); } -void WebCfgServer::printTextarea(const char *token, +void WebCfgServer::printTextarea(PsychicStreamResponse *response, + const char *token, const char *description, const char *value, const size_t& maxLength, @@ -3978,106 +4001,106 @@ void WebCfgServer::printTextarea(const char *token, itoa(maxLength, maxLengthStr, 10); - _response.concat(""); - _response.concat(description); + response->print(""); + response->print(description); if(showLengthRestriction) { - _response.concat(" (Max. "); - _response.concat(maxLength); - _response.concat(" characters)"); + response->print(" (Max. "); + response->print(maxLength); + response->print(" characters)"); } - _response.concat(""); - _response.concat(" "); - _response.concat(""); + response->print(" name=\""); + response->print(token); + response->print("\" maxlength=\""); + response->print(maxLengthStr); + response->print("\">"); + response->print(value); + response->print(""); + response->print(""); } -void WebCfgServer::printDropDown(const char *token, const char *description, const String preselectedValue, const std::vector> options, const String className) +void WebCfgServer::printDropDown(PsychicStreamResponse *response, const char *token, const char *description, const String preselectedValue, const std::vector> options, const String className) { - _response.concat(""); - _response.concat(description); - _response.concat(""); + response->print(""); + response->print(description); + response->print(""); - if(className.length() > 0) _response.concat("print(""); - _response.concat(""); + response->print(""); + response->print(""); } -void WebCfgServer::buildNavigationButton(const char *caption, const char *targetPath, const char* labelText) +void WebCfgServer::buildNavigationButton(PsychicStreamResponse *response, const char *caption, const char *targetPath, const char* labelText) { - _response.concat("
"); - _response.concat(" "); - _response.concat(labelText); - _response.concat("
"); + response->print("
print(targetPath); + response->print("\">"); + response->print(" "); + response->print(labelText); + response->print("
"); } -void WebCfgServer::buildNavigationMenuEntry(const char *title, const char *targetPath, const char* warningMessage) +void WebCfgServer::buildNavigationMenuEntry(PsychicStreamResponse *response, const char *title, const char *targetPath, const char* warningMessage) { - _response.concat(""); - _response.concat("
  • "); - _response.concat(title); + response->print("print(targetPath); + response->print("\">"); + response->print("
  • "); + response->print(title); if(strcmp(warningMessage, "") != 0){ - _response.concat(""); - _response.concat(warningMessage); - _response.concat(""); + response->print(""); + response->print(warningMessage); + response->print(""); } - _response.concat("
  • "); + response->print(""); } -void WebCfgServer::printParameter(const char *description, const char *value, const char *link, const char *id) +void WebCfgServer::printParameter(PsychicStreamResponse *response, const char *description, const char *value, const char *link, const char *id) { - _response.concat(""); - _response.concat(""); - _response.concat(description); - _response.concat(""); - if(strcmp(id, "") == 0) _response.concat(""); + response->print(""); + response->print(""); + response->print(description); + response->print(""); + if(strcmp(id, "") == 0) response->print(""); else { - _response.concat(""); + response->print("print(id); + response->print("\">"); } - if(strcmp(link, "") == 0) _response.concat(value); + if(strcmp(link, "") == 0) response->print(value); else { - _response.concat(" "); - _response.concat(value); - _response.concat(""); + response->print("print(link); + response->print("\"> "); + response->print(value); + response->print(""); } - _response.concat(""); - _response.concat(""); + response->print(""); + response->print(""); } diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index 583175d..4eb30a8 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -1,9 +1,8 @@ #pragma once #include -#include -#include -#include +#include +#include #include "esp_ota_ops.h" #include "Config.h" @@ -37,9 +36,9 @@ class WebCfgServer { public: #ifndef NUKI_HUB_UPDATER - WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, AsyncWebServer* asyncServer); + WebCfgServer(NukiWrapper* nuki, NukiOpenerWrapper* nukiOpener, NukiNetwork* network, Gpio* gpio, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer); #else - WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, AsyncWebServer* asyncServer); + WebCfgServer(NukiNetwork* network, Preferences* preferences, bool allowRestartToPortal, uint8_t partitionType, PsychicHttpServer* psychicServer); #endif ~WebCfgServer() = default; @@ -47,34 +46,34 @@ public: private: #ifndef NUKI_HUB_UPDATER - void sendSettings(AsyncWebServerRequest *request); - bool processArgs(AsyncWebServerRequest *request, String& message); - bool processImport(AsyncWebServerRequest *request, String& message); - void processGpioArgs(AsyncWebServerRequest *request); - void buildHtml(AsyncWebServerRequest *request); - void buildAccLvlHtml(AsyncWebServerRequest *request); - void buildCredHtml(AsyncWebServerRequest *request); - void buildImportExportHtml(AsyncWebServerRequest *request); - void buildMqttConfigHtml(AsyncWebServerRequest *request); - void buildStatusHtml(AsyncWebServerRequest *request); - void buildAdvancedConfigHtml(AsyncWebServerRequest *request); - void buildNukiConfigHtml(AsyncWebServerRequest *request); - void buildGpioConfigHtml(AsyncWebServerRequest *request); + esp_err_t sendSettings(PsychicRequest *request); + bool processArgs(PsychicRequest *request, String& message); + bool processImport(PsychicRequest *request, String& message); + void processGpioArgs(PsychicRequest *request); + esp_err_t buildHtml(PsychicRequest *request); + esp_err_t buildAccLvlHtml(PsychicRequest *request); + esp_err_t buildCredHtml(PsychicRequest *request); + esp_err_t buildImportExportHtml(PsychicRequest *request); + esp_err_t buildMqttConfigHtml(PsychicRequest *request); + esp_err_t buildStatusHtml(PsychicRequest *request); + esp_err_t buildAdvancedConfigHtml(PsychicRequest *request); + esp_err_t buildNukiConfigHtml(PsychicRequest *request); + esp_err_t buildGpioConfigHtml(PsychicRequest *request); #ifndef CONFIG_IDF_TARGET_ESP32H2 - void buildConfigureWifiHtml(AsyncWebServerRequest *request); + esp_err_t buildConfigureWifiHtml(PsychicRequest *request); #endif - void buildInfoHtml(AsyncWebServerRequest *request); - void buildCustomNetworkConfigHtml(AsyncWebServerRequest *request); - void processUnpair(AsyncWebServerRequest *request, bool opener); - void processUpdate(AsyncWebServerRequest *request); - void processFactoryReset(AsyncWebServerRequest *request); - void printInputField(const char* token, const char* description, const char* value, const size_t& maxLength, const char* args, const bool& isPassword = false, const bool& showLengthRestriction = false); - void printInputField(const char* token, const char* description, const int value, size_t maxLength, const char* args); - void printCheckBox(const char* token, const char* description, const bool value, const char* htmlClass); - void printTextarea(const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false); - void printDropDown(const char *token, const char *description, const String preselectedValue, std::vector> options, const String className); - void buildNavigationButton(const char* caption, const char* targetPath, const char* labelText = ""); - void buildNavigationMenuEntry(const char *title, const char *targetPath, const char* warningMessage = ""); + esp_err_t buildInfoHtml(PsychicRequest *request); + esp_err_t buildCustomNetworkConfigHtml(PsychicRequest *request); + esp_err_t processUnpair(PsychicRequest *request, bool opener); + esp_err_t processUpdate(PsychicRequest *request); + esp_err_t processFactoryReset(PsychicRequest *request); + void printInputField(PsychicStreamResponse *response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* args, const bool& isPassword = false, const bool& showLengthRestriction = false); + void printInputField(PsychicStreamResponse *response, const char* token, const char* description, const int value, size_t maxLength, const char* args); + void printCheckBox(PsychicStreamResponse *response, const char* token, const char* description, const bool value, const char* htmlClass); + void printTextarea(PsychicStreamResponse *response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false); + void printDropDown(PsychicStreamResponse *response, const char *token, const char *description, const String preselectedValue, std::vector> options, const String className); + void buildNavigationButton(PsychicStreamResponse *response, const char* caption, const char* targetPath, const char* labelText = ""); + void buildNavigationMenuEntry(PsychicStreamResponse *response, const char *title, const char *targetPath, const char* warningMessage = ""); const std::vector> getNetworkDetectionOptions() const; const std::vector> getGpioOptions() const; @@ -86,8 +85,8 @@ private: String getPreselectionForGpio(const uint8_t& pin); String pinStateToString(uint8_t value); - void printParameter(const char* description, const char* value, const char *link = "", const char *id = ""); - + void printParameter(PsychicStreamResponse *response, const char* description, const char* value, const char *link = "", const char *id = ""); + NukiWrapper* _nuki = nullptr; NukiOpenerWrapper* _nukiOpener = nullptr; Gpio* _gpio = nullptr; @@ -96,21 +95,19 @@ private: bool _rebootRequired = false; #endif - String _response; String generateConfirmCode(); String _confirmCode = "----"; - void buildConfirmHtml(AsyncWebServerRequest *request, const String &message, uint32_t redirectDelay = 5, bool redirect = false); - void buildOtaHtml(AsyncWebServerRequest *request, bool debug = false); - void buildOtaCompletedHtml(AsyncWebServerRequest *request); - void sendCss(AsyncWebServerRequest *request); - void sendFavicon(AsyncWebServerRequest *request); - void buildHtmlHeader(String additionalHeader = ""); + esp_err_t buildConfirmHtml(PsychicRequest *request, const String &message, uint32_t redirectDelay = 5, bool redirect = false); + esp_err_t buildOtaHtml(PsychicRequest *request, bool debug = false); + esp_err_t buildOtaCompletedHtml(PsychicRequest *request); + esp_err_t sendCss(PsychicRequest *request); + esp_err_t sendFavicon(PsychicRequest *request); + void buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader = ""); void waitAndProcess(const bool blocking, const uint32_t duration); - void handleOtaUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); + void handleOtaUpload(PsychicRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); void printProgress(size_t prg, size_t sz); - void sendResponse(AsyncWebServerRequest *request); - - AsyncWebServer* _asyncServer = nullptr; + + PsychicHttpServer* _psychicServer = nullptr; NukiNetwork* _network = nullptr; Preferences* _preferences = nullptr; diff --git a/src/main.cpp b/src/main.cpp index e1ae161..2cd32ca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,11 +19,10 @@ #include "Logger.h" #include "PreferencesKeys.h" #include "RestartReason.h" -#include -#include -#include +#ifdef DEBUG_NUKIHUB #include #include +#endif char log_print_buffer[1024]; @@ -54,7 +53,7 @@ int64_t restartTs = 10 * 1000 * 60000; #endif -AsyncWebServer* asyncServer = nullptr; +PsychicHttpServer* psychicServer = nullptr; NukiNetwork* network = nullptr; WebCfgServer* webCfgServer = nullptr; Preferences* preferences = nullptr; @@ -418,11 +417,11 @@ void setup() if(!doOta) { - asyncServer = new AsyncWebServer(80); - webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, asyncServer); + psychicServer = new PsychicHttpServer; + webCfgServer = new WebCfgServer(network, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer); webCfgServer->initialize(); - asyncServer->onNotFound([](AsyncWebServerRequest* request) { request->redirect("/"); }); - asyncServer->begin(); + psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/"); }); + psychicServer->listen(80); } #else Log->print(F("Nuki Hub version ")); @@ -490,24 +489,26 @@ void setup() { if(!doOta) { - asyncServer = new AsyncWebServer(80); + psychicServer = new PsychicHttpServer; if(forceEnableWebServer || preferences->getBool(preference_webserver_enabled, true)) { - webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, asyncServer); + webCfgServer = new WebCfgServer(nuki, nukiOpener, network, gpio, preferences, network->networkDeviceType() == NetworkDeviceType::WiFi, partitionType, psychicServer); webCfgServer->initialize(); - asyncServer->onNotFound([](AsyncWebServerRequest* request) { request->redirect("/"); }); + psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/"); }); } - else asyncServer->onNotFound([](AsyncWebServerRequest* request) { request->redirect("/webserial"); }); + #ifdef DEBUG_NUKIHUB + else psychicServer->onNotFound([](PsychicRequest* request) { return request->redirect("/webserial"); }); if(preferences->getBool(preference_webserial_enabled, false)) { - WebSerial.setAuthentication(preferences->getString(preference_cred_user), preferences->getString(preference_cred_password)); - WebSerial.begin(asyncServer); - WebSerial.setBuffer(1024); + //WebSerial.setAuthentication(preferences->getString(preference_cred_user), preferences->getString(preference_cred_password)); + //WebSerial.begin(asyncServer); + //WebSerial.setBuffer(1024); } + #endif - asyncServer->begin(); + psychicServer->listen(80); } } #endif diff --git a/updater/platformio.ini b/updater/platformio.ini index 7bf7ec3..8a0d352 100644 --- a/updater/platformio.ini +++ b/updater/platformio.ini @@ -53,8 +53,7 @@ lib_ignore = SimpleBLE WiFiProv lib_deps = - AsyncTCP=symlink://../lib/AsyncTCP - ESPAsyncWebServer=symlink://../lib/ESPAsyncWebServer + PsychicHttp=symlink://../lib/PsychicHttp WiFiManager=symlink://../lib/WiFiManager monitor_speed = 115200 @@ -86,8 +85,7 @@ board = esp32-h2-devkitm-1 board_build.cmake_extra_args = -DNUKI_TARGET_H2=y lib_deps = - AsyncTCP=symlink://../lib/AsyncTCP - ESPAsyncWebServer=symlink://../lib/ESPAsyncWebServer + PsychicHttp=symlink://../lib/PsychicHttp [env:updater_esp32-solo1] extends = env:updater_esp32 diff --git a/updater/sdkconfig.defaults b/updater/sdkconfig.defaults index e60db16..14d722b 100644 --- a/updater/sdkconfig.defaults +++ b/updater/sdkconfig.defaults @@ -22,4 +22,13 @@ CONFIG_ETH_ENABLED=y CONFIG_ETH_USE_SPI_ETHERNET=y CONFIG_ETH_SPI_ETHERNET_W5500=y CONFIG_ETH_SPI_ETHERNET_DM9051=y -CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y \ No newline at end of file +CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 +CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_ERR_RESP_NO_DELAY=y +CONFIG_HTTPD_PURGE_BUF_LEN=32 +CONFIG_HTTPD_WS_SUPPORT=y +CONFIG_ESP_HTTPS_SERVER_ENABLE=y \ No newline at end of file