From 22348d71a34c3f6bc4844c64c7271b9fb8ecd6c2 Mon Sep 17 00:00:00 2001 From: iranl Date: Wed, 21 Aug 2024 13:40:10 +0200 Subject: [PATCH] Fixes (#460) --- src/NukiNetwork.cpp | 69 +- src/NukiNetwork.h | 3 + src/NukiNetworkLock.cpp | 21 +- src/NukiNetworkLock.h | 1 + src/NukiNetworkOpener.cpp | 13 + src/NukiNetworkOpener.h | 1 + src/NukiOpenerWrapper.cpp | 25 +- src/NukiOpenerWrapper.h | 2 + src/NukiWrapper.cpp | 94 +- src/NukiWrapper.h | 1 + src/PreferencesKeys.h | 182 +- src/WebCfgServer.cpp | 2403 ++++++++++++------------- src/WebCfgServer.h | 24 +- src/main.cpp | 7 +- src/networkDevices/EthernetDevice.cpp | 4 +- src/networkDevices/EthernetDevice.h | 1 - src/networkDevices/WifiDevice.cpp | 27 +- src/networkDevices/WifiDevice.h | 2 - 18 files changed, 1459 insertions(+), 1421 deletions(-) diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index a2c669e..8cae45b 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -40,8 +40,8 @@ NukiNetwork::NukiNetwork(Preferences *preferences) } _inst = this; - _hostname = _preferences->getString(preference_hostname); _webEnabled = _preferences->getBool(preference_webserver_enabled, true); + _updateFromMQTT = _preferences->getBool(preference_update_from_mqtt, false); #ifndef NUKI_HUB_UPDATER memset(_maintenancePathPrefix, 0, sizeof(_maintenancePathPrefix)); @@ -197,24 +197,22 @@ bool NukiNetwork::update() #else void NukiNetwork::initialize() { - _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false); - _checkUpdates = _preferences->getBool(preference_check_updates, false); - _reconnectNetworkOnMqttDisconnect = _preferences->getBool(preference_recon_netw_on_mqtt_discon, false); - _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval, 0) * 1000; _hostname = _preferences->getString(preference_hostname, ""); - _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); - _mqttPort = _preferences->getInt(preference_mqtt_broker_port, 1883); if(_hostname == "") { _hostname = "nukihub"; _preferences->putString(preference_hostname, _hostname); } - if(_rssiPublishInterval == 0) + + _mqttPort = _preferences->getInt(preference_mqtt_broker_port, 0); + + if(_mqttPort == 0) { - _rssiPublishInterval = 60000; - _preferences->putInt(preference_rssi_publish_interval, 60); + _mqttPort = 1883; + _preferences->putInt(preference_mqtt_broker_port, _mqttPort); } + strcpy(_hostnameArr, _hostname.c_str()); _device->initialize(); @@ -224,13 +222,6 @@ void NukiNetwork::initialize() String brokerAddr = _preferences->getString(preference_mqtt_broker); strcpy(_mqttBrokerAddr, brokerAddr.c_str()); - int port = _preferences->getInt(preference_mqtt_broker_port, 0); - if(port == 0) - { - port = 1883; - _preferences->putInt(preference_mqtt_broker_port, port); - } - String mqttUser = _preferences->getString(preference_mqtt_user); if(mqttUser.length() > 0) { @@ -254,21 +245,12 @@ void NukiNetwork::initialize() Log->print(F("MQTT Broker: ")); Log->print(_mqttBrokerAddr); Log->print(F(":")); - Log->println(port); + Log->println(_mqttPort); _device->mqttSetClientId(_hostnameArr); _device->mqttSetCleanSession(MQTT_CLEAN_SESSIONS); _device->mqttSetKeepAlive(MQTT_KEEP_ALIVE); - _networkTimeout = _preferences->getInt(preference_network_timeout, 0); - if(_networkTimeout == 0) - { - _networkTimeout = -1; - _preferences->putInt(preference_network_timeout, _networkTimeout); - } - - _publishDebugInfo = _preferences->getBool(preference_publish_debug_info, false); - char gpioPath[250]; bool rebGpio = rebuildGpio(); @@ -309,6 +291,33 @@ void NukiNetwork::initialize() { gpioActionCallback(action, pin); }); + + _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); + _offEnabled = _preferences->getBool(preference_official_hybrid, false); + readSettings(); +} + +void NukiNetwork::readSettings() +{ + _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect, false); + _checkUpdates = _preferences->getBool(preference_check_updates, false); + _reconnectNetworkOnMqttDisconnect = _preferences->getBool(preference_recon_netw_on_mqtt_discon, false); + _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval, 0) * 1000; + + if(_rssiPublishInterval == 0) + { + _rssiPublishInterval = 60000; + _preferences->putInt(preference_rssi_publish_interval, 60); + } + + _networkTimeout = _preferences->getInt(preference_network_timeout, 0); + if(_networkTimeout == 0) + { + _networkTimeout = -1; + _preferences->putInt(preference_network_timeout, _networkTimeout); + } + + _publishDebugInfo = _preferences->getBool(preference_publish_debug_info, false); } bool NukiNetwork::update() @@ -1055,7 +1064,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha removeHassTopic((char*)"sensor", (char*)"mqtt_log", uidString); } - if(_preferences->getBool(preference_official_hybrid, false)) + if(_offEnabled) { // Hybrid connected publishHassTopic("binary_sensor", @@ -1180,7 +1189,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha "", { { (char*)"en", (char*)"true" }}); - if(_preferences->getBool(preference_check_updates)) + if(_checkUpdates) { // NUKI Hub latest publishHassTopic("sensor", @@ -1204,7 +1213,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha _lockPath.toCharArray(latest_version_topic,_lockPath.length() + 1); strcat(latest_version_topic, mqtt_topic_info_nuki_hub_latest); - if(!_preferences->getBool(preference_update_from_mqtt, false)) + if(!_updateFromMQTT) { publishHassTopic("update", "nuki_hub_update", diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index ec0d20e..65d2921 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -22,6 +22,7 @@ class NukiNetwork { public: void initialize(); + void readSettings(); bool update(); void reconfigureDevice(); void clearWifiFallback(); @@ -112,6 +113,8 @@ private: NetworkDeviceType _networkDeviceType = (NetworkDeviceType)-1; bool _firstBootAfterDeviceChange = false; bool _webEnabled = true; + bool _updateFromMQTT = false; + bool _offEnabled = false; #ifndef NUKI_HUB_UPDATER static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 5bf97c7..a5f77af 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -204,7 +204,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const else if(comparePrefixedPath(topic, mqtt_topic_update) && strcmp(value, "1") == 0 && _preferences->getBool(preference_update_from_mqtt, false)) { Log->println(F("Update requested via MQTT.")); - + bool otaManifestSuccess = false; JsonDocument doc; @@ -235,7 +235,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const { String currentVersion = NUKI_HUB_VERSION; - if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) + if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) { if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as()) == 0) { @@ -294,7 +294,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const delay(200); restartEsp(RestartReason::OTAReboot); } - } + } } else { @@ -702,6 +702,12 @@ void NukiNetworkLock::publishAuthorizationInfo(const std::list 0) + { + memset(_authName, 0, sizeof(_authName)); + memcpy(_authName, _authEntries[_authId].c_str(), sizeof(_authEntries[_authId].c_str())); + } } } @@ -710,6 +716,12 @@ void NukiNetworkLock::publishAuthorizationInfo(const std::list().length() == 0 && _authEntries.count(log.authId) > 0) + { + entry["authorizationName"] = _authEntries[log.authId]; + } + entry["timeYear"] = log.timeStampYear; entry["timeMonth"] = log.timeStampMonth; entry["timeDay"] = log.timeStampDay; @@ -1368,6 +1380,7 @@ void NukiNetworkLock::publishAuth(const std::list& jsonEntry["idType"] = entry.idType; //CONSIDER INT TO STRING jsonEntry["enabled"] = entry.enabled; jsonEntry["name"] = entry.name; + _authEntries[jsonEntry["authId"]] = jsonEntry["name"].as(); jsonEntry["remoteAllowed"] = entry.remoteAllowed; char createdDT[20]; sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond); @@ -1612,7 +1625,7 @@ void NukiNetworkLock::publishHASSConfig(char *deviceType, const char *baseTopic, { _network->removeHASSConfigTopic((char*)"binary_sensor", (char*)"door_sensor", uidString); } - + #ifndef CONFIG_IDF_TARGET_ESP32H2 _network->publishHASSWifiRssiConfig(deviceType, baseTopic, name, uidString); #endif diff --git a/src/NukiNetworkLock.h b/src/NukiNetworkLock.h index f15f2dd..21f921c 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -105,6 +105,7 @@ private: NukiNetwork* _network; Preferences* _preferences; + std::map _authEntries; std::vector _offTopics; char _mqttPath[181] = {0}; char _offMqttPath[181] = {0}; diff --git a/src/NukiNetworkOpener.cpp b/src/NukiNetworkOpener.cpp index 9408019..da7e868 100644 --- a/src/NukiNetworkOpener.cpp +++ b/src/NukiNetworkOpener.cpp @@ -481,6 +481,12 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list 0) + { + memset(_authName, 0, sizeof(_authName)); + memcpy(_authName, _authEntries[_authId].c_str(), sizeof(_authEntries[_authId].c_str())); + } } } @@ -489,6 +495,12 @@ void NukiNetworkOpener::publishAuthorizationInfo(const std::list().length() == 0 && _authEntries.count(log.authId) > 0) + { + entry["authorizationName"] = _authEntries[log.authId]; + } + entry["timeYear"] = log.timeStampYear; entry["timeMonth"] = log.timeStampMonth; entry["timeDay"] = log.timeStampDay; @@ -1176,6 +1188,7 @@ void NukiNetworkOpener::publishAuth(const std::list(); jsonEntry["remoteAllowed"] = entry.remoteAllowed; char createdDT[20]; sprintf(createdDT, "%04d-%02d-%02d %02d:%02d:%02d", entry.createdYear, entry.createdMonth, entry.createdDay, entry.createdHour, entry.createdMinute, entry.createdSecond); diff --git a/src/NukiNetworkOpener.h b/src/NukiNetworkOpener.h index 218ba03..498cfe7 100644 --- a/src/NukiNetworkOpener.h +++ b/src/NukiNetworkOpener.h @@ -80,6 +80,7 @@ private: NukiNetwork* _network = nullptr; + std::map _authEntries; char _mqttPath[181] = {0}; bool _isConnected = false; bool _firstTunerStatePublish = true; diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index e08460e..6c8c5e9 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -31,7 +31,7 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback); network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback); - if(_preferences->getBool(preference_disable_non_json, false)) network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback); + network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback); network->setKeypadJsonCommandReceivedCallback(nukiOpenerInst->onKeypadJsonCommandReceivedCallback); network->setTimeControlCommandReceivedCallback(nukiOpenerInst->onTimeControlCommandReceivedCallback); network->setAuthCommandReceivedCallback(nukiOpenerInst->onAuthCommandReceivedCallback); @@ -49,7 +49,17 @@ NukiOpenerWrapper::~NukiOpenerWrapper() void NukiOpenerWrapper::initialize() { _nukiOpener.initialize(); + _nukiOpener.registerBleScanner(_bleScanner); + _nukiOpener.setEventHandler(this); + _nukiOpener.setConnectTimeout(3); + _nukiOpener.setDisconnectTimeout(5000); + _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; + readSettings(); +} + +void NukiOpenerWrapper::readSettings() +{ esp_power_level_t powerLevel; int pwrLvl = _preferences->getInt(preference_ble_tx_power, 9); @@ -64,7 +74,6 @@ void NukiOpenerWrapper::initialize() else if(pwrLvl >= -12) powerLevel = ESP_PWR_LVL_N12; _nukiOpener.setPower(powerLevel); - _nukiOpener.registerBleScanner(_bleScanner); _intervalLockstate = _preferences->getInt(preference_query_interval_lockstate); _intervalConfig = _preferences->getInt(preference_query_interval_configuration); @@ -76,10 +85,10 @@ void NukiOpenerWrapper::initialize() _maxTimeControlEntryCount = _preferences->getUInt(preference_opener_max_timecontrol_entry_count); _maxAuthEntryCount = _preferences->getUInt(preference_opener_max_auth_entry_count); _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); - _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200); _retryDelay = _preferences->getInt(preference_command_retry_delay); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; + _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); _preferences->getBytes(preference_conf_opener_basic_acl, &_basicOpenerConfigAclPrefs, sizeof(_basicOpenerConfigAclPrefs)); _preferences->getBytes(preference_conf_opener_advanced_acl, &_advancedOpenerConfigAclPrefs, sizeof(_advancedOpenerConfigAclPrefs)); @@ -126,10 +135,6 @@ void NukiOpenerWrapper::initialize() _preferences->putInt(preference_restart_ble_beacon_lost, _restartBeaconTimeout); } - _nukiOpener.setEventHandler(this); - _nukiOpener.setConnectTimeout(3); - _nukiOpener.setDisconnectTimeout(5000); - Log->print(F("Opener state interval: ")); Log->print(_intervalLockstate); Log->print(F(" | Battery interval: ")); @@ -1584,7 +1589,7 @@ void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint &id, const String &name, const String &code, const int& enabled) { - if(_preferences->getBool(preference_disable_non_json, false)) return; + if(_disableNonJSON) return; if(!_preferences->getBool(preference_keypad_control_enabled, false)) { @@ -2625,13 +2630,13 @@ void NukiOpenerWrapper::onAuthCommandReceived(const char *value) memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen); /* memcpy(&entry.sharedKey, secretKeyK, 32); - + if(idType != 1) { _network->publishAuthCommandResult("invalidIdType"); return; } - + entry.idType = idType; */ entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0; diff --git a/src/NukiOpenerWrapper.h b/src/NukiOpenerWrapper.h index 0c3952f..ffdfebd 100644 --- a/src/NukiOpenerWrapper.h +++ b/src/NukiOpenerWrapper.h @@ -15,6 +15,7 @@ public: virtual ~NukiOpenerWrapper(); void initialize(); + void readSettings(); void update(); void electricStrikeActuation(); @@ -104,6 +105,7 @@ private: int _restartBeaconTimeout = 0; // seconds bool _publishAuthData = false; bool _clearAuthData = false; + bool _disableNonJSON = false; int _nrOfRetries = 0; int _retryDelay = 0; int _retryCount = 0; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 64c0a7e..fe1dff0 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -34,7 +34,7 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback); network->setOfficialUpdateReceivedCallback(nukiInst->onOfficialUpdateReceivedCallback); network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback); - if(_disableNonJSON) network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback); + network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback); network->setKeypadJsonCommandReceivedCallback(nukiInst->onKeypadJsonCommandReceivedCallback); network->setTimeControlCommandReceivedCallback(nukiInst->onTimeControlCommandReceivedCallback); network->setAuthCommandReceivedCallback(nukiInst->onAuthCommandReceivedCallback); @@ -51,45 +51,11 @@ NukiWrapper::~NukiWrapper() void NukiWrapper::initialize(const bool& firstStart) { - _preferences->remove(preference_presence_detection_timeout); - _nukiLock.initialize(); - - esp_power_level_t powerLevel; - int pwrLvl = _preferences->getInt(preference_ble_tx_power, 9); - - if(pwrLvl >= 9) powerLevel = ESP_PWR_LVL_P9; - else if(pwrLvl >= 6) powerLevel = ESP_PWR_LVL_P6; - else if(pwrLvl >= 3) powerLevel = ESP_PWR_LVL_P6; - else if(pwrLvl >= 0) powerLevel = ESP_PWR_LVL_P3; - else if(pwrLvl >= -3) powerLevel = ESP_PWR_LVL_N3; - else if(pwrLvl >= -6) powerLevel = ESP_PWR_LVL_N6; - else if(pwrLvl >= -9) powerLevel = ESP_PWR_LVL_N9; - else if(pwrLvl >= -12) powerLevel = ESP_PWR_LVL_N12; - - _nukiLock.setPower(powerLevel); _nukiLock.registerBleScanner(_bleScanner); - - _intervalLockstate = _preferences->getInt(preference_query_interval_lockstate); - _intervalHybridLockstate = _preferences->getInt(preference_query_interval_hybrid_lockstate); - _intervalConfig = _preferences->getInt(preference_query_interval_configuration); - _intervalBattery = _preferences->getInt(preference_query_interval_battery); - _intervalKeypad = _preferences->getInt(preference_query_interval_keypad); - _keypadEnabled = _preferences->getBool(preference_keypad_info_enabled); - _publishAuthData = _preferences->getBool(preference_publish_authdata); - _maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count); - _maxTimeControlEntryCount = _preferences->getUInt(preference_lock_max_timecontrol_entry_count); - _maxAuthEntryCount = _preferences->getUInt(preference_lock_max_auth_entry_count); - _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); - _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; - _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200); - _retryDelay = _preferences->getInt(preference_command_retry_delay); - _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; - _offEnabled = _preferences->getBool(preference_official_hybrid, false); - _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); - - _preferences->getBytes(preference_conf_lock_basic_acl, &_basicLockConfigaclPrefs, sizeof(_basicLockConfigaclPrefs)); - _preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs)); + _nukiLock.setEventHandler(this); + _nukiLock.setConnectTimeout(3); + _nukiLock.setDisconnectTimeout(5000); if(firstStart) { @@ -138,6 +104,46 @@ void NukiWrapper::initialize(const bool& firstStart) _preferences->putInt(preference_query_interval_keypad, 1800); } + _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; + _offEnabled = _preferences->getBool(preference_official_hybrid, false); + readSettings(); +} + +void NukiWrapper::readSettings() +{ + esp_power_level_t powerLevel; + int pwrLvl = _preferences->getInt(preference_ble_tx_power, 9); + + if(pwrLvl >= 9) powerLevel = ESP_PWR_LVL_P9; + else if(pwrLvl >= 6) powerLevel = ESP_PWR_LVL_P6; + else if(pwrLvl >= 3) powerLevel = ESP_PWR_LVL_P6; + else if(pwrLvl >= 0) powerLevel = ESP_PWR_LVL_P3; + else if(pwrLvl >= -3) powerLevel = ESP_PWR_LVL_N3; + else if(pwrLvl >= -6) powerLevel = ESP_PWR_LVL_N6; + else if(pwrLvl >= -9) powerLevel = ESP_PWR_LVL_N9; + else if(pwrLvl >= -12) powerLevel = ESP_PWR_LVL_N12; + + _nukiLock.setPower(powerLevel); + + _intervalLockstate = _preferences->getInt(preference_query_interval_lockstate); + _intervalHybridLockstate = _preferences->getInt(preference_query_interval_hybrid_lockstate); + _intervalConfig = _preferences->getInt(preference_query_interval_configuration); + _intervalBattery = _preferences->getInt(preference_query_interval_battery); + _intervalKeypad = _preferences->getInt(preference_query_interval_keypad); + _keypadEnabled = _preferences->getBool(preference_keypad_info_enabled); + _publishAuthData = _preferences->getBool(preference_publish_authdata); + _maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count); + _maxTimeControlEntryCount = _preferences->getUInt(preference_lock_max_timecontrol_entry_count); + _maxAuthEntryCount = _preferences->getUInt(preference_lock_max_auth_entry_count); + _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); + _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200); + _retryDelay = _preferences->getInt(preference_command_retry_delay); + _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; + _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); + + _preferences->getBytes(preference_conf_lock_basic_acl, &_basicLockConfigaclPrefs, sizeof(_basicLockConfigaclPrefs)); + _preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs)); + if(_nrOfRetries < 0 || _nrOfRetries == 200) { Log->println("Invalid nrOfRetries, revert to default (3)"); @@ -187,10 +193,6 @@ void NukiWrapper::initialize(const bool& firstStart) _preferences->putInt(preference_restart_ble_beacon_lost, _restartBeaconTimeout); } - _nukiLock.setEventHandler(this); - _nukiLock.setConnectTimeout(3); - _nukiLock.setDisconnectTimeout(5000); - Log->print(F("Lock state interval: ")); Log->print(_intervalLockstate); Log->print(F(" | Battery interval: ")); @@ -2758,7 +2760,7 @@ void NukiWrapper::onAuthCommandReceived(const char *value) return; } } - + /* if(sharedKey.length() != 64) { @@ -2883,20 +2885,20 @@ void NukiWrapper::onAuthCommandReceived(const char *value) { _network->publishAuthCommandResult("addActionNotSupported"); return; - + NukiLock::NewAuthorizationEntry entry; memset(&entry, 0, sizeof(entry)); size_t nameLen = name.length(); memcpy(&entry.name, name.c_str(), nameLen > 32 ? 32 : nameLen); /* memcpy(&entry.sharedKey, secretKeyK, 32); - + if(idType != 1) { _network->publishAuthCommandResult("invalidIdType"); return; } - + entry.idType = idType; */ entry.remoteAllowed = remoteAllowed == 1 ? 1 : 0; diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index 90e1f4d..f87693d 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -16,6 +16,7 @@ public: virtual ~NukiWrapper(); void initialize(const bool& firstStart); + void readSettings(); void update(); void lock(); diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index edb8ac7..858d15c 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -3,105 +3,27 @@ #include #include "Config.h" -#define preference_started_before (char*)"run" -#define preference_config_version (char*)"confVersion" -#define preference_device_id_lock (char*)"deviceId" -#define preference_device_id_opener (char*)"deviceIdOp" -#define preference_nuki_id_lock (char*)"nukiId" -#define preference_nuki_id_opener (char*)"nukidOp" +//CHANGE REQUIRES REBOOT TO TAKE EFFECT +#define preference_ip_dhcp_enabled (char*)"dhcpena" +#define preference_ip_address (char*)"ipaddr" +#define preference_ip_subnet (char*)"ipsub" +#define preference_ip_gateway (char*)"ipgtw" +#define preference_ip_dns_server (char*)"dnssrv" #define preference_mqtt_broker (char*)"mqttbroker" #define preference_mqtt_broker_port (char*)"mqttport" #define preference_mqtt_user (char*)"mqttuser" #define preference_mqtt_password (char*)"mqttpass" #define preference_mqtt_log_enabled (char*)"mqttlog" #define preference_webserial_enabled (char*)"weblog" -#define preference_webserver_enabled (char*)"websrvena" #define preference_lock_enabled (char*)"lockena" -#define preference_lock_pin_status (char*)"lockpin" #define preference_mqtt_lock_path (char*)"mqttpath" #define preference_opener_enabled (char*)"openerena" -#define preference_opener_pin_status (char*)"openerpin" -#define preference_opener_continuous_mode (char*)"openercont" #define preference_mqtt_opener_path (char*)"mqttoppath" -#define preference_check_updates (char*)"checkupdates" -#define preference_lock_max_keypad_code_count (char*)"maxkpad" -#define preference_opener_max_keypad_code_count (char*)"opmaxkpad" -#define preference_lock_max_timecontrol_entry_count (char*)"maxtc" -#define preference_opener_max_timecontrol_entry_count (char*)"opmaxtc" #define preference_mqtt_ca (char*)"mqttca" #define preference_mqtt_crt (char*)"mqttcrt" #define preference_mqtt_key (char*)"mqttkey" -#define preference_mqtt_hass_discovery (char*)"hassdiscovery" -#define preference_mqtt_hass_cu_url (char*)"hassConfigUrl" -#define preference_ip_dhcp_enabled (char*)"dhcpena" -#define preference_ip_address (char*)"ipaddr" -#define preference_ip_subnet (char*)"ipsub" -#define preference_ip_gateway (char*)"ipgtw" -#define preference_ip_dns_server (char*)"dnssrv" #define preference_network_hardware (char*)"nwhw" -#define preference_network_hardware_gpio (char*)"nwhwdt" // obsolete -#define preference_network_wifi_fallback_disabled (char*)"nwwififb" -#define preference_find_best_rssi (char*)"nwbestrssi" -#define preference_rssi_publish_interval (char*)"rssipb" #define preference_hostname (char*)"hostname" -#define preference_network_timeout (char*)"nettmout" -#define preference_restart_on_disconnect (char*)"restdisc" -#define preference_restart_ble_beacon_lost (char*)"rstbcn" -#define preference_query_interval_lockstate (char*)"lockStInterval" -#define preference_query_interval_configuration (char*)"configInterval" -#define preference_query_interval_battery (char*)"batInterval" -#define preference_query_interval_keypad (char*)"kpInterval" -#define preference_access_level (char*)"accLvl" -#define preference_keypad_info_enabled (char*)"kpInfoEnabled" -#define preference_keypad_topic_per_entry (char*)"kpPerEntry" -#define preference_keypad_control_enabled (char*)"kpCntrlEnabled" -#define preference_keypad_publish_code (char*)"kpPubCode" -#define preference_timecontrol_control_enabled (char*)"tcCntrlEnabled" -#define preference_timecontrol_topic_per_entry (char*)"tcPerEntry" -#define preference_timecontrol_info_enabled (char*)"tcInfoEnabled" -#define preference_publish_authdata (char*)"pubAuth" -#define preference_acl (char*)"aclLckOpn" -#define preference_conf_info_enabled (char*)"cnfInfoEnabled" -#define preference_conf_lock_basic_acl (char*)"confLckBasAcl" -#define preference_conf_lock_advanced_acl (char*)"confLckAdvAcl" -#define preference_conf_opener_basic_acl (char*)"confOpnBasAcl" -#define preference_conf_opener_advanced_acl (char*)"confOpnAdvAcl" -#define preference_register_as_app (char*)"regAsApp" // true = register as hub; false = register as app -#define preference_register_opener_as_app (char*)"regOpnAsApp" -#define preference_command_nr_of_retries (char*)"nrRetry" -#define preference_command_retry_delay (char*)"rtryDelay" -#define preference_cred_user (char*)"crdusr" -#define preference_cred_password (char*)"crdpass" -#define preference_gpio_locking_enabled (char*)"gpiolck" // obsolete -#define preference_gpio_configuration (char*)"gpiocfg" -#define preference_publish_debug_info (char*)"pubdbg" -#define preference_presence_detection_timeout (char*)"prdtimeout" -#define preference_latest_version (char*)"latest" -#define preference_task_size_network (char*)"tsksznetw" -#define preference_task_size_nuki (char*)"tsksznuki" -#define preference_authlog_max_entries (char*)"authmaxentry" -#define preference_keypad_max_entries (char*)"kpmaxentry" -#define preference_timecontrol_max_entries (char*)"tcmaxentry" -#define preference_bootloop_counter (char*)"btlpcounter" -#define preference_enable_bootloop_reset (char*)"enabtlprst" -#define preference_buffer_size (char*)"buffsize" -#define preference_disable_non_json (char*)"disnonjson" -#define preference_official_hybrid (char*)"offHybrid" -#define preference_official_hybrid_actions (char*)"hybridAct" -#define preference_official_hybrid_retry (char*)"hybridRtry" -#define preference_query_interval_hybrid_lockstate (char*)"hybridTimer" -#define preference_ota_main_url (char*)"otaMainUrl" -#define preference_ota_updater_url (char*)"otaUpdUrl" -#define preference_update_from_mqtt (char*)"updMqtt" -#define preference_show_secrets (char*)"showSecr" -#define preference_ble_tx_power (char*)"bleTxPwr" -#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis" -#define preference_lock_max_auth_entry_count (char*)"maxauth" -#define preference_opener_max_auth_entry_count (char*)"opmaxauth" -#define preference_auth_control_enabled (char*)"authCtrlEna" -#define preference_auth_topic_per_entry (char*)"authPerEntry" -#define preference_auth_info_enabled (char*)"authInfoEna" -#define preference_auth_max_entries (char*)"authmaxentry" #define preference_network_custom_phy (char*)"ntwPHY" #define preference_network_custom_addr (char*)"ntwADDR" #define preference_network_custom_irq (char*)"ntwIRQ" @@ -114,10 +36,94 @@ #define preference_network_custom_mdio (char*)"ntwMDIO" #define preference_network_custom_mdc (char*)"ntwMDC" #define preference_network_custom_clk (char*)"ntwCLK" +#define preference_auth_control_enabled (char*)"authCtrlEna" +#define preference_keypad_control_enabled (char*)"kpCntrlEnabled" +#define preference_timecontrol_control_enabled (char*)"tcCntrlEnabled" +#define preference_ota_main_url (char*)"otaMainUrl" +#define preference_ota_updater_url (char*)"otaUpdUrl" +#define preference_task_size_network (char*)"tsksznetw" +#define preference_task_size_nuki (char*)"tsksznuki" +#define preference_buffer_size (char*)"buffsize" +#define preference_cred_user (char*)"crdusr" +#define preference_cred_password (char*)"crdpass" +#define preference_gpio_configuration (char*)"gpiocfg" +#define preference_mqtt_hass_discovery (char*)"hassdiscovery" +#define preference_webserver_enabled (char*)"websrvena" +#define preference_update_from_mqtt (char*)"updMqtt" +#define preference_disable_non_json (char*)"disnonjson" +#define preference_official_hybrid (char*)"offHybrid" + +// CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT +#define preference_find_best_rssi (char*)"nwbestrssi" #define preference_ntw_reconfigure (char*)"ntwRECONF" +#define preference_auth_max_entries (char*)"authmaxentry" +#define preference_auth_info_enabled (char*)"authInfoEna" +#define preference_auth_topic_per_entry (char*)"authPerEntry" +#define preference_authlog_max_entries (char*)"authmaxentry" +#define preference_keypad_max_entries (char*)"kpmaxentry" +#define preference_timecontrol_max_entries (char*)"tcmaxentry" +#define preference_register_as_app (char*)"regAsApp" // true = register as hub; false = register as app +#define preference_register_opener_as_app (char*)"regOpnAsApp" +#define preference_acl (char*)"aclLckOpn" +#define preference_conf_lock_basic_acl (char*)"confLckBasAcl" +#define preference_conf_lock_advanced_acl (char*)"confLckAdvAcl" +#define preference_conf_opener_basic_acl (char*)"confOpnBasAcl" +#define preference_conf_opener_advanced_acl (char*)"confOpnAdvAcl" +#define preference_ble_tx_power (char*)"bleTxPwr" +#define preference_show_secrets (char*)"showSecr" +#define preference_enable_bootloop_reset (char*)"enabtlprst" +#define preference_keypad_info_enabled (char*)"kpInfoEnabled" +#define preference_keypad_topic_per_entry (char*)"kpPerEntry" +#define preference_keypad_publish_code (char*)"kpPubCode" +#define preference_timecontrol_topic_per_entry (char*)"tcPerEntry" +#define preference_timecontrol_info_enabled (char*)"tcInfoEnabled" +#define preference_publish_authdata (char*)"pubAuth" +#define preference_conf_info_enabled (char*)"cnfInfoEnabled" +#define preference_restart_ble_beacon_lost (char*)"rstbcn" +#define preference_query_interval_lockstate (char*)"lockStInterval" +#define preference_query_interval_configuration (char*)"configInterval" +#define preference_query_interval_battery (char*)"batInterval" +#define preference_query_interval_keypad (char*)"kpInterval" +#define preference_command_nr_of_retries (char*)"nrRetry" +#define preference_command_retry_delay (char*)"rtryDelay" +#define preference_query_interval_hybrid_lockstate (char*)"hybridTimer" +#define preference_mqtt_hass_cu_url (char*)"hassConfigUrl" +#define preference_network_wifi_fallback_disabled (char*)"nwwififb" +#define preference_check_updates (char*)"checkupdates" +#define preference_opener_continuous_mode (char*)"openercont" +#define preference_rssi_publish_interval (char*)"rssipb" +#define preference_network_timeout (char*)"nettmout" +#define preference_restart_on_disconnect (char*)"restdisc" +#define preference_publish_debug_info (char*)"pubdbg" +#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis" +#define preference_official_hybrid_actions (char*)"hybridAct" +#define preference_official_hybrid_retry (char*)"hybridRtry" + +//NOT USER CHANGABLE #define preference_updater_version (char*)"updVer" #define preference_updater_build (char*)"updBuild" #define preference_updater_date (char*)"updDate" +#define preference_lock_max_auth_entry_count (char*)"maxauth" +#define preference_opener_max_auth_entry_count (char*)"opmaxauth" +#define preference_started_before (char*)"run" +#define preference_config_version (char*)"confVersion" +#define preference_device_id_lock (char*)"deviceId" +#define preference_device_id_opener (char*)"deviceIdOp" +#define preference_nuki_id_lock (char*)"nukiId" +#define preference_nuki_id_opener (char*)"nukidOp" +#define preference_lock_pin_status (char*)"lockpin" +#define preference_opener_pin_status (char*)"openerpin" +#define preference_lock_max_keypad_code_count (char*)"maxkpad" +#define preference_opener_max_keypad_code_count (char*)"opmaxkpad" +#define preference_lock_max_timecontrol_entry_count (char*)"maxtc" +#define preference_opener_max_timecontrol_entry_count (char*)"opmaxtc" +#define preference_latest_version (char*)"latest" + +//OBSOLETE +#define preference_access_level (char*)"accLvl" +#define preference_gpio_locking_enabled (char*)"gpiolck" +#define preference_network_hardware_gpio (char*)"nwhwdt" +#define preference_presence_detection_timeout (char*)"prdtimeout" inline bool initPreferences(Preferences* preferences) { @@ -128,8 +134,6 @@ inline bool initPreferences(Preferences* preferences) bool firstStart = !preferences->getBool(preference_started_before); #endif - preferences->remove(preference_bootloop_counter); - if(firstStart) { preferences->putBool(preference_started_before, true); @@ -273,8 +277,8 @@ private: preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, - preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_presence_detection_timeout, - preference_official_hybrid, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_latest_version, + preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, + preference_official_hybrid, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, @@ -310,7 +314,7 @@ private: preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_buffer_size, preference_network_hardware, preference_rssi_publish_interval, preference_network_timeout, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_command_nr_of_retries, - preference_command_retry_delay, preference_presence_detection_timeout, preference_query_interval_hybrid_lockstate, preference_latest_version, + preference_command_retry_delay, preference_query_interval_hybrid_lockstate, preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_ble_tx_power, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 8142ec1..90e9b5b 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -72,6 +72,8 @@ 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(); #ifndef NUKI_HUB_UPDATER @@ -109,15 +111,7 @@ void WebCfgServer::initialize() }); _asyncServer->on("/acclvl", HTTP_GET, [&](AsyncWebServerRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildAccLvlHtml(request, 0); - }); - _asyncServer->on("/acllock", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildAccLvlHtml(request, 1); - }); - _asyncServer->on("/aclopener", HTTP_GET, [&](AsyncWebServerRequest *request){ - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); - buildAccLvlHtml(request, 2); + buildAccLvlHtml(request); }); _asyncServer->on("/custntw", HTTP_GET, [&](AsyncWebServerRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); @@ -178,17 +172,11 @@ void WebCfgServer::initialize() if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); _preferences->putBool(preference_publish_debug_info, true); buildConfirmHtml(request, "Debug On", 3, true); - Log->println(F("Restarting")); - waitAndProcess(true, 1000); - restartEsp(RestartReason::ConfigurationUpdated); }); _asyncServer->on("/debugoff", HTTP_GET, [&](AsyncWebServerRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); _preferences->putBool(preference_publish_debug_info, false); buildConfirmHtml(request, "Debug Off", 3, true); - Log->println(F("Restarting")); - waitAndProcess(true, 1000); - restartEsp(RestartReason::ConfigurationUpdated); }); _asyncServer->on("/savecfg", HTTP_POST, [&](AsyncWebServerRequest *request){ if(strlen(_credUser) > 0 && strlen(_credPassword) > 0) if(!request->authenticate(_credUser, _credPassword)) return request->requestAuthentication(); @@ -245,10 +233,22 @@ void WebCfgServer::initialize() //Update.onProgress(printProgress); } +void WebCfgServer::sendResponse(AsyncWebServerRequest *request) +{ + 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; + }); + + request->send(response); +} + void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - buildHtmlHeader(response); + _response = ""; + buildHtmlHeader(); bool errored = false; if(request->hasParam("errored")) @@ -257,21 +257,21 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) if(p->value() != "") errored = true; } - if(errored) response->print("
Over-the-air update errored. Please check the logs for more info

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

"); if(_partitionType == 0) { - 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(""); + _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("
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("
"); + _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("
"); String release_type; @@ -283,18 +283,18 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) #else String build_type = "debug"; #endif - response->print("

"); - response->print("

"); - response->print("

"); - response->print("

"); + _response.concat("

"); + _response.concat("

"); + _response.concat("

"); + _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("
"); + _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("
"); #ifndef NUKI_HUB_UPDATER bool manifestSuccess = false; @@ -325,40 +325,40 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) if(!manifestSuccess) { - response->print("currentverlatestverdevverbetaver"); + _response.concat("currentverlatestverdevverbetaver"); } else { - 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: "); + _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: "); if(doc["beta"]["fullversion"] != "No beta available") { - response->print(doc["beta"]["fullversion"].as()); - response->print(" ("); - response->print(doc["beta"]["build"].as()); - response->print("), "); - response->print(doc["beta"]["time"].as()); + _response.concat(doc["beta"]["fullversion"].as()); + _response.concat(" ("); + _response.concat(doc["beta"]["build"].as()); + _response.concat(")
, "); + _response.concat(doc["beta"]["time"].as()); } else { - response->print(doc["beta"]["fullversion"].as()); - response->print(""); + _response.concat(doc["beta"]["fullversion"].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("
"); - + _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("
"); + String currentVersion = NUKI_HUB_VERSION; const char* latestVersion; @@ -370,87 +370,87 @@ 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->print("
"); + _response.concat("
"); if(_partitionType == 1) { - 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:
"); + _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:
"); } else { - 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("

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("
"); - response->print("

GitHub


"); - response->print(""); - response->print("

"); - response->print("

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


"); + _response.concat("
"); + _response.concat("

GitHub


"); + _response.concat(""); + _response.concat("

"); + _response.concat("

"); + _response.concat(""); + _response.concat(""); + sendResponse(request); } void WebCfgServer::buildOtaCompletedHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - buildHtmlHeader(response); + _response = ""; + buildHtmlHeader(); - response->print("
Over-the-air update completed.
You will be forwarded automatically.
"); - response->print(""); - response->print(""); - request->send(response); + _response.concat("
Over-the-air update completed.
You will be forwarded automatically.
"); + _response.concat(""); + _response.concat(""); + sendResponse(request); } -void WebCfgServer::buildHtmlHeader(AsyncResponseStream *response, String additionalHeader) +void WebCfgServer::buildHtmlHeader(String additionalHeader) { - response->print(""); - response->print(""); - if(strcmp(additionalHeader.c_str(), "") != 0) response->print(additionalHeader); - response->print(""); - response->print("Nuki Hub"); + _response.concat(""); + _response.concat(""); + if(strcmp(additionalHeader.c_str(), "") != 0) _response.concat(additionalHeader); + _response.concat(""); + _response.concat("Nuki Hub"); } void WebCfgServer::waitAndProcess(const bool blocking, const uint32_t duration) @@ -557,7 +557,7 @@ void WebCfgServer::handleOtaUpload(AsyncWebServerRequest *request, String filena void WebCfgServer::buildConfirmHtml(AsyncWebServerRequest *request, const String &message, uint32_t redirectDelay, bool redirect) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); + _response = ""; String header; if(!redirect) @@ -570,10 +570,10 @@ void WebCfgServer::buildConfirmHtml(AsyncWebServerRequest *request, const String String delay(redirectDelay * 1000); header = ""; } - buildHtmlHeader(response, header); - response->print(message); - response->print(""); - request->send(response); + buildHtmlHeader(header); + _response.concat(message); + _response.concat(""); + sendResponse(request); } void WebCfgServer::sendCss(AsyncWebServerRequest *request) @@ -581,7 +581,7 @@ void WebCfgServer::sendCss(AsyncWebServerRequest *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 ); + request->send(asyncResponse); } void WebCfgServer::sendFavicon(AsyncWebServerRequest *request) @@ -769,10 +769,14 @@ void WebCfgServer::sendSettings(AsyncWebServerRequest *request) serializeJsonPretty(json, jsonPretty); - AsyncWebServerResponse *asyncResponse = request->beginResponse(200, "application/json", jsonPretty); - asyncResponse->addHeader("Content-Disposition", "attachment; filename=nuki_hub.json"); - request->send(asyncResponse); + 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); } bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) @@ -783,6 +787,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) bool clearCredentials = false; bool manPairLck = false; bool manPairOpn = false; + bool networkReconfigure = false; unsigned char currentBleAddress[6]; unsigned char authorizationId[4] = {0x00}; unsigned char secretKeyK[32] = {0x00}; @@ -920,7 +925,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(value.toInt() > 1) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; if(value.toInt() != 11) _preferences->putInt(preference_network_custom_phy, 0); } _preferences->putInt(preference_network_hardware, value.toInt()); @@ -933,7 +938,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_phy, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_phy, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -944,7 +949,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_addr, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_addr, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -955,7 +960,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_irq, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_irq, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -966,7 +971,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_rst, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_rst, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -977,7 +982,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_cs, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_cs, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -988,7 +993,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_sck, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_sck, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -999,7 +1004,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_miso, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_miso, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -1010,7 +1015,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_mosi, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_mosi, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -1021,7 +1026,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_pwr, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_pwr, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -1032,7 +1037,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_mdio, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_mdio, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -1043,7 +1048,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_mdc, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_mdc, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -1054,7 +1059,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) { if(_preferences->getInt(preference_network_custom_clk, 0) != value.toInt()) { - _preferences->putBool(preference_ntw_reconfigure, true); + networkReconfigure = true; _preferences->putInt(preference_network_custom_clk, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); @@ -1068,7 +1073,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_network_wifi_fallback_disabled, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "RSSI") @@ -1078,7 +1083,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_rssi_publish_interval, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "HASSDISCOVERY") @@ -1100,7 +1105,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_opener_continuous_mode, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "HASSCUURL") @@ -1110,7 +1115,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putString(preference_mqtt_hass_cu_url, value); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "BESTRSSI") @@ -1120,7 +1125,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_find_best_rssi, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "HOSTNAME") @@ -1140,7 +1145,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_network_timeout, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "RSTDISC") @@ -1150,7 +1155,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_restart_on_disconnect, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "RECNWTMQTTDIS") @@ -1160,7 +1165,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_recon_netw_on_mqtt_discon, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "MQTTLOG") @@ -1190,7 +1195,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_check_updates, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "UPDATEMQTT") @@ -1222,7 +1227,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) if(value == "1") _preferences->putBool(preference_register_as_app, true); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "HYBRIDTIMER") @@ -1232,7 +1237,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_query_interval_hybrid_lockstate, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "HYBRIDRETRY") @@ -1242,7 +1247,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_official_hybrid_retry, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "DISNONJSON") @@ -1312,7 +1317,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_query_interval_lockstate, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "CFGINT") @@ -1322,7 +1327,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_query_interval_configuration, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "BATINT") @@ -1332,7 +1337,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_query_interval_battery, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "KPINT") @@ -1342,7 +1347,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_query_interval_keypad, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "NRTRY") @@ -1352,7 +1357,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_command_nr_of_retries, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "TRYDLY") @@ -1362,7 +1367,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_command_retry_delay, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "TXPWR") @@ -1374,7 +1379,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_ble_tx_power, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } } @@ -1385,7 +1390,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_restart_ble_beacon_lost, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "TSKNTWK") @@ -1423,7 +1428,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_authlog_max_entries, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } } @@ -1436,7 +1441,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_keypad_max_entries, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } } @@ -1449,7 +1454,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_timecontrol_max_entries, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } } @@ -1462,7 +1467,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putInt(preference_auth_max_entries, value.toInt()); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } } @@ -1486,7 +1491,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_enable_bootloop_reset, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "OTAUPD") @@ -1516,7 +1521,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_show_secrets, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "ACLLVLCHANGED") @@ -1530,7 +1535,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_conf_info_enabled, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "KPPUB") @@ -1540,7 +1545,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_keypad_info_enabled, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "KPCODE") @@ -1550,7 +1555,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_keypad_publish_code, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "KPENA") @@ -1570,7 +1575,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_timecontrol_info_enabled, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "AUTHPUB") @@ -1580,7 +1585,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_auth_info_enabled, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "KPPER") @@ -1590,7 +1595,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_keypad_topic_per_entry, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "TCPER") @@ -1600,7 +1605,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_timecontrol_topic_per_entry, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "TCENA") @@ -1620,7 +1625,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_auth_topic_per_entry, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "AUTHENA") @@ -1640,7 +1645,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_publish_authdata, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "ACLLCKLCK") @@ -2006,7 +2011,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_register_as_app, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "REGAPPOPN") @@ -2016,7 +2021,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBool(preference_register_opener_as_app, (value == "1")); Log->print(F("Setting changed: ")); Log->println(key); - configChanged = true; + //configChanged = true; } } else if(key == "LOCKENA") @@ -2142,6 +2147,11 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) } } + if(networkReconfigure) + { + _preferences->putBool(preference_ntw_reconfigure, true); + } + if(manPairLck) { Log->println(F("Changing lock pairing")); @@ -2239,7 +2249,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBytes(preference_acl, (byte*)(&aclPrefs), sizeof(aclPrefs)); Log->print(F("Setting changed: ")); Log->println("ACLPREFS"); - configChanged = true; + //configChanged = true; break; } } @@ -2250,7 +2260,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); Log->print(F("Setting changed: ")); Log->println("ACLCONFBASICLOCK"); - configChanged = true; + //configChanged = true; break; } } @@ -2261,7 +2271,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); Log->print(F("Setting changed: ")); Log->println("ACLCONFADVANCEDLOCK"); - configChanged = true; + //configChanged = true; break; } @@ -2273,7 +2283,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); Log->print(F("Setting changed: ")); Log->println("ACLCONFBASICOPENER"); - configChanged = true; + //configChanged = true; break; } } @@ -2284,7 +2294,7 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) _preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); Log->print(F("Setting changed: ")); Log->println("ACLCONFBADVANCEDOPENER"); - configChanged = true; + //configChanged = true; break; } } @@ -2292,10 +2302,23 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) if(configChanged) { - message = "Configuration saved."; + message = "Configuration saved, reboot required to apply"; _rebootRequired = true; } - else message = "Nothing changed."; + else + { + message = "Configuration saved."; + } + + _network->readSettings(); + if(_nuki != nullptr) + { + _nuki->readSettings(); + } + if(_nukiOpener != nullptr) + { + _nukiOpener->readSettings(); + } return configChanged; } @@ -2447,9 +2470,13 @@ bool WebCfgServer::processImport(AsyncWebServerRequest *request, String& message if(configChanged) { - message = "Configuration saved."; + message = "Configuration saved, reboot is required to apply."; _rebootRequired = true; } + else + { + message = "Configuration saved and applied."; + } return configChanged; } @@ -2477,81 +2504,85 @@ void WebCfgServer::processGpioArgs(AsyncWebServerRequest *request) void WebCfgServer::buildImportExportHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - buildHtmlHeader(response); + _response = ""; + buildHtmlHeader(); - response->print("

Import configuration

"); - response->print("

"); - response->print("


"); - response->print("
"); - response->print("

Export configuration


"); - response->print(""); - response->print("

"); - response->print("

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

Import configuration

"); + _response.concat("

"); + _response.concat("


"); + _response.concat("
"); + _response.concat("

Export configuration


"); + _response.concat(""); + _response.concat("

"); + _response.concat("

"); + _response.concat("
"); + sendResponse(request); } void WebCfgServer::buildCustomNetworkConfigHtml(AsyncWebServerRequest *request) { String header = ""; - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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, ""); + _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, ""); #if defined(CONFIG_IDF_TARGET_ESP32) - 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\""); + 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\""); #endif - 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\""); + 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->print("
"); + _response.concat(""); - response->print("
"); - response->print("
"); - response->print(""); - request->send(response); + _response.concat("
"); + _response.concat(""); + _response.concat(""); + sendResponse(request); } void WebCfgServer::buildHtml(AsyncWebServerRequest *request) { String header = ""; - AsyncResponseStream *response = request->beginResponseStream("text/html"); - buildHtmlHeader(response, header); + _response = ""; + buildHtmlHeader(header); - if(_rebootRequired) response->print("
REBOOT REQUIRED TO APPLY SETTINGS
"); + 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
"); + #ifdef DEBUG_NUKIHUB + _response.concat("
RUNNING DEBUG BUILD, SWITCH TO RELEASE BUILD ASAP
"); + #endif - response->print("

