#include "WebCfgServer.h" #include "PreferencesKeys.h" #include "Version.h" WebCfgServer::WebCfgServer(NukiWrapper* nuki, Network* network, Preferences* preferences) : server(80), _nuki(nuki), _network(network), _preferences(preferences) { String str = _preferences->getString(preference_cred_user); if(str.length() > 0) { _hasCredentials = true; const char *user = str.c_str(); memcpy(&_credUser, user, str.length()); str = _preferences->getString(preference_cred_password); const char *pass = str.c_str(); memcpy(&_credPassword, pass, str.length()); } } void WebCfgServer::initialize() { server.on("/", [&]() { if (_hasCredentials && !server.authenticate(_credUser, _credPassword)) { return server.requestAuthentication(); } String response = ""; buildHtml(response); server.send(200, "text/html", response); }); server.on("/cred", [&]() { if (_hasCredentials && !server.authenticate(_credUser, _credPassword)) { return server.requestAuthentication(); } String response = ""; buildCredHtml(response); server.send(200, "text/html", response); }); server.on("/wifi", [&]() { if (_hasCredentials && !server.authenticate(_credUser, _credPassword)) { return server.requestAuthentication(); } String response = ""; buildConfigureWifiHtml(response); server.send(200, "text/html", response); }); server.on("/wifimanager", [&]() { if (_hasCredentials && !server.authenticate(_credUser, _credPassword)) { return server.requestAuthentication(); } String response = ""; buildConfirmHtml(response, "Restarting. Connect to ESP access point to reconfigure WiFi.", 0); server.send(200, "text/html", response); waitAndProcess(true, 2000); _network->restartAndConfigureWifi(); }); server.on("/method=get", [&]() { if (_hasCredentials && !server.authenticate(_credUser, _credPassword)) { return server.requestAuthentication(); } String message = ""; bool restartEsp = processArgs(message); if(restartEsp) { String response = ""; buildConfirmHtml(response, message); server.send(200, "text/html", response); Serial.println(F("Restarting")); waitAndProcess(true, 1000); ESP.restart(); } else { String response = ""; buildConfirmHtml(response, message, 3); server.send(200, "text/html", response); waitAndProcess(false, 1000); } }); server.begin(); } bool WebCfgServer::processArgs(String& message) { bool configChanged = false; bool clearMqttCredentials = false; bool clearCredentials = false; int count = server.args(); for(int index = 0; index < count; index++) { String key = server.argName(index); String value = server.arg(index); if(key == "MQTTSERVER") { _preferences->putString(preference_mqtt_broker, value); configChanged = true; } else if(key == "MQTTPORT") { _preferences->putInt(preference_mqtt_broker_port, value.toInt()); configChanged = true; } else if(key == "MQTTUSER") { if(value == "#") { clearMqttCredentials = true; } else { _preferences->putString(preference_mqtt_user, value); configChanged = true; } } else if(key == "MQTTPASS") { if(value != "*") { _preferences->putString(preference_mqtt_password, value); configChanged = true; } } else if(key == "MQTTPATH") { _preferences->putString(preference_mqtt_path, value); configChanged = true; } else if(key == "HOSTNAME") { _preferences->putString(preference_hostname, value); configChanged = true; } else if(key == "LSTINT") { _preferences->putInt(preference_query_interval_lockstate, value.toInt()); configChanged = true; } else if(key == "BATINT") { _preferences->putInt(preference_query_interval_battery, value.toInt()); configChanged = true; } else if(key == "PRDTMO") { _preferences->putInt(preference_presence_detection_timeout, value.toInt()); configChanged = true; } else if(key == "CREDUSER") { if(value == "#") { clearCredentials = true; } else { _preferences->putString(preference_cred_user, value); configChanged = true; } } else if(key == "CREDPASS") { _preferences->putString(preference_cred_password, value); configChanged = true; } else if(key == "NUKIPIN") { if(value == "#") { message = "PIN cleared"; _nuki->setPin(0xffff); } else { message = "PIN saved"; _nuki->setPin(value.toInt()); } } } if(clearMqttCredentials) { _preferences->putString(preference_mqtt_user, ""); _preferences->putString(preference_mqtt_password, ""); configChanged = true; } if(clearCredentials) { _preferences->putString(preference_cred_user, ""); _preferences->putString(preference_cred_password, ""); configChanged = true; } if(configChanged) { message = "Configuration saved ... restarting."; _enabled = false; _preferences->end(); } return configChanged; } void WebCfgServer::update() { if(!_enabled) return; server.handleClient(); } void WebCfgServer::buildHtml(String& response) { buildHtmlHeader(response); response.concat("

Info

\n"); String version = " "; version.concat(nuki_hub_version); char lockstateArr[20]; Nuki::lockstateToString(_nuki->keyTurnerState().lockState, lockstateArr); String lockState = " "; lockState.concat(lockstateArr); response.concat(""); printParameter(response, "Paired", _nuki->isPaired() ? " Yes" : " No"); printParameter(response, "MQTT Connected", _network->isMqttConnected() ? " Yes" : " No"); printParameter(response, "Lock state", lockState.c_str()); printParameter(response, "Firmware", version.c_str()); response.concat("


"); response.concat("
"); response.concat("

Configuration

"); response.concat(""); 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); printInputField(response, "MQTTPASS", "MQTT Password", "*", 30, true); printInputField(response, "MQTTPATH", "MQTT Path", _preferences->getString(preference_mqtt_path).c_str(), 180); printInputField(response, "HOSTNAME", "Host name", _preferences->getString(preference_hostname).c_str(), 100); printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10); printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10); printInputField(response, "PRDTMO", "Presence detection timeout (seconds, -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10); response.concat("
"); response.concat("
"); response.concat("


"); response.concat("

Credentials

"); response.concat("
"); response.concat(""); response.concat("
"); response.concat("

WiFi

"); response.concat("
"); response.concat(""); response.concat("
"); response.concat("\n"); response.concat("\n"); } void WebCfgServer::buildCredHtml(String &response) { buildHtmlHeader(response); response.concat("
"); response.concat("

Credentials

"); response.concat(""); printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 20); printInputField(response, "CREDPASS", "Password", "*", 30, true); response.concat("
"); response.concat("
"); response.concat("
"); response.concat("

"); response.concat("

NUKI Pin Code

"); response.concat(""); printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, true); response.concat("
"); response.concat("
"); response.concat("
"); response.concat("\n"); response.concat("\n"); } void WebCfgServer::buildConfirmHtml(String &response, const String &message, uint32_t redirectDelay) { String delay(redirectDelay); response.concat("\n"); response.concat("\n"); response.concat("NUKI Hub\n"); response.concat(""); response.concat("\n\n"); response.concat("\n"); response.concat(message); response.concat("\n"); response.concat("\n"); } void WebCfgServer::buildConfigureWifiHtml(String &response) { buildHtmlHeader(response); response.concat("

WiFi

"); response.concat("Click confirm to restart ESP into WiFi configuration mode. After restart, connect to ESP access point to reconfigure WiFI.

"); response.concat("
"); response.concat(""); response.concat("
"); response.concat("\n"); response.concat("\n"); } void WebCfgServer::buildHtmlHeader(String &response) { response.concat("\n"); response.concat("\n"); response.concat("NUKI Hub\n"); response.concat("\n"); response.concat("\n"); } void WebCfgServer::printInputField(String& response, const char *token, const char *description, const char *value, const size_t maxLength, const bool isPassword) { char maxLengthStr[20]; itoa(maxLength, maxLengthStr, 10); response.concat(""); response.concat(""); response.concat(description); response.concat(""); response.concat(""); response.concat(" "); response.concat(""); response.concat(""); } void WebCfgServer::printInputField(String& response, const char *token, const char *description, const int value, size_t maxLength) { char valueStr[20]; itoa(value, valueStr, 10); printInputField(response, token, description, valueStr, maxLength); } void WebCfgServer::printParameter(String& response, const char *description, const char *value) { response.concat(""); response.concat(""); response.concat(description); response.concat(""); response.concat(""); response.concat(value); response.concat(""); response.concat(""); } void WebCfgServer::waitAndProcess(const bool blocking, const uint32_t duration) { unsigned long timeout = millis() + duration; while(millis() < timeout) { server.handleClient(); if(blocking) { delay(10); } else { vTaskDelay( 50 / portTICK_PERIOD_MS); } } }