Info


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

Info


"); + _response.concat("
"); - printParameter(response, "Hostname", _hostname.c_str(), "", "hostname"); - printParameter(response, "MQTT Connected", _network->mqttConnectionState() > 0 ? "Yes" : "No", "", "mqttState"); + printParameter("Hostname", _hostname.c_str(), "", "hostname"); + printParameter("MQTT Connected", _network->mqttConnectionState() > 0 ? "Yes" : "No", "", "mqttState"); if(_nuki != nullptr) { char lockStateArr[20]; NukiLock::lockstateToString(_nuki->keyTurnerState().lockState, lockStateArr); - printParameter(response, "Nuki Lock paired", _nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No", "", "lockPaired"); - printParameter(response, "Nuki Lock state", lockStateArr, "", "lockState"); + printParameter("Nuki Lock paired", _nuki->isPaired() ? ("Yes (BLE Address " + _nuki->getBleAddress().toString() + ")").c_str() : "No", "", "lockPaired"); + printParameter("Nuki Lock state", lockStateArr, "", "lockState"); if(_nuki->isPaired()) { String lockState = pinStateToString(_preferences->getInt(preference_lock_pin_status, 4)); - printParameter(response, "Nuki Lock PIN status", lockState.c_str(), "", "lockPin"); + printParameter("Nuki Lock PIN status", lockState.c_str(), "", "lockPin"); if(_preferences->getBool(preference_official_hybrid, false)) { String offConnected = _nuki->offConnected() ? "Yes": "No"; - printParameter(response, "Nuki Lock hybrid mode connected", offConnected.c_str(), "", "lockHybrid"); + printParameter("Nuki Lock hybrid mode connected", offConnected.c_str(), "", "lockHybrid"); } } } @@ -2559,231 +2590,232 @@ void WebCfgServer::buildHtml(AsyncWebServerRequest *request) { char openerStateArr[20]; NukiOpener::lockstateToString(_nukiOpener->keyTurnerState().lockState, openerStateArr); - printParameter(response, "Nuki Opener paired", _nukiOpener->isPaired() ? ("Yes (BLE Address " + _nukiOpener->getBleAddress().toString() + ")").c_str() : "No", "", "openerPaired"); + printParameter("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->keyTurnerState().nukiState == NukiOpener::State::ContinuousMode) printParameter("Nuki Opener state", "Open (Continuous Mode)", "", "openerState"); + else printParameter("Nuki Opener state", openerStateArr, "", "openerState"); if(_nukiOpener->isPaired()) { String openerState = pinStateToString(_preferences->getInt(preference_opener_pin_status, 4)); - printParameter(response, "Nuki Opener PIN status", openerState.c_str(), "", "openerPin"); + printParameter("Nuki Opener PIN status", openerState.c_str(), "", "openerPin"); } } - 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"); + 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"); if(_preferences->getInt(preference_network_hardware, 0) == 11) { - buildNavigationMenuEntry(response, "Custom Ethernet Configuration", "/custntw"); + buildNavigationMenuEntry("Custom Ethernet Configuration", "/custntw"); } if (_preferences->getBool(preference_publish_debug_info, false)) { - buildNavigationMenuEntry(response, "Advanced Configuration", "/advanced"); + buildNavigationMenuEntry("Advanced Configuration", "/advanced"); } if(_preferences->getBool(preference_webserial_enabled, false)) { - buildNavigationMenuEntry(response, "Open Webserial", "/webserial"); + buildNavigationMenuEntry("Open Webserial", "/webserial"); } #ifndef CONFIG_IDF_TARGET_ESP32H2 - if(_allowRestartToPortal) buildNavigationMenuEntry(response, "Configure Wi-Fi", "/wifi"); + if(_allowRestartToPortal) buildNavigationMenuEntry("Configure Wi-Fi", "/wifi"); #endif - buildNavigationMenuEntry(response, "Reboot Nuki Hub", "/reboot"); - response->print("
    "); - request->send(response); + buildNavigationMenuEntry("Reboot Nuki Hub", "/reboot"); + _response.concat("
"); + sendResponse(request); } void WebCfgServer::buildCredHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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("
"); + _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("
"); if(_nuki != nullptr) { - response->print("

"); - response->print("

Nuki Lock PIN

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

"); + _response.concat("

Nuki Lock PIN

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

"); - response->print("

Nuki Opener PIN

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

"); + _response.concat("

Nuki Opener PIN

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

Unpair Nuki Lock

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

Unpair Nuki Lock

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

Unpair Nuki Opener

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

Unpair Nuki Opener

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

Factory reset Nuki Hub

"); - response->print("

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

Factory reset Nuki Hub

"); + _response.concat("

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

"); - response->print("
"); - response->print(""); + _response.concat(""); + _response.concat(""); + _response.concat("
"); String message = "Type "; message.concat(_confirmCode); message.concat(" to confirm factory reset"); - printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10, ""); + printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); #ifndef CONFIG_IDF_TARGET_ESP32H2 - printCheckBox(response, "WIFI", "Also reset WiFi settings", false, ""); + printCheckBox("WIFI", "Also reset WiFi settings", false, ""); #endif - response->print("
"); - response->print("
"); - response->print(""); - request->send(response); + _response.concat(""); + _response.concat("
"); + _response.concat(""); + sendResponse(request); } void WebCfgServer::buildMqttConfigHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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 = ""; + 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("

"); - 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(), ""); + _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(), ""); #ifndef CONFIG_IDF_TARGET_ESP32H2 - 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, ""); + 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, ""); #endif - 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, "WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_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), ""); - response->print("
"); - response->print("* If no encryption is configured for the MQTT broker, leave empty.

"); + 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), ""); + _response.concat(""); + _response.concat("* If no encryption is configured for the MQTT broker, leave empty.

"); - 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(""); - request->send(response); + _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); } void WebCfgServer::buildAdvancedConfigHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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(""); - response->print(""); - 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\""); - printCheckBox(response, "SHOWSECRETS", "Show Pairing secrets on Info page (for 120s after next boot)", _preferences->getBool(preference_show_secrets), ""); + _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("
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
"); + _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), ""); if(_preferences->getBool(preference_lock_enabled, true)) { - 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, ""); + 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, ""); } if(_preferences->getBool(preference_opener_enabled, false)) { - 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, ""); + 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, ""); } - printInputField(response, "OTAUPD", "Custom URL to update Nuki Hub updater", "", 255, ""); - printInputField(response, "OTAMAIN", "Custom URL to update Nuki Hub", "", 255, ""); - 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
"); + printInputField("OTAUPD", "Custom URL to update Nuki Hub updater", "", 255, ""); + printInputField("OTAMAIN", "Custom URL to update Nuki Hub", "", 255, ""); + _response.concat(""); - response->print("
"); - response->print("
"); - response->print(""); - request->send(response); + _response.concat("
"); + _response.concat(""); + _response.concat(""); + sendResponse(request); } void WebCfgServer::buildStatusHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("application/json"); + _response = ""; JsonDocument json; char _resbuf[2048]; bool mqttDone = false; @@ -2847,8 +2879,8 @@ void WebCfgServer::buildStatusHtml(AsyncWebServerRequest *request) if(mqttDone && lockDone && openerDone && latestDone) json["stop"] = 1; serializeJson(json, _resbuf, sizeof(_resbuf)); - response->print(_resbuf); - request->send(response); + _response.concat(_resbuf); + sendResponse(request); } String WebCfgServer::pinStateToString(uint8_t value) { @@ -2865,249 +2897,228 @@ String WebCfgServer::pinStateToString(uint8_t value) { } } -void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request, int aclPart) +void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request) { - String partString = ""; - AsyncResponseStream *response; + _response = ""; + buildHtmlHeader(); - if(aclPart == 0) - { - response = request->beginResponseStream("text/html"); - buildHtmlHeader(response); - } - else response = request->beginResponseStream("text/plain"); - - partAccLvlHtml(partString, aclPart); - response->print(partString); - request->send(response); -} - -void WebCfgServer::partAccLvlHtml(String &partString, int aclPart) -{ uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); - switch(aclPart) + _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), ""); + + if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) { - case 0: - partString.concat(""); - partString.concat(""); - partString.concat("

Nuki General Access Control

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

"); - partString.concat("
"); - partString.concat("
"); - partString.concat("
"); - partString.concat("
"); - partString.concat(""); - partString.concat(""); - break; - case 1: - 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)); - - partString.concat("

Nuki Lock Access Control

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

"); - - partString.concat("

Nuki Lock Config Control (Requires PIN to be set)

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

"); - break; - case 2: - 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)); - - partString.concat("

Nuki Opener Access Control

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

"); - - partString.concat("

Nuki Opener Config Control (Requires PIN to be set)

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

"); - break; - default: - break; + 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("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("
"); + _response.concat("
"); + _response.concat("
"); + _response.concat("
"); + _response.concat(""); + + if(_nuki != nullptr) + { + 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("

Nuki Lock Access Control

"); + _response.concat(""); + _response.concat(""); + _response.concat(""); + + 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

"); + + _response.concat("

Nuki Lock Config Control (Requires PIN to be set)

"); + _response.concat(""); + _response.concat(""); + _response.concat(""); + + 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("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

"); + } + if(_nukiOpener != nullptr) + { + 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("

Nuki Opener Access Control

"); + _response.concat(""); + _response.concat(""); + _response.concat(""); + + 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

"); + + _response.concat("

Nuki Opener Config Control (Requires PIN to be set)

"); + _response.concat(""); + _response.concat(""); + _response.concat(""); + + 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("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(""); + sendResponse(request); } void WebCfgServer::buildNukiConfigHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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(""); + _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(""); - 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, ""); + 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, ""); if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) { - printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10, ""); + printInputField("KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 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, ""); + 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, ""); - response->print("
"); - response->print("
"); - response->print("
"); - response->print(""); - request->send(response); + _response.concat(""); + _response.concat("
"); + _response.concat(""); + _response.concat(""); + sendResponse(request); } void WebCfgServer::buildGpioConfigHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - buildHtmlHeader(response); - response->print("
"); - response->print("

GPIO Configuration

"); - response->print(""); + _response = ""; + buildHtmlHeader(); + _response.concat(""); + _response.concat("

GPIO Configuration

"); + _response.concat("
"); std::vector> options; String gpiopreselects = "var gpio = []; "; @@ -3118,129 +3129,129 @@ void WebCfgServer::buildGpioConfigHtml(AsyncWebServerRequest *request) { String pinStr = String(pin); String pinDesc = "Gpio " + pinStr; - printDropDown(response, pinStr.c_str(), pinDesc.c_str(), "", options, "gpioselect"); + printDropDown(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->print("
"); - response->print("
"); - response->print("
"); + _response.concat(""); + _response.concat("
"); + _response.concat(""); options = getGpioOptions(); - response->print(""); - response->print(""); - request->send(response); + _response.concat("'; 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.concat(""); + sendResponse(request); } #ifndef CONFIG_IDF_TARGET_ESP32H2 void WebCfgServer::buildConfigureWifiHtml(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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(""); - request->send(response); + _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); } #endif void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request) { + _response = ""; uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); - AsyncResponseStream *response = request->beginResponseStream("text/html"); - 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);
+    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);
     #ifndef DEBUG_NUKIHUB
-    response->print("\nBuild type: Release");
+    _response.concat("\nBuild type: Release");
     #else
-    response->print("\nBuild type: Debug");
+    _response.concat("\nBuild type: Debug");
     #endif
-    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");
+    _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");
     if(_network->isConnected())
     {
-        response->print("\nIP Address: ");
-        response->print(_network->localIP());
+        _response.concat("\nIP Address: ");
+        _response.concat(_network->localIP());
 
         if(_network->networkDeviceName() == "Built-in Wi-Fi")
         {
             #ifndef CONFIG_IDF_TARGET_ESP32H2
-            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());
+            _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());
             #endif
         }
         else
@@ -3248,274 +3259,274 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
             //Ethernet info
         }
     }
-    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");
+    _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");
     else
     {
-        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, ""));
+        _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, ""));
     }
 
     #ifndef CONFIG_IDF_TARGET_ESP32H2
-    response->print("\nFallback to Wi-Fi / Wi-Fi config portal disabled: ");
-    response->print(_preferences->getBool(preference_network_wifi_fallback_disabled, false) ? "Yes" : "No");
+    _response.concat("\nFallback to Wi-Fi / Wi-Fi config portal disabled: ");
+    _response.concat(_preferences->getBool(preference_network_wifi_fallback_disabled, false) ? "Yes" : "No");
     if(_network->networkDeviceName() == "Built-in Wi-Fi")
     {
-        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): ");
+        _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): ");
 
-        if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) response->print("Disabled");
-        else response->print(_preferences->getInt(preference_rssi_publish_interval, 60));
+        if(_preferences->getInt(preference_rssi_publish_interval, 60) < 0) _response.concat("Disabled");
+        else _response.concat(_preferences->getInt(preference_rssi_publish_interval, 60));
     }
     #endif
-    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");
+    _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");
     if(_preferences->getBool(preference_lock_enabled, true))
     {
-        response->print("\nMQTT lock base topic: ");
-        response->print(_preferences->getString(preference_mqtt_lock_path, ""));
+        _response.concat("\nMQTT lock base topic: ");
+        _response.concat(_preferences->getString(preference_mqtt_lock_path, ""));
     }
     if(_preferences->getBool(preference_opener_enabled, false))
     {
-        response->print("\nMQTT opener base topic: ");
-        response->print(_preferences->getString(preference_mqtt_lock_path, ""));
+        _response.concat("\nMQTT opener base topic: ");
+        _response.concat(_preferences->getString(preference_mqtt_lock_path, ""));
     }
-    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: ");
+    _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: ");
     if(_preferences->getString(preference_mqtt_hass_discovery, "").length() > 0)
     {
-        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());
+        _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());
     }
-    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("No");
+    _response.concat("\n\n------------ NUKI LOCK ------------");
+    if(_nuki == nullptr || !_preferences->getBool(preference_lock_enabled, true)) _response.concat("\nLock enabled: No");
     else
     {
-        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");
+        _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");
         if(_nuki->hasKeypad())
         {
-            response->print("\nKeypad highest entries count: ");
-            response->print(_preferences->getInt(preference_lock_max_keypad_code_count, 0));
+            _response.concat("\nKeypad highest entries count: ");
+            _response.concat(_preferences->getInt(preference_lock_max_keypad_code_count, 0));
         }
-        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");
+        _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");
         else
         {
-            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");
+            _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");
             if(_preferences->getBool(preference_official_hybrid_actions, false))
             {
-                response->print("\nRetry actions through BLE enabled: ");
-                response->print(_preferences->getBool(preference_official_hybrid_retry, false) ? "Yes" : "No");
+                _response.concat("\nRetry actions through BLE enabled: ");
+                _response.concat(_preferences->getBool(preference_official_hybrid_retry, false) ? "Yes" : "No");
             }
-            response->print("\nTime between status updates when official MQTT is offline (s): ");
-            response->print(_preferences->getInt(preference_query_interval_hybrid_lockstate, 600));
+            _response.concat("\nTime between status updates when official MQTT is offline (s): ");
+            _response.concat(_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->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");
+        _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");
 
         if(_preferences->getBool(preference_show_secrets))
         {
@@ -3529,151 +3540,151 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
             nukiBlePref.getBytes("secretKeyK", secretKeyK, 32);
             nukiBlePref.getBytes("authorizationId", authorizationId, 4);
             nukiBlePref.end();
-            response->print("\n\n------------ NUKI LOCK PAIRING ------------");
-            response->print("\nBLE Address: ");
+            _response.concat("\n\n------------ NUKI LOCK PAIRING ------------");
+            _response.concat("\nBLE Address: ");
             for (int i = 0; i < 6; i++)
             {
                 sprintf(tmp, "%02x", currentBleAddress[i]);
-                response->print(tmp);
+                _response.concat(tmp);
             }
-            response->print("\nSecretKeyK: ");
+            _response.concat("\nSecretKeyK: ");
             for (int i = 0; i < 32; i++)
             {
                 sprintf(tmp, "%02x", secretKeyK[i]);
-                response->print(tmp);
+                _response.concat(tmp);
             }
-            response->print("\nAuthorizationId: ");
+            _response.concat("\nAuthorizationId: ");
             for (int i = 0; i < 4; i++)
             {
                 sprintf(tmp, "%02x", authorizationId[i]);
-                response->print(tmp);
+                _response.concat(tmp);
             }
             uint32_t authorizationIdInt = authorizationId[0] + 256U*authorizationId[1] + 65536U*authorizationId[2] + 16777216U*authorizationId[3];
-            response->print("\nAuthorizationId (UINT32_T): ");
-            response->print(authorizationIdInt);
+            _response.concat("\nAuthorizationId (UINT32_T): ");
+            _response.concat(authorizationIdInt);
         }
     }
 
-    response->print("\n\n------------ NUKI OPENER ------------");
-    if(_nukiOpener == nullptr || !_preferences->getBool(preference_opener_enabled, false)) response->print("\nOpener enabled: No");
+    _response.concat("\n\n------------ NUKI OPENER ------------");
+    if(_nukiOpener == nullptr || !_preferences->getBool(preference_opener_enabled, false)) _response.concat("\nOpener enabled: No");
     else
     {
-        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");
+        _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");
         if(_nuki->hasKeypad())
         {
-            response->print("\nKeypad highest entries count: ");
-            response->print(_preferences->getInt(preference_opener_max_keypad_code_count, 0));
+            _response.concat("\nKeypad highest entries count: ");
+            _response.concat(_preferences->getInt(preference_opener_max_keypad_code_count, 0));
         }
-        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");
+        _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");
         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->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");
+        _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");
         if(_preferences->getBool(preference_show_secrets))
         {
             char tmp[16];
@@ -3686,35 +3697,34 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request)
             nukiBlePref.getBytes("secretKeyK", secretKeyKOpn, 32);
             nukiBlePref.getBytes("authorizationId", authorizationIdOpn, 4);
             nukiBlePref.end();
-            response->print("\n\n------------ NUKI OPENER PAIRING ------------");
-            response->print("\nBLE Address: ");
+            _response.concat("\n\n------------ NUKI OPENER PAIRING ------------");
+            _response.concat("\nBLE Address: ");
             for (int i = 0; i < 6; i++)
             {
                 sprintf(tmp, "%02x", currentBleAddressOpn[i]);
-                response->print(tmp);
+                _response.concat(tmp);
             }
-            response->print("\nSecretKeyK: ");
+            _response.concat("\nSecretKeyK: ");
             for (int i = 0; i < 32; i++)
             {
                 sprintf(tmp, "%02x", secretKeyKOpn[i]);
-                response->print(tmp);
+                _response.concat(tmp);
             }
-            response->print("\nAuthorizationId: ");
+            _response.concat("\nAuthorizationId: ");
             for (int i = 0; i < 4; i++)
             {
                 sprintf(tmp, "%02x", authorizationIdOpn[i]);
-                response->print(tmp);
+                _response.concat(tmp);
             }
         }
     }
 
-    response->print("\n\n------------ GPIO ------------\n");
+    _response.concat("\n\n------------ GPIO ------------\n");
     String gpioStr = "";
     _gpio->getConfigurationText(gpioStr, _gpio->pinConfiguration());
-    response->print(gpioStr);
-
-    response->print("
"); - request->send(response); + _response.concat(gpioStr); + _response.concat("
"); + sendResponse(request); } void WebCfgServer::processUnpair(AsyncWebServerRequest *request, bool opener) @@ -3733,6 +3743,7 @@ void WebCfgServer::processUnpair(AsyncWebServerRequest *request, bool opener) } buildConfirmHtml(request, opener ? "Unpairing Nuki Opener and restarting." : "Unpairing Nuki Lock and restarting.", 3, true); + if(!opener && _nuki != nullptr) { _nuki->disableHASS(); @@ -3840,7 +3851,10 @@ void WebCfgServer::processFactoryReset(AsyncWebServerRequest *request) resetWifi = true; 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); + else + { + buildConfirmHtml(request, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener.", 3, true); + } } waitAndProcess(false, 2000); @@ -3874,8 +3888,7 @@ void WebCfgServer::processFactoryReset(AsyncWebServerRequest *request) restartEsp(RestartReason::NukiHubReset); } -void WebCfgServer::printInputField(AsyncResponseStream *response, - const char *token, +void WebCfgServer::printInputField(const char *token, const char *description, const char *value, const size_t& maxLength, @@ -3887,39 +3900,38 @@ void WebCfgServer::printInputField(AsyncResponseStream *response, itoa(maxLength, maxLengthStr, 10); - response->print(""); - response->print(description); + _response.concat(""); + _response.concat(description); if(showLengthRestriction) { - response->print(" (Max. "); - response->print(maxLength); - response->print(" characters)"); + _response.concat(" (Max. "); + _response.concat(maxLength); + _response.concat(" characters)"); } - response->print(""); - response->print(""); + _response.concat("print(" "); - response->print(args); + _response.concat(" "); + _response.concat(args); } if(strcmp(value, "") != 0) { - response->print(" value=\""); - response->print(value); + _response.concat(" value=\""); + _response.concat(value); } - response->print("\" name=\""); - response->print(token); - response->print("\" size=\"25\" maxlength=\""); - response->print(maxLengthStr); - response->print("\"/>"); - response->print(""); + _response.concat("\" name=\""); + _response.concat(token); + _response.concat("\" size=\"25\" maxlength=\""); + _response.concat(maxLengthStr); + _response.concat("\"/>"); + _response.concat(""); } -void WebCfgServer::printInputField(AsyncResponseStream *response, - const char *token, +void WebCfgServer::printInputField(const char *token, const char *description, const int value, size_t maxLength, @@ -3927,55 +3939,32 @@ void WebCfgServer::printInputField(AsyncResponseStream *response, { char valueStr[20]; itoa(value, valueStr, 10); - printInputField(response, token, description, valueStr, maxLength, args); + printInputField(token, description, valueStr, maxLength, args); } -void WebCfgServer::printCheckBox(AsyncResponseStream *response, const char *token, const char *description, const bool value, const char *htmlClass) +void WebCfgServer::printCheckBox(const char *token, const char *description, const bool value, const char *htmlClass) { - response->print(""); - response->print(description); - response->print(""); + _response.concat(""); + _response.concat(description); + _response.concat(""); - response->print("print(token); - response->print("\" value=\"0\""); - response->print("/>"); + _response.concat(""); - response->print("print(token); + _response.concat("print("\" class=\""); - response->print(htmlClass); + _response.concat("\" class=\""); + _response.concat(htmlClass); - response->print("\" value=\"1\""); - response->print(value ? " checked=\"checked\"" : ""); - response->print("/>"); + _response.concat("\" value=\"1\""); + _response.concat(value ? " checked=\"checked\"" : ""); + _response.concat("/>"); } -void WebCfgServer::printCheckBox(String &partString, const char *token, const char *description, const bool value, const char *htmlClass) -{ - partString.concat(""); - partString.concat(description); - partString.concat(""); - - partString.concat(""); - - partString.concat(""); -} - -void WebCfgServer::printTextarea(AsyncResponseStream *response, - const char *token, +void WebCfgServer::printTextarea(const char *token, const char *description, const char *value, const size_t& maxLength, @@ -3986,106 +3975,106 @@ void WebCfgServer::printTextarea(AsyncResponseStream *response, itoa(maxLength, maxLengthStr, 10); - response->print(""); - response->print(description); + _response.concat(""); + _response.concat(description); if(showLengthRestriction) { - response->print(" (Max. "); - response->print(maxLength); - response->print(" characters)"); + _response.concat(" (Max. "); + _response.concat(maxLength); + _response.concat(" characters)"); } - response->print(""); - response->print(" "); - response->print(""); + _response.concat(" name=\""); + _response.concat(token); + _response.concat("\" maxlength=\""); + _response.concat(maxLengthStr); + _response.concat("\">"); + _response.concat(value); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::printDropDown(AsyncResponseStream *response, const char *token, const char *description, const String preselectedValue, const std::vector> options, const String className) +void WebCfgServer::printDropDown(const char *token, const char *description, const String preselectedValue, const std::vector> options, const String className) { - response->print(""); - response->print(description); - response->print(""); + _response.concat(""); + _response.concat(description); + _response.concat(""); - if(className.length() > 0) response->print("print(token); - response->print("\">"); + if(className.length() > 0) _response.concat(""); - response->print(""); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildNavigationButton(AsyncResponseStream *response, const char *caption, const char *targetPath, const char* labelText) +void WebCfgServer::buildNavigationButton(const char *caption, const char *targetPath, const char* labelText) { - response->print("
print(targetPath); - response->print("\">"); - response->print(" "); - response->print(labelText); - response->print("
"); + _response.concat("
"); + _response.concat(" "); + _response.concat(labelText); + _response.concat("
"); } -void WebCfgServer::buildNavigationMenuEntry(AsyncResponseStream *response, const char *title, const char *targetPath, const char* warningMessage) +void WebCfgServer::buildNavigationMenuEntry(const char *title, const char *targetPath, const char* warningMessage) { - response->print("print(targetPath); - response->print("\">"); - response->print("
  • "); - response->print(title); + _response.concat(""); + _response.concat("
  • "); + _response.concat(title); if(strcmp(warningMessage, "") != 0){ - response->print(""); - response->print(warningMessage); - response->print(""); + _response.concat(""); + _response.concat(warningMessage); + _response.concat(""); } - response->print("
  • "); + _response.concat(""); } -void WebCfgServer::printParameter(AsyncResponseStream *response, const char *description, const char *value, const char *link, const char *id) +void WebCfgServer::printParameter(const char *description, const char *value, const char *link, const char *id) { - response->print(""); - response->print(""); - response->print(description); - response->print(""); - if(strcmp(id, "") == 0) response->print(""); + _response.concat(""); + _response.concat(""); + _response.concat(description); + _response.concat(""); + if(strcmp(id, "") == 0) _response.concat(""); else { - response->print("print(id); - response->print("\">"); + _response.concat(""); } - if(strcmp(link, "") == 0) response->print(value); + if(strcmp(link, "") == 0) _response.concat(value); else { - response->print("print(link); - response->print("\"> "); - response->print(value); - response->print(""); + _response.concat(" "); + _response.concat(value); + _response.concat(""); } - response->print(""); - response->print(""); + _response.concat(""); + _response.concat(""); } diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index d85619b..583175d 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -52,7 +52,7 @@ private: bool processImport(AsyncWebServerRequest *request, String& message); void processGpioArgs(AsyncWebServerRequest *request); void buildHtml(AsyncWebServerRequest *request); - void buildAccLvlHtml(AsyncWebServerRequest *request, int aclPart = 0); + void buildAccLvlHtml(AsyncWebServerRequest *request); void buildCredHtml(AsyncWebServerRequest *request); void buildImportExportHtml(AsyncWebServerRequest *request); void buildMqttConfigHtml(AsyncWebServerRequest *request); @@ -68,15 +68,13 @@ private: void processUnpair(AsyncWebServerRequest *request, bool opener); void processUpdate(AsyncWebServerRequest *request); void processFactoryReset(AsyncWebServerRequest *request); - void printInputField(AsyncResponseStream *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(AsyncResponseStream *response, const char* token, const char* description, const int value, size_t maxLength, const char* args); - void printCheckBox(AsyncResponseStream *response, const char* token, const char* description, const bool value, const char* htmlClass); - void printCheckBox(String &partString, const char* token, const char* description, const bool value, const char* htmlClass); - void printTextarea(AsyncResponseStream *response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false); - void printDropDown(AsyncResponseStream *response, const char *token, const char *description, const String preselectedValue, std::vector> options, const String className); - void buildNavigationButton(AsyncResponseStream *response, const char* caption, const char* targetPath, const char* labelText = ""); - void buildNavigationMenuEntry(AsyncResponseStream *response, const char *title, const char *targetPath, const char* warningMessage = ""); - void partAccLvlHtml(String &partString, int aclPart); + 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 = ""); const std::vector> getNetworkDetectionOptions() const; const std::vector> getGpioOptions() const; @@ -88,7 +86,7 @@ private: String getPreselectionForGpio(const uint8_t& pin); String pinStateToString(uint8_t value); - void printParameter(AsyncResponseStream *response, const char* description, const char* value, const char *link = "", const char *id = ""); + void printParameter(const char* description, const char* value, const char *link = "", const char *id = ""); NukiWrapper* _nuki = nullptr; NukiOpenerWrapper* _nukiOpener = nullptr; @@ -98,6 +96,7 @@ 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); @@ -105,10 +104,11 @@ private: void buildOtaCompletedHtml(AsyncWebServerRequest *request); void sendCss(AsyncWebServerRequest *request); void sendFavicon(AsyncWebServerRequest *request); - void buildHtmlHeader(AsyncResponseStream *response, String additionalHeader = ""); + void buildHtmlHeader(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 printProgress(size_t prg, size_t sz); + void sendResponse(AsyncWebServerRequest *request); AsyncWebServer* _asyncServer = nullptr; NukiNetwork* _network = nullptr; diff --git a/src/main.cpp b/src/main.cpp index 7858f0b..e1ae161 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -115,15 +115,16 @@ void setReroute(){ void networkTask(void *pvParameters) { int64_t networkLoopTs = 0; - bool secrets = preferences->getBool(preference_show_secrets, false); bool reroute = true; - + if(preferences->getBool(preference_show_secrets, false)) + { + preferences->putBool(preference_show_secrets, false); + } while(true) { int64_t ts = (esp_timer_get_time() / 1000); if(ts > 120000 && ts < 125000) { - if(secrets) preferences->putBool(preference_show_secrets, false); if(bootloopCounter > 0) { bootloopCounter = (int8_t)0; diff --git a/src/networkDevices/EthernetDevice.cpp b/src/networkDevices/EthernetDevice.cpp index ecbf108..4a9f7cc 100644 --- a/src/networkDevices/EthernetDevice.cpp +++ b/src/networkDevices/EthernetDevice.cpp @@ -52,8 +52,6 @@ EthernetDevice::EthernetDevice(const String &hostname, void EthernetDevice::init() { - _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect); - #ifndef NUKI_HUB_UPDATER size_t caLength = _preferences->getString(preference_mqtt_ca, _ca, TLS_CA_MAX_SIZE); size_t crtLength = _preferences->getString(preference_mqtt_crt, _cert, TLS_CERT_MAX_SIZE); @@ -253,7 +251,7 @@ ReconnectStatus EthernetDevice::reconnect(bool force) void EthernetDevice::onDisconnected() { - if(_restartOnDisconnect && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); + if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); reconnect(); } diff --git a/src/networkDevices/EthernetDevice.h b/src/networkDevices/EthernetDevice.h index 4fd0095..dfb45dd 100644 --- a/src/networkDevices/EthernetDevice.h +++ b/src/networkDevices/EthernetDevice.h @@ -67,7 +67,6 @@ private: void onNetworkEvent(arduino_event_id_t event, arduino_event_info_t info); bool _connected = false; - bool _restartOnDisconnect = false; char* _path; bool _hardwareInitialized = false; diff --git a/src/networkDevices/WifiDevice.cpp b/src/networkDevices/WifiDevice.cpp index 1c8c9d3..dc32369 100644 --- a/src/networkDevices/WifiDevice.cpp +++ b/src/networkDevices/WifiDevice.cpp @@ -18,8 +18,6 @@ WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const I _startAp = strcmp(WiFiDevice_reconfdetect, "reconfigure_wifi") == 0; #ifndef NUKI_HUB_UPDATER - _restartOnDisconnect = preferences->getBool(preference_restart_on_disconnect, false); - size_t caLength = preferences->getString(preference_mqtt_ca, _ca, TLS_CA_MAX_SIZE); size_t crtLength = preferences->getString(preference_mqtt_crt, _cert, TLS_CERT_MAX_SIZE); size_t keyLength = preferences->getString(preference_mqtt_key, _key, TLS_KEY_MAX_SIZE); @@ -49,11 +47,11 @@ WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const I if(preferences->getBool(preference_mqtt_log_enabled, false) || preferences->getBool(preference_webserial_enabled, false)) { MqttLoggerMode mode; - + if(preferences->getBool(preference_mqtt_log_enabled, false) && preferences->getBool(preference_webserial_enabled, false)) mode = MqttLoggerMode::MqttAndSerialAndWeb; else if (preferences->getBool(preference_webserial_enabled, false)) mode = MqttLoggerMode::SerialAndWeb; else mode = MqttLoggerMode::MqttAndSerial; - + _path = new char[200]; memset(_path, 0, sizeof(_path)); @@ -72,15 +70,14 @@ const String WifiDevice::deviceName() const void WifiDevice::initialize() { - _wifiFallbackDisabled = _preferences->getBool(preference_network_wifi_fallback_disabled, false); std::vector wm_menu; wm_menu.push_back("wifi"); wm_menu.push_back("exit"); - _wm.setEnableConfigPortal(_startAp || !_wifiFallbackDisabled); + _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); // reduced timeout if ESP is set to restart on disconnect _wm.setFindBestRSSI(_preferences->getBool(preference_find_best_rssi)); _wm.setConnectTimeout(20); - _wm.setConfigPortalTimeout(_restartOnDisconnect ? 60 * 3 : 60 * 30); + _wm.setConfigPortalTimeout(_preferences->getBool(preference_restart_on_disconnect, false) ? 60 * 3 : 60 * 30); _wm.setShowInfoUpdate(false); _wm.setMenu(wm_menu); _wm.setHostname(_hostname); @@ -94,7 +91,7 @@ void WifiDevice::initialize() bool res = false; bool connectedFromPortal = false; - + if(_startAp) { Log->println(F("Opening Wi-Fi configuration portal.")); @@ -119,7 +116,7 @@ void WifiDevice::initialize() else { Log->print(F("Wi-Fi connected: ")); Log->println(WiFi.localIP().toString()); - + if(connectedFromPortal) { Log->println(F("Connected using WifiManager portal. Wait for ESP restart.")); @@ -160,37 +157,39 @@ bool WifiDevice::isConnected() ReconnectStatus WifiDevice::reconnect(bool force) { + _wm.setFindBestRSSI(_preferences->getBool(preference_find_best_rssi)); + if((!isConnected() || force) && !_isReconnecting) { _isReconnecting = true; WiFi.disconnect(); int loop = 0; - + while(isConnected() && loop <20) { delay(100); loop++; } - + _wm.resetScan(); _wm.autoConnect(); _isReconnecting = false; } - if(!isConnected() && _disconnectTs > (esp_timer_get_time() / 1000) - 120000) _wm.setEnableConfigPortal(_startAp || !_wifiFallbackDisabled); + if(!isConnected() && _disconnectTs > (esp_timer_get_time() / 1000) - 120000) _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); return isConnected() ? ReconnectStatus::Success : ReconnectStatus::Failure; } void WifiDevice::onConnected() { _isReconnecting = false; - _wm.setEnableConfigPortal(_startAp || !_wifiFallbackDisabled); + _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); } void WifiDevice::onDisconnected() { _disconnectTs = (esp_timer_get_time() / 1000); - if(_restartOnDisconnect && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); + if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); _wm.setEnableConfigPortal(false); reconnect(); } diff --git a/src/networkDevices/WifiDevice.h b/src/networkDevices/WifiDevice.h index cdd5eba..26aec2f 100644 --- a/src/networkDevices/WifiDevice.h +++ b/src/networkDevices/WifiDevice.h @@ -38,10 +38,8 @@ private: WiFiManager _wm; Preferences* _preferences = nullptr; - bool _restartOnDisconnect = false; bool _startAp = false; bool _isReconnecting = false; - bool _wifiFallbackDisabled = false; char* _path; int64_t _disconnectTs = 0;