diff --git a/include/RTOS.h b/include/RTOS.h deleted file mode 100644 index b4a3449..0000000 --- a/include/RTOS.h +++ /dev/null @@ -1 +0,0 @@ -// #include \ No newline at end of file diff --git a/ota/beta/nuki_hub_esp32.bin b/ota/beta/nuki_hub_esp32.bin deleted file mode 100644 index 23a9c4b..0000000 Binary files a/ota/beta/nuki_hub_esp32.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_esp32c3.bin b/ota/beta/nuki_hub_esp32c3.bin deleted file mode 100644 index 1bdc5ca..0000000 Binary files a/ota/beta/nuki_hub_esp32c3.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_esp32c6.bin b/ota/beta/nuki_hub_esp32c6.bin deleted file mode 100644 index 1f1e4eb..0000000 Binary files a/ota/beta/nuki_hub_esp32c6.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_esp32s3.bin b/ota/beta/nuki_hub_esp32s3.bin deleted file mode 100644 index 4699d2d..0000000 Binary files a/ota/beta/nuki_hub_esp32s3.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_esp32solo1.bin b/ota/beta/nuki_hub_esp32solo1.bin deleted file mode 100644 index 72a92cc..0000000 Binary files a/ota/beta/nuki_hub_esp32solo1.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_updater_esp32.bin b/ota/beta/nuki_hub_updater_esp32.bin deleted file mode 100644 index 9e0a5e7..0000000 Binary files a/ota/beta/nuki_hub_updater_esp32.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_updater_esp32c3.bin b/ota/beta/nuki_hub_updater_esp32c3.bin deleted file mode 100644 index 784290f..0000000 Binary files a/ota/beta/nuki_hub_updater_esp32c3.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_updater_esp32c6.bin b/ota/beta/nuki_hub_updater_esp32c6.bin deleted file mode 100644 index 888c825..0000000 Binary files a/ota/beta/nuki_hub_updater_esp32c6.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_updater_esp32s3.bin b/ota/beta/nuki_hub_updater_esp32s3.bin deleted file mode 100644 index 65509c7..0000000 Binary files a/ota/beta/nuki_hub_updater_esp32s3.bin and /dev/null differ diff --git a/ota/beta/nuki_hub_updater_esp32solo1.bin b/ota/beta/nuki_hub_updater_esp32solo1.bin deleted file mode 100644 index e0f8233..0000000 Binary files a/ota/beta/nuki_hub_updater_esp32solo1.bin and /dev/null differ diff --git a/ota/manifest.json b/ota/manifest.json deleted file mode 100644 index 648eeef..0000000 --- a/ota/manifest.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "release": { - "time": "2024-07-18 00:00:00", - "version": "8.35", - "fullversion": "8.35", - "build": "" - }, - "beta": { - "time": "2024-08-01 14:05:28", - "version": "9.00", - "fullversion": "9.00-beta4", - "build": "10199751147.8.1", - "number": 4 - }, - "master": { - "time": "2024-08-01 14:21:27", - "version": "9.00", - "fullversion": "9.00-master4", - "build": "10199923947.18.1", - "number": 4 - } -} \ No newline at end of file diff --git a/ota/master/nuki_hub_esp32.bin b/ota/master/nuki_hub_esp32.bin deleted file mode 100644 index c351192..0000000 Binary files a/ota/master/nuki_hub_esp32.bin and /dev/null differ diff --git a/ota/master/nuki_hub_esp32c3.bin b/ota/master/nuki_hub_esp32c3.bin deleted file mode 100644 index b9a44c4..0000000 Binary files a/ota/master/nuki_hub_esp32c3.bin and /dev/null differ diff --git a/ota/master/nuki_hub_esp32c6.bin b/ota/master/nuki_hub_esp32c6.bin deleted file mode 100644 index ca32555..0000000 Binary files a/ota/master/nuki_hub_esp32c6.bin and /dev/null differ diff --git a/ota/master/nuki_hub_esp32s3.bin b/ota/master/nuki_hub_esp32s3.bin deleted file mode 100644 index 3566c95..0000000 Binary files a/ota/master/nuki_hub_esp32s3.bin and /dev/null differ diff --git a/ota/master/nuki_hub_esp32solo1.bin b/ota/master/nuki_hub_esp32solo1.bin deleted file mode 100644 index 1c1269f..0000000 Binary files a/ota/master/nuki_hub_esp32solo1.bin and /dev/null differ diff --git a/ota/master/nuki_hub_updater_esp32.bin b/ota/master/nuki_hub_updater_esp32.bin deleted file mode 100644 index 11b9132..0000000 Binary files a/ota/master/nuki_hub_updater_esp32.bin and /dev/null differ diff --git a/ota/master/nuki_hub_updater_esp32c3.bin b/ota/master/nuki_hub_updater_esp32c3.bin deleted file mode 100644 index c39e595..0000000 Binary files a/ota/master/nuki_hub_updater_esp32c3.bin and /dev/null differ diff --git a/ota/master/nuki_hub_updater_esp32c6.bin b/ota/master/nuki_hub_updater_esp32c6.bin deleted file mode 100644 index d2d117c..0000000 Binary files a/ota/master/nuki_hub_updater_esp32c6.bin and /dev/null differ diff --git a/ota/master/nuki_hub_updater_esp32s3.bin b/ota/master/nuki_hub_updater_esp32s3.bin deleted file mode 100644 index 4f21b65..0000000 Binary files a/ota/master/nuki_hub_updater_esp32s3.bin and /dev/null differ diff --git a/ota/master/nuki_hub_updater_esp32solo1.bin b/ota/master/nuki_hub_updater_esp32solo1.bin deleted file mode 100644 index 7554b30..0000000 Binary files a/ota/master/nuki_hub_updater_esp32solo1.bin and /dev/null differ diff --git a/ota/nuki_hub_esp32.bin b/ota/nuki_hub_esp32.bin deleted file mode 100644 index 6996959..0000000 Binary files a/ota/nuki_hub_esp32.bin and /dev/null differ diff --git a/ota/nuki_hub_esp32c3.bin b/ota/nuki_hub_esp32c3.bin deleted file mode 100644 index e0c9742..0000000 Binary files a/ota/nuki_hub_esp32c3.bin and /dev/null differ diff --git a/ota/nuki_hub_esp32s3.bin b/ota/nuki_hub_esp32s3.bin deleted file mode 100644 index 560f38a..0000000 Binary files a/ota/nuki_hub_esp32s3.bin and /dev/null differ diff --git a/ota/nuki_hub_esp32solo1.bin b/ota/nuki_hub_esp32solo1.bin deleted file mode 100644 index 6e1dbdb..0000000 Binary files a/ota/nuki_hub_esp32solo1.bin and /dev/null differ diff --git a/pio_package.py b/pio_package.py index 65cd0ab..4115b8e 100644 --- a/pio_package.py +++ b/pio_package.py @@ -1,8 +1,7 @@ """ PlatformIO POST script execution to copy artifacts """ Import("env") -import os -import shutil +import re, shutil, os from pathlib import Path def get_board_name(env): @@ -77,4 +76,14 @@ env.AddPostAction("$BUILD_DIR/bootloader.bin", copy_files) if env.GetProjectOption("custom_build") == 'debug': env.AddPostAction("$BUILD_DIR/firmware.elf", copy_files) -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_bin) \ No newline at end of file +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_bin) + +regex = r"\#define NUKI_HUB_DATE \"(.*)\"" +content_new = "" + +with open ('src/Config.h', 'r' ) as readfile: + file_content = readfile.read() + content_new = re.sub(regex, "#define NUKI_HUB_DATE \"unknownbuilddate\"", file_content, flags = re.M) + +with open('src/Config.h', 'w') as writefile: + writefile.write(content_new) \ No newline at end of file diff --git a/pio_package_pre.py b/pio_package_pre.py index 4cccda2..8b7152e 100644 --- a/pio_package_pre.py +++ b/pio_package_pre.py @@ -1,6 +1,15 @@ +Import("env") import re, shutil, os from datetime import datetime, timezone +def recursive_purge(dir, pattern): + if os.path.isdir(dir): + for f in os.listdir(dir): + if os.path.isdir(os.path.join(dir, f)): + recursive_purge(os.path.join(dir, f), pattern) + elif re.search(pattern, os.path.join(dir, f)): + os.remove(os.path.join(dir, f)) + regex = r"\#define NUKI_HUB_DATE \"(.*)\"" content_new = "" @@ -10,3 +19,16 @@ with open ('src/Config.h', 'r' ) as readfile: with open('src/Config.h', 'w') as writefile: writefile.write(content_new) + +recursive_purge("managed_components", ".component_hash") + +if env.get('BOARD_MCU') == "esp32": + board = "esp32dev" +else: + board = env.get('BOARD_MCU') + +if os.path.exists("sdkconfig." + board): + os.remove("sdkconfig." + board) + +if os.path.exists("sdkconfig." + board + "_dbg"): + os.remove("sdkconfig." + board + "_dbg") \ No newline at end of file diff --git a/sdkconfig.debug.defaults b/sdkconfig.debug.defaults index 8e6203d..19ac7c9 100644 --- a/sdkconfig.debug.defaults +++ b/sdkconfig.debug.defaults @@ -1,3 +1,5 @@ CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y CONFIG_LOG_DEFAULT_LEVEL=4 -CONFIG_ESP_IPC_TASK_STACK_SIZE=8192 \ No newline at end of file +CONFIG_ESP_IPC_TASK_STACK_SIZE=8192 +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL=1 \ No newline at end of file diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 2590b3d..1577e67 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -75,6 +75,4 @@ CONFIG_ARDUINO_SELECTIVE_SimpleBLE=n CONFIG_HEAP_TASK_TRACKING=n CONFIG_LOG_COLORS=n CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=n -CONFIG_LOG_MAXIMUM_LEVEL=4 -CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y -CONFIG_LOG_BOOTLOADER_LEVEL=2 \ No newline at end of file +CONFIG_LOG_MAXIMUM_LEVEL=4 \ No newline at end of file diff --git a/sdkconfig.release.defaults b/sdkconfig.release.defaults index 9ccb244..7127d99 100644 --- a/sdkconfig.release.defaults +++ b/sdkconfig.release.defaults @@ -1,3 +1,5 @@ CONFIG_LOG_DEFAULT_LEVEL_NONE=y CONFIG_LOG_DEFAULT_LEVEL=0 -CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 \ No newline at end of file +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL=1 \ No newline at end of file diff --git a/src/Gpio.cpp b/src/Gpio.cpp index 4520617..2a55284 100644 --- a/src/Gpio.cpp +++ b/src/Gpio.cpp @@ -17,7 +17,7 @@ Gpio::Gpio(Preferences* preferences) _inst = this; loadPinConfiguration(); - if(_preferences->getBool(preference_gpio_locking_enabled)) + if(_preferences->getBool(preference_gpio_locking_enabled, false)) { migrateObsoleteSetting(); } diff --git a/src/NukiDeviceId.cpp b/src/NukiDeviceId.cpp index 561273d..5c9bd42 100644 --- a/src/NukiDeviceId.cpp +++ b/src/NukiDeviceId.cpp @@ -8,7 +8,7 @@ NukiDeviceId::NukiDeviceId(Preferences* preferences, const std::string& preferen : _preferences(preferences), _preferencesId(preferencesId) { - _deviceId = _preferences->getUInt(_preferencesId.c_str()); + _deviceId = _preferences->getUInt(_preferencesId.c_str(), 0); if(_deviceId == 0) { diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index 28b2fc1..34ec21f 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -215,7 +215,7 @@ NetworkDevice *NukiNetwork::device() #ifdef NUKI_HUB_UPDATER void NukiNetwork::initialize() { - _hostname = _preferences->getString(preference_hostname); + _hostname = _preferences->getString(preference_hostname, ""); if(_hostname == "") { @@ -240,9 +240,9 @@ 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) * 1000; - - _hostname = _preferences->getString(preference_hostname); + _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval, 0) * 1000; + _hostname = _preferences->getString(preference_hostname, ""); + _discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery, ""); if(_hostname == "") { @@ -251,8 +251,8 @@ void NukiNetwork::initialize() } if(_rssiPublishInterval == 0) { - _rssiPublishInterval = 60; - _preferences->putInt(preference_rssi_publish_interval, _rssiPublishInterval); + _rssiPublishInterval = 60000; + _preferences->putInt(preference_rssi_publish_interval, 60); } strcpy(_hostnameArr, _hostname.c_str()); _device->initialize(); @@ -263,7 +263,7 @@ void NukiNetwork::initialize() String brokerAddr = _preferences->getString(preference_mqtt_broker); strcpy(_mqttBrokerAddr, brokerAddr.c_str()); - int port = _preferences->getInt(preference_mqtt_broker_port); + int port = _preferences->getInt(preference_mqtt_broker_port, 0); if(port == 0) { port = 1883; @@ -299,14 +299,14 @@ void NukiNetwork::initialize() _device->mqttSetCleanSession(MQTT_CLEAN_SESSIONS); _device->mqttSetKeepAlive(MQTT_KEEP_ALIVE); - _networkTimeout = _preferences->getInt(preference_network_timeout); + _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); + _publishDebugInfo = _preferences->getBool(preference_publish_debug_info, false); char gpioPath[250]; bool rebGpio = rebuildGpio(); @@ -433,7 +433,7 @@ bool NukiNetwork::update() _lastConnectedTs = ts; -#if PRESENCE_DETECTION_ENABLED + #if PRESENCE_DETECTION_ENABLED if(_presenceDetection != nullptr && (_lastPresenceTs == 0 || (ts - _lastPresenceTs) > 3000)) { char* presenceCsv = _presenceDetection->generateCsv(); @@ -446,7 +446,7 @@ bool NukiNetwork::update() _lastPresenceTs = ts; } -#endif + #endif if(_device->signalStrength() != 127 && _rssiPublishInterval > 0 && ts - _lastRssiTs > _rssiPublishInterval) { @@ -515,7 +515,7 @@ bool NukiNetwork::update() if(strcmp(_latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, _latestVersion); } } - } + } https.end(); } delete client; @@ -592,7 +592,7 @@ void NukiNetwork::onMqttDisconnect(const espMqttClientTypes::DisconnectReason &r bool NukiNetwork::reconnect() { _mqttConnectionState = 0; - int port = _preferences->getInt(preference_mqtt_broker_port); + int port = _preferences->getInt(preference_mqtt_broker_port, 1883); while (!_device->mqttConnected() && (esp_timer_get_time() / 1000) > _nextReconnect) { @@ -907,7 +907,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha json["dev"]["sw"] = softwareVersion; json["dev"]["hw"] = hardwareVersion; - String cuUrl = _preferences->getString(preference_mqtt_hass_cu_url); + String cuUrl = _preferences->getString(preference_mqtt_hass_cu_url, ""); if (cuUrl != "") { @@ -942,7 +942,7 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha serializeJson(json, _buffer, _bufferSize); - String path = _preferences->getString(preference_mqtt_hass_discovery); + String path = _preferences->getString(preference_mqtt_hass_discovery, "homeassistant"); path.concat("/lock/"); path.concat(uidString); path.concat("/smartlock/config"); @@ -3453,9 +3453,7 @@ void NukiNetwork::publishHassTopic(const String& mqttDeviceType, std::vector> additionalEntries ) { - String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); - - if (discoveryTopic != "") + if (_discoveryTopic != "") { JsonDocument json; json = createHassJson(uidString, uidStringPostfix, displayName, name, baseTopic, stateTopic, deviceType, deviceClass, stateClass, entityCat, commandTopic, additionalEntries); @@ -3467,8 +3465,7 @@ void NukiNetwork::publishHassTopic(const String& mqttDeviceType, String NukiNetwork::createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) { - String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); - String path = discoveryTopic; + String path = _discoveryTopic; path.concat("/"); path.concat(mqttDeviceType); path.concat("/"); @@ -3482,9 +3479,7 @@ String NukiNetwork::createHassTopicPath(const String& mqttDeviceType, const Stri void NukiNetwork::removeHassTopic(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString) { - String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); - - if (discoveryTopic != "") + if (_discoveryTopic != "") { String path = createHassTopicPath(mqttDeviceType, mqttDeviceName, uidString); _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); @@ -3883,4 +3878,9 @@ String NukiNetwork::localIP() { return _device->localIP(); } + +bool NukiNetwork::isConnected() +{ + return _device->isConnected(); +} #endif \ No newline at end of file diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index d435f71..2f50112 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -55,6 +55,7 @@ public: void disableAutoRestarts(); // disable on OTA start void disableMqtt(); String localIP(); + bool isConnected(); void subscribe(const char* prefix, const char* path); void initTopic(const char* prefix, const char* path, const char* value); @@ -153,6 +154,7 @@ private: const char* _lastWillPayload = "offline"; char _mqttConnectionStateTopic[211] = {0}; String _lockPath; + String _discoveryTopic; PresenceDetection* _presenceDetection; Gpio* _gpio; diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 6c47281..12a4cff 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -40,7 +40,7 @@ NukiNetworkLock::~NukiNetworkLock() void NukiNetworkLock::initialize() { - String mqttPath = _preferences->getString(preference_mqtt_lock_path); + String mqttPath = _preferences->getString(preference_mqtt_lock_path, ""); if(mqttPath.length() > 0) { size_t len = mqttPath.length(); @@ -55,11 +55,13 @@ void NukiNetworkLock::initialize() _preferences->putString(preference_mqtt_lock_path, _mqttPath); } -#if PRESENCE_DETECTION_ENABLED + #if PRESENCE_DETECTION_ENABLED _network->setMqttPresencePath(_mqttPath); -#endif + #endif - _haEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; + _haEnabled = _preferences->getString(preference_mqtt_hass_discovery, "") != ""; + _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); + _offEnabled = _preferences->getBool(preference_official_hybrid, false); _network->initTopic(_mqttPath, mqtt_topic_lock_action, "--"); _network->subscribe(_mqttPath, mqtt_topic_lock_action); @@ -86,7 +88,7 @@ void NukiNetworkLock::initialize() _network->subscribe(_mqttPath, mqtt_topic_query_lockstate); _network->subscribe(_mqttPath, mqtt_topic_query_battery); - if(_preferences->getBool(preference_disable_non_json, false)) + if(_disableNonJSON) { _network->removeTopic(_mqttPath, mqtt_topic_keypad_command_action); _network->removeTopic(_mqttPath, mqtt_topic_keypad_command_id); @@ -125,7 +127,7 @@ void NukiNetworkLock::initialize() if(_preferences->getBool(preference_keypad_control_enabled)) { - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { _network->subscribe(_mqttPath, mqtt_topic_keypad_command_action); _network->subscribe(_mqttPath, mqtt_topic_keypad_command_id); @@ -151,7 +153,7 @@ void NukiNetworkLock::initialize() _network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--"); } - if(_preferences->getBool(preference_official_hybrid, false)) + if(_offEnabled) { char uidString[20]; itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16); @@ -253,7 +255,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const if(atoi(value) > 0 && atoi(value) > _lastRollingLog) _lastRollingLog = atoi(value); } - if(_preferences->getBool(preference_official_hybrid, false)) + if(_offEnabled) { for(auto offTopic : _offTopics) { @@ -301,7 +303,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const } } - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { if(comparePrefixedPath(topic, mqtt_topic_keypad_command_action)) { @@ -515,14 +517,14 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT jsonBattery["level"] = level; jsonBattery["keypadCritical"] = keypadCritical ? "1" : "0"; - if((_firstTunerStatePublish || keyTurnerState.criticalBatteryState != lastKeyTurnerState.criticalBatteryState) && !_preferences->getBool(preference_disable_non_json, false)) + if((_firstTunerStatePublish || keyTurnerState.criticalBatteryState != lastKeyTurnerState.criticalBatteryState) && !_disableNonJSON) { publishBool(mqtt_topic_battery_critical, critical, true); publishBool(mqtt_topic_battery_charging, charging, true); publishInt(mqtt_topic_battery_level, level, true); } - if((_firstTunerStatePublish || keyTurnerState.accessoryBatteryState != lastKeyTurnerState.accessoryBatteryState) && !_preferences->getBool(preference_disable_non_json, false)) + if((_firstTunerStatePublish || keyTurnerState.accessoryBatteryState != lastKeyTurnerState.accessoryBatteryState) && !_disableNonJSON) { publishBool(mqtt_topic_battery_keypad_critical, keypadCritical, true); } @@ -736,7 +738,7 @@ void NukiNetworkLock::publishLockstateCommandResult(const char *resultStr) void NukiNetworkLock::publishBatteryReport(const NukiLock::BatteryReport& batteryReport) { - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { publishFloat(mqtt_topic_battery_voltage, (float)batteryReport.batteryVoltage / 1000.0, true); publishInt(mqtt_topic_battery_drain, batteryReport.batteryDrain, true); // milliwatt seconds @@ -818,7 +820,7 @@ void NukiNetworkLock::publishConfig(const NukiLock::Config &config) serializeJson(json, _buffer, _bufferSize); publishString(mqtt_topic_config_basic_json, _buffer, true); - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { publishBool(mqtt_topic_config_button_enabled, config.buttonEnabled == 1, true); publishBool(mqtt_topic_config_led_enabled, config.ledEnabled == 1, true); @@ -873,7 +875,7 @@ void NukiNetworkLock::publishAdvancedConfig(const NukiLock::AdvancedConfig &conf serializeJson(json, _buffer, _bufferSize); publishString(mqtt_topic_config_advanced_json, _buffer, true); - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { publishBool(mqtt_topic_config_auto_unlock, config.autoUnLockDisabled == 0, true); publishBool(mqtt_topic_config_auto_lock, config.autoLockEnabled == 1, true); @@ -897,6 +899,8 @@ void NukiNetworkLock::publishBleAddress(const std::string &address) void NukiNetworkLock::publishKeypad(const std::list& entries, uint maxKeypadCodeCount) { + bool publishCode = _preferences->getBool(preference_keypad_publish_code, false); + bool topicPerEntry = _preferences->getBool(preference_keypad_topic_per_entry, false); uint index = 0; char uidString[20]; itoa(_preferences->getUInt(preference_nuki_id_lock, 0), uidString, 16); @@ -914,11 +918,7 @@ void NukiNetworkLock::publishKeypad(const std::list& entr jsonEntry["codeId"] = entry.codeId; - if(_preferences->getBool(preference_keypad_publish_code, false)) - { - jsonEntry["code"] = entry.code; - } - + if(publishCode) jsonEntry["code"] = entry.code; jsonEntry["enabled"] = entry.enabled; jsonEntry["name"] = entry.name; char createdDT[20]; @@ -991,7 +991,7 @@ void NukiNetworkLock::publishKeypad(const std::list& entr sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin); jsonEntry["allowedUntilTime"] = allowedUntilTimeT; - if(_preferences->getBool(preference_keypad_topic_per_entry, false)) + if(topicPerEntry) { basePath = mqtt_topic_keypad; basePath.concat("/codes/"); @@ -1042,7 +1042,7 @@ void NukiNetworkLock::publishKeypad(const std::list& entr serializeJson(json, _buffer, _bufferSize); publishString(mqtt_topic_keypad_json, _buffer, true); - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { while(index < maxKeypadCodeCount) { @@ -1056,7 +1056,7 @@ void NukiNetworkLock::publishKeypad(const std::list& entr ++index; } - if(!_preferences->getBool(preference_keypad_publish_code, false)) + if(!publishCode) { for(int i=0; i& entr void NukiNetworkLock::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry) { - if(_preferences->getBool(preference_disable_non_json, false)) return; + if(_disableNonJSON) return; char codeName[sizeof(entry.name) + 1]; memset(codeName, 0, sizeof(codeName)); @@ -1133,6 +1133,7 @@ void NukiNetworkLock::publishKeypadEntry(const String topic, NukiLock::KeypadEnt void NukiNetworkLock::publishTimeControl(const std::list& timeControlEntries, uint maxTimeControlEntryCount) { + bool topicPerEntry = _preferences->getBool(preference_timecontrol_topic_per_entry, false); uint index = 0; char str[50]; char uidString[20]; @@ -1202,7 +1203,7 @@ void NukiNetworkLock::publishTimeControl(const std::listgetBool(preference_timecontrol_topic_per_entry, false)) + if(topicPerEntry) { String basePath = mqtt_topic_timecontrol; basePath.concat("/entries/"); @@ -1267,7 +1268,7 @@ void NukiNetworkLock::publishConfigCommandResult(const char* result) void NukiNetworkLock::publishKeypadCommandResult(const char* result) { - if(_preferences->getBool(preference_disable_non_json, false)) return; + if(_disableNonJSON) return; publishString(mqtt_topic_keypad_command_result, result, true); } @@ -1303,7 +1304,7 @@ void NukiNetworkLock::setConfigUpdateReceivedCallback(void (*configUpdateReceive void NukiNetworkLock::setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(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; _keypadCommandReceivedReceivedCallback = keypadCommandReceivedReceivedCallback; } diff --git a/src/NukiNetworkLock.h b/src/NukiNetworkLock.h index 0a88058..22240b0 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -109,6 +109,8 @@ private: int64_t _lastMaintenanceTs = 0; bool _haEnabled = false; bool _reconnected = false; + bool _disableNonJSON = false; + bool _offEnabled = false; String _keypadCommandName = ""; String _keypadCommandCode = ""; diff --git a/src/NukiNetworkOpener.cpp b/src/NukiNetworkOpener.cpp index 433f094..b2173a8 100644 --- a/src/NukiNetworkOpener.cpp +++ b/src/NukiNetworkOpener.cpp @@ -35,7 +35,8 @@ void NukiNetworkOpener::initialize() _preferences->putString(preference_mqtt_opener_path, _mqttPath); } - _haEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; + _haEnabled = _preferences->getString(preference_mqtt_hass_discovery, "") != ""; + _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); _network->initTopic(_mqttPath, mqtt_topic_lock_action, "--"); _network->subscribe(_mqttPath, mqtt_topic_lock_action); @@ -50,7 +51,7 @@ void NukiNetworkOpener::initialize() _network->subscribe(_mqttPath, mqtt_topic_query_lockstate); _network->subscribe(_mqttPath, mqtt_topic_query_battery); - if(_preferences->getBool(preference_disable_non_json, false)) + if(_disableNonJSON) { _network->removeTopic(_mqttPath, mqtt_topic_keypad_command_action); _network->removeTopic(_mqttPath, mqtt_topic_keypad_command_id); @@ -81,9 +82,9 @@ void NukiNetworkOpener::initialize() _network->removeTopic(_mqttPath, mqtt_topic_config_single_lock); } - if(_preferences->getBool(preference_keypad_control_enabled)) + if(_preferences->getBool(preference_keypad_control_enabled, false)) { - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { _network->subscribe(_mqttPath, mqtt_topic_keypad_command_action); _network->subscribe(_mqttPath, mqtt_topic_keypad_command_id); @@ -103,7 +104,7 @@ void NukiNetworkOpener::initialize() _network->initTopic(_mqttPath, mqtt_topic_keypad_json_action, "--"); } - if(_preferences->getBool(preference_timecontrol_control_enabled)) + if(_preferences->getBool(preference_timecontrol_control_enabled, false)) { _network->subscribe(_mqttPath, mqtt_topic_timecontrol_action); _network->initTopic(_mqttPath, mqtt_topic_timecontrol_action, "--"); @@ -181,7 +182,7 @@ void NukiNetworkOpener::onMqttDataReceived(const char* topic, byte* payload, con } } - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { if(comparePrefixedPath(topic, mqtt_topic_keypad_command_action)) { @@ -367,7 +368,7 @@ void NukiNetworkOpener::publishKeyTurnerState(const NukiOpener::OpenerState& key bool critical = (keyTurnerState.criticalBatteryState & 0b00000001) > 0; jsonBattery["critical"] = critical ? "1" : "0"; - if((_firstTunerStatePublish || keyTurnerState.criticalBatteryState != lastKeyTurnerState.criticalBatteryState) && !_preferences->getBool(preference_disable_non_json, false)) + if((_firstTunerStatePublish || keyTurnerState.criticalBatteryState != lastKeyTurnerState.criticalBatteryState) && !_disableNonJSON) { publishBool(mqtt_topic_battery_critical, critical, true); } @@ -623,7 +624,7 @@ void NukiNetworkOpener::publishLockstateCommandResult(const char *resultStr) void NukiNetworkOpener::publishBatteryReport(const NukiOpener::BatteryReport& batteryReport) { - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { publishFloat(mqtt_topic_battery_voltage, (float)batteryReport.batteryVoltage / 1000.0, true); } @@ -697,7 +698,7 @@ void NukiNetworkOpener::publishConfig(const NukiOpener::Config &config) serializeJson(json, _buffer, _bufferSize); publishString(mqtt_topic_config_basic_json, _buffer, true); - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { publishBool(mqtt_topic_config_button_enabled, config.buttonEnabled == 1, true); publishBool(mqtt_topic_config_led_enabled, config.ledFlashEnabled == 1, true); @@ -753,7 +754,7 @@ void NukiNetworkOpener::publishAdvancedConfig(const NukiOpener::AdvancedConfig & serializeJson(json, _buffer, _bufferSize); publishString(mqtt_topic_config_advanced_json, _buffer, true); - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { publishUInt(mqtt_topic_config_sound_level, config.soundLevel, true); } @@ -808,6 +809,8 @@ void NukiNetworkOpener::removeHASSConfig(char* uidString) void NukiNetworkOpener::publishKeypad(const std::list& entries, uint maxKeypadCodeCount) { + bool publishCode = _preferences->getBool(preference_keypad_publish_code, false); + bool topicPerEntry = _preferences->getBool(preference_keypad_topic_per_entry, false); uint index = 0; char uidString[20]; itoa(_preferences->getUInt(preference_nuki_id_opener, 0), uidString, 16); @@ -825,11 +828,7 @@ void NukiNetworkOpener::publishKeypad(const std::list& en jsonEntry["codeId"] = entry.codeId; - if(_preferences->getBool(preference_keypad_publish_code, false)) - { - jsonEntry["code"] = entry.code; - } - + if(publishCode) jsonEntry["code"] = entry.code; jsonEntry["enabled"] = entry.enabled; jsonEntry["name"] = entry.name; char createdDT[20]; @@ -902,7 +901,7 @@ void NukiNetworkOpener::publishKeypad(const std::list& en sprintf(allowedUntilTimeT, "%02d:%02d", entry.allowedUntilTimeHour, entry.allowedUntilTimeMin); jsonEntry["allowedUntilTime"] = allowedUntilTimeT; - if(_preferences->getBool(preference_keypad_topic_per_entry, false)) + if(topicPerEntry) { basePath = mqtt_topic_keypad; basePath.concat("/codes/"); @@ -953,7 +952,7 @@ void NukiNetworkOpener::publishKeypad(const std::list& en serializeJson(json, _buffer, _bufferSize); publishString(mqtt_topic_keypad_json, _buffer, true); - if(!_preferences->getBool(preference_disable_non_json, false)) + if(!_disableNonJSON) { while(index < maxKeypadCodeCount) { @@ -967,7 +966,7 @@ void NukiNetworkOpener::publishKeypad(const std::list& en ++index; } - if(!_preferences->getBool(preference_keypad_publish_code, false)) + if(!publishCode) { for(int i=0; i& en void NukiNetworkOpener::publishTimeControl(const std::list& timeControlEntries, uint maxTimeControlEntryCount) { + bool topicPerEntry = _preferences->getBool(preference_timecontrol_topic_per_entry, false); uint index = 0; char str[50]; char uidString[20]; @@ -1085,7 +1085,7 @@ void NukiNetworkOpener::publishTimeControl(const std::listgetBool(preference_timecontrol_topic_per_entry, false)) + if(topicPerEntry) { String basePath = mqtt_topic_timecontrol; basePath.concat("/entries/"); @@ -1149,7 +1149,7 @@ void NukiNetworkOpener::publishConfigCommandResult(const char* result) void NukiNetworkOpener::publishKeypadCommandResult(const char* result) { - if(_preferences->getBool(preference_disable_non_json, false)) return; + if(_disableNonJSON) return; publishString(mqtt_topic_keypad_command_result, result, true); } @@ -1180,7 +1180,7 @@ void NukiNetworkOpener::setConfigUpdateReceivedCallback(void (*configUpdateRecei void NukiNetworkOpener::setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(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; _keypadCommandReceivedReceivedCallback = keypadCommandReceivedReceivedCallback; } @@ -1237,7 +1237,7 @@ void NukiNetworkOpener::publishString(const char* topic, const char* value, bool void NukiNetworkOpener::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry) { - if(_preferences->getBool(preference_disable_non_json, false)) return; + if(_disableNonJSON) return; char codeName[sizeof(entry.name) + 1]; memset(codeName, 0, sizeof(codeName)); diff --git a/src/NukiNetworkOpener.h b/src/NukiNetworkOpener.h index ccedc6c..d137295 100644 --- a/src/NukiNetworkOpener.h +++ b/src/NukiNetworkOpener.h @@ -82,8 +82,9 @@ private: char _mqttPath[181] = {0}; bool _isConnected = false; bool _firstTunerStatePublish = true; - bool _haEnabled= false; + bool _haEnabled = false; bool _reconnected = false; + bool _disableNonJSON = false; String _keypadCommandName = ""; String _keypadCommandCode = ""; diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index 3918d0e..0dac01b 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -1,5 +1,4 @@ #include "NukiOpenerWrapper.h" -#include #include "PreferencesKeys.h" #include "MqttTopics.h" #include "Logger.h" @@ -51,14 +50,16 @@ void NukiOpenerWrapper::initialize() esp_power_level_t powerLevel; - if(_preferences->getInt(preference_ble_tx_power, 9) >= 9) powerLevel = ESP_PWR_LVL_P9; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= 6) powerLevel = ESP_PWR_LVL_P6; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= 3) powerLevel = ESP_PWR_LVL_P6; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= 0) powerLevel = ESP_PWR_LVL_P3; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -3) powerLevel = ESP_PWR_LVL_N3; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -6) powerLevel = ESP_PWR_LVL_N6; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -9) powerLevel = ESP_PWR_LVL_N9; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -12) powerLevel = ESP_PWR_LVL_N12; + 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; _nukiOpener.setPower(powerLevel); _nukiOpener.registerBleScanner(_bleScanner); @@ -76,6 +77,8 @@ void NukiOpenerWrapper::initialize() _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries, 200); _retryDelay = _preferences->getInt(preference_command_retry_delay); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; + _preferences->getBytes(preference_conf_opener_basic_acl, &_basicOpenerConfigAclPrefs, sizeof(_basicOpenerConfigAclPrefs)); + _preferences->getBytes(preference_conf_opener_advanced_acl, &_advancedOpenerConfigAclPrefs, sizeof(_advancedOpenerConfigAclPrefs)); if(_nrOfRetries < 0 || _nrOfRetries == 200) { @@ -477,7 +480,8 @@ void NukiOpenerWrapper::updateConfig() _firmwareVersion = std::to_string(_nukiConfig.firmwareVersion[0]) + "." + std::to_string(_nukiConfig.firmwareVersion[1]) + "." + std::to_string(_nukiConfig.firmwareVersion[2]); _hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]); if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig); - if(_preferences->getBool(preference_timecontrol_info_enabled)) updateTimeControl(false); + _retryConfigCount = 0; + if(_preferences->getBool(preference_timecontrol_info_enabled, false)) updateTimeControl(false); const int pinStatus = _preferences->getInt(preference_opener_pin_status, 4); @@ -993,13 +997,6 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) const char *advancedKeys[20] = {"intercomID", "busModeSwitch", "shortCircuitDuration", "electricStrikeDelay", "randomElectricStrikeDelay", "electricStrikeDuration", "disableRtoAfterRing", "rtoTimeout", "doorbellSuppression", "doorbellSuppressionDuration", "soundRing", "soundOpen", "soundRto", "soundCm", "soundConfirmation", "soundLevel", "singleButtonPressAction", "doubleButtonPressAction", "batteryType", "automaticBatteryTypeDetection"}; bool basicUpdated = false; bool advancedUpdated = false; - uint32_t basicOpenerConfigAclPrefs[16]; - uint32_t advancedOpenerConfigAclPrefs[20]; - - nukiOpenerPreferences = new Preferences(); - nukiOpenerPreferences->begin("nukihub", true); - nukiOpenerPreferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs)); - nukiOpenerPreferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs)); for(int i=0; i < 14; i++) { @@ -1013,7 +1010,7 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) continue; } - if((int)basicOpenerConfigAclPrefs[i] == 1) + if((int)_basicOpenerConfigAclPrefs[i] == 1) { cmdResult = Nuki::CmdResult::Error; _retryCount = 0; @@ -1203,7 +1200,7 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) continue; } - if((int)advancedOpenerConfigAclPrefs[j] == 1) + if((int)_advancedOpenerConfigAclPrefs[j] == 1) { cmdResult = Nuki::CmdResult::Error; _retryCount = 0; @@ -1449,8 +1446,6 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *value) } } - nukiOpenerPreferences->end(); - if(basicUpdated || advancedUpdated) jsonResult["general"] = "success"; else jsonResult["general"] = "noChange"; @@ -1501,7 +1496,7 @@ void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint { if(_preferences->getBool(preference_disable_non_json, false)) return; - if(!_preferences->getBool(preference_keypad_control_enabled)) + if(!_preferences->getBool(preference_keypad_control_enabled, false)) { _network->publishKeypadCommandResult("KeypadControlDisabled"); return; @@ -1637,7 +1632,7 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value) return; } - if(!_preferences->getBool(preference_keypad_control_enabled)) + if(!_preferences->getBool(preference_keypad_control_enabled, false)) { _network->publishKeypadJsonCommandResult("keypadControlDisabled"); return; @@ -2097,7 +2092,7 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value) return; } - if(!_preferences->getBool(preference_timecontrol_control_enabled)) + if(!_preferences->getBool(preference_timecontrol_control_enabled, false)) { _network->publishTimeControlCommandResult("timeControlControlDisabled"); return; @@ -2399,7 +2394,7 @@ void NukiOpenerWrapper::setupHASS() char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - if(_preferences->getBool(preference_opener_continuous_mode)) _network->publishHASSConfig((char*)"Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, _firmwareVersion.c_str(), _hardwareVersion.c_str(), _publishAuthData, _hasKeypad, (char*)"deactivateCM", (char*)"activateCM", (char*)"electricStrikeActuation"); + if(_preferences->getBool(preference_opener_continuous_mode, false)) _network->publishHASSConfig((char*)"Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, _firmwareVersion.c_str(), _hardwareVersion.c_str(), _publishAuthData, _hasKeypad, (char*)"deactivateCM", (char*)"activateCM", (char*)"electricStrikeActuation"); else _network->publishHASSConfig((char*)"Opener", baseTopic.c_str(), (char*)_nukiConfig.name, uidString, _firmwareVersion.c_str(), _hardwareVersion.c_str(), _publishAuthData, _hasKeypad, (char*)"deactivateRTO", (char*)"activateRTO", (char*)"electricStrikeActuation"); _hassSetupCompleted = true; diff --git a/src/NukiOpenerWrapper.h b/src/NukiOpenerWrapper.h index b95eceb..de43423 100644 --- a/src/NukiOpenerWrapper.h +++ b/src/NukiOpenerWrapper.h @@ -140,6 +140,8 @@ private: int64_t _nextRssiTs = 0; int64_t _lastRssi = 0; int64_t _disableBleWatchdogTs = 0; + uint32_t _basicOpenerConfigAclPrefs[16]; + uint32_t _advancedOpenerConfigAclPrefs[20]; std::string _firmwareVersion = ""; std::string _hardwareVersion = ""; NukiOpener::LockAction _nextLockAction = (NukiOpener::LockAction)0xff; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 2c63a06..19706eb 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -1,5 +1,4 @@ #include "NukiWrapper.h" -#include #include "PreferencesKeys.h" #include "MqttTopics.h" #include "Logger.h" @@ -35,7 +34,7 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, NukiDeviceId* deviceId, network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback); network->setOfficialUpdateReceivedCallback(nukiInst->onOfficialUpdateReceivedCallback); network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback); - if(_preferences->getBool(preference_disable_non_json, false)) network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback); + if(_disableNonJSON) network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback); network->setKeypadJsonCommandReceivedCallback(nukiInst->onKeypadJsonCommandReceivedCallback); network->setTimeControlCommandReceivedCallback(nukiInst->onTimeControlCommandReceivedCallback); @@ -54,15 +53,16 @@ void NukiWrapper::initialize(const bool& firstStart) _nukiLock.initialize(); esp_power_level_t powerLevel; + int pwrLvl = _preferences->getInt(preference_ble_tx_power, 9); - if(_preferences->getInt(preference_ble_tx_power, 9) >= 9) powerLevel = ESP_PWR_LVL_P9; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= 6) powerLevel = ESP_PWR_LVL_P6; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= 3) powerLevel = ESP_PWR_LVL_P6; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= 0) powerLevel = ESP_PWR_LVL_P3; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -3) powerLevel = ESP_PWR_LVL_N3; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -6) powerLevel = ESP_PWR_LVL_N6; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -9) powerLevel = ESP_PWR_LVL_N9; - else if(_preferences->getInt(preference_ble_tx_power, 9) >= -12) powerLevel = ESP_PWR_LVL_N12; + 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); @@ -81,6 +81,11 @@ void NukiWrapper::initialize(const bool& firstStart) _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)); if(firstStart) { @@ -128,9 +133,9 @@ void NukiWrapper::initialize(const bool& firstStart) _preferences->putInt(preference_query_interval_configuration, 3600); _preferences->putInt(preference_query_interval_battery, 1800); _preferences->putInt(preference_query_interval_keypad, 1800); -#if PRESENCE_DETECTION_ENABLED + #if PRESENCE_DETECTION_ENABLED _preferences->putInt(preference_presence_detection_timeout, -1); -#endif + #endif } if(_nrOfRetries < 0 || _nrOfRetries == 200) @@ -898,6 +903,7 @@ LockActionResult NukiWrapper::onLockActionReceivedCallback(const char *value) } nukiLockPreferences->end(); + return LockActionResult::AccessDenied; } @@ -1041,10 +1047,7 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value) Log->print(F("Lockstate: ")); Log->println(str); - if(_preferences->getString(preference_mqtt_hass_discovery) != "") - { - _network->publishState((NukiLock::LockState)_network->_offState); - } + _network->publishState((NukiLock::LockState)_network->_offState); } else if(strcmp(topic, mqtt_topic_official_doorsensorState) == 0) { @@ -1066,7 +1069,7 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value) Log->print(F("Battery critical: ")); Log->println(_network->_offCritical); - if(!_preferences->getBool(preference_disable_non_json, false)) _network->publishBool(mqtt_topic_battery_critical, _network->_offCritical, true); + if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_critical, _network->_offCritical, true); publishBatteryJson = true; } else if(strcmp(topic, mqtt_topic_official_batteryCharging) == 0) @@ -1076,7 +1079,7 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value) Log->print(F("Battery charging: ")); Log->println(_network->_offCharging); - if(!_preferences->getBool(preference_disable_non_json, false)) _network->publishBool(mqtt_topic_battery_charging, _network->_offCharging, true); + if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_charging, _network->_offCharging, true); publishBatteryJson = true; } else if(strcmp(topic, mqtt_topic_official_batteryChargeState) == 0) @@ -1086,19 +1089,19 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value) Log->print(F("Battery level: ")); Log->println(_network->_offChargeState); - if(!_preferences->getBool(preference_disable_non_json, false)) _network->publishInt(mqtt_topic_battery_level, _network->_offChargeState, true); + if(!_disableNonJSON) _network->publishInt(mqtt_topic_battery_level, _network->_offChargeState, true); publishBatteryJson = true; } else if(strcmp(topic, mqtt_topic_official_keypadBatteryCritical) == 0) { _network->_offKeypadCritical = (strcmp(value, "true") == 0 ? 1 : 0); - if(!_preferences->getBool(preference_disable_non_json, false)) _network->publishBool(mqtt_topic_battery_keypad_critical, _network->_offKeypadCritical, true); + if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_keypad_critical, _network->_offKeypadCritical, true); publishBatteryJson = true; } else if(strcmp(topic, mqtt_topic_official_doorsensorBatteryCritical) == 0) { _network->_offDoorsensorCritical = (strcmp(value, "true") == 0 ? 1 : 0); - if(!_preferences->getBool(preference_disable_non_json, false)) _network->publishBool(mqtt_topic_battery_doorsensor_critical, _network->_offDoorsensorCritical, true); + if(!_disableNonJSON) _network->publishBool(mqtt_topic_battery_doorsensor_critical, _network->_offDoorsensorCritical, true); publishBatteryJson = true; } else if(strcmp(topic, mqtt_topic_official_commandResponse) == 0) @@ -1197,13 +1200,6 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) const char *advancedKeys[22] = {"unlockedPositionOffsetDegrees", "lockedPositionOffsetDegrees", "singleLockedPositionOffsetDegrees", "unlockedToLockedTransitionOffsetDegrees", "lockNgoTimeout", "singleButtonPressAction", "doubleButtonPressAction", "detachedCylinder", "batteryType", "automaticBatteryTypeDetection", "unlatchDuration", "autoLockTimeOut", "autoUnLockDisabled", "nightModeEnabled", "nightModeStartTime", "nightModeEndTime", "nightModeAutoLockEnabled", "nightModeAutoUnlockDisabled", "nightModeImmediateLockOnStart", "autoLockEnabled", "immediateAutoLockEnabled", "autoUpdateEnabled"}; bool basicUpdated = false; bool advancedUpdated = false; - uint32_t basicLockConfigAclPrefs[16]; - uint32_t advancedLockConfigAclPrefs[22]; - - nukiLockPreferences = new Preferences(); - nukiLockPreferences->begin("nukihub", true); - nukiLockPreferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs)); - nukiLockPreferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs)); for(int i=0; i < 16; i++) { @@ -1217,7 +1213,7 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) continue; } - if((int)basicLockConfigAclPrefs[i] == 1) + if((int)_basicLockConfigaclPrefs[i] == 1) { cmdResult = Nuki::CmdResult::Error; _retryCount = 0; @@ -1429,7 +1425,7 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) continue; } - if((int)advancedLockConfigAclPrefs[j] == 1) + if((int)_advancedLockConfigaclPrefs[j] == 1) { cmdResult = Nuki::CmdResult::Error; _retryCount = 0; @@ -1701,8 +1697,6 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) } } - nukiLockPreferences->end(); - if(basicUpdated || advancedUpdated) jsonResult["general"] = "success"; else jsonResult["general"] = "noChange"; @@ -1731,9 +1725,6 @@ void NukiWrapper::onTimeControlCommandReceivedCallback(const char *value) void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin) { - nukiLockPreferences = new Preferences(); - nukiLockPreferences->begin("nukihub", true); - switch(action) { case GpioAction::Lock: @@ -1782,13 +1773,11 @@ void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin) } break; } - - nukiLockPreferences->end(); } void NukiWrapper::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)) { @@ -2619,7 +2608,7 @@ void NukiWrapper::notify(Nuki::EventType eventType) { if(!_network->_offConnected) { - if(_preferences->getBool(preference_official_hybrid, false) && _intervalHybridLockstate > 0 && (esp_timer_get_time() / 1000) > (_intervalHybridLockstate * 1000)) + if(_offEnabled && _intervalHybridLockstate > 0 && (esp_timer_get_time() / 1000) > (_intervalHybridLockstate * 1000)) { Log->println("OffKeyTurnerStatusUpdated"); _statusUpdated = true; diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index 424e3b8..93c8815 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -113,7 +113,8 @@ private: bool _nukiAdvancedConfigValid = false; bool _hassEnabled = false; bool _hassSetupCompleted = false; - + bool _offEnabled = false; + bool _disableNonJSON = false; bool _paired = false; bool _statusUpdated = false; bool _hasKeypad = false; @@ -139,6 +140,8 @@ private: int64_t _nextRssiTs = 0; int64_t _lastRssi = 0; int64_t _disableBleWatchdogTs = 0; + uint32_t _basicLockConfigaclPrefs[16]; + uint32_t _advancedLockConfigaclPrefs[22]; std::string _firmwareVersion = ""; std::string _hardwareVersion = ""; volatile NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff; diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index 8b1358c..b883b5a 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -103,12 +103,12 @@ inline bool initPreferences(Preferences* preferences) { -#ifdef NUKI_HUB_UPDATER + #ifdef NUKI_HUB_UPDATER bool firstStart = false; return firstStart; -#else + #else bool firstStart = !preferences->getBool(preference_started_before); -#endif + #endif preferences->remove(preference_bootloop_counter); @@ -289,145 +289,6 @@ private: { preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2 }; - const bool isRedacted(const char* key) const - { - return std::find(_redact.begin(), _redact.end(), key) != _redact.end(); - } - const String redact(const String s) const - { - return s == "" ? "" : "***"; - } - const String redact(const int32_t i) const - { - return i == 0 ? "" : "***"; - } - const String redact(const uint32_t i) const - { - return i == 0 ? "" : "***"; - } - const String redact(const int64_t i) const - { - return i == 0 ? "" : "***"; - } - const String redact(const uint64_t i) const - { - return i == 0 ? "" : "***"; - } - - const void appendPreferenceInt8(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const int32_t)preferences->getChar(key)) : String(preferences->getChar(key))); - s.concat("\n"); - } - const void appendPreferenceUInt8(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const uint32_t)preferences->getUChar(key)) : String(preferences->getUChar(key))); - s.concat("\n"); - } - const void appendPreferenceInt16(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const int32_t)preferences->getShort(key)) : String(preferences->getShort(key))); - s.concat("\n"); - } - const void appendPreferenceUInt16(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const uint32_t)preferences->getUShort(key)) : String(preferences->getUShort(key))); - s.concat("\n"); - } - const void appendPreferenceInt32(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const int32_t)preferences->getInt(key)) : String(preferences->getInt(key))); - s.concat("\n"); - } - const void appendPreferenceUInt32(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const uint32_t)preferences->getUInt(key)) : String(preferences->getUInt(key))); - s.concat("\n"); - } - const void appendPreferenceInt64(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const int64_t)preferences->getLong64(key)) : String(preferences->getLong64(key))); - s.concat("\n"); - } - const void appendPreferenceUInt64(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const uint64_t)preferences->getULong64(key)) : String(preferences->getULong64(key))); - s.concat("\n"); - } - const void appendPreferenceBool(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(preferences->getBool(key) ? "true" : "false"); - s.concat("\n"); - } - const void appendPreferenceString(Preferences *preferences, String& s, const char* description, const char* key) - { - s.concat(description); - s.concat(": "); - s.concat(isRedacted(key) ? redact((const String)preferences->getString(key)) : preferences->getString(key)); - s.concat("\n"); - } - - const void appendPreference(Preferences *preferences, String& s, const char* key) - { - if(std::find(_boolPrefs.begin(), _boolPrefs.end(), key) != _boolPrefs.end()) - { - appendPreferenceBool(preferences, s, key, key); - return; - } - - switch(preferences->getType(key)) - { - case PT_I8: - appendPreferenceInt8(preferences, s, key, key); - break; - case PT_I16: - appendPreferenceInt16(preferences, s, key, key); - break; - case PT_I32: - appendPreferenceInt32(preferences, s, key, key); - break; - case PT_I64: - appendPreferenceInt64(preferences, s, key, key); - break; - case PT_U8: - appendPreferenceUInt8(preferences, s, key, key); - break; - case PT_U16: - appendPreferenceUInt16(preferences, s, key, key); - break; - case PT_U32: - appendPreferenceUInt32(preferences, s, key, key); - break; - case PT_U64: - appendPreferenceUInt64(preferences, s, key, key); - break; - case PT_STR: - appendPreferenceString(preferences, s, key, key); - break; - default: - appendPreferenceString(preferences, s, key, key); - break; - } - } - public: const std::vector getPreferencesKeys() { @@ -453,16 +314,4 @@ public: { return _charPrefs; } - const String preferencesToString(Preferences *preferences) - { - String s = ""; - - for(const auto& key : _keys) - { - appendPreference(preferences, s, key); - } - - return s; - } - }; \ No newline at end of file diff --git a/src/PresenceDetection.cpp b/src/PresenceDetection.cpp index 733c14c..791f033 100644 --- a/src/PresenceDetection.cpp +++ b/src/PresenceDetection.cpp @@ -13,7 +13,7 @@ PresenceDetection::PresenceDetection(Preferences* preferences, BleScanner::Scann _csv(buffer), _bufferSize(bufferSize) { - _timeout = _preferences->getInt(preference_presence_detection_timeout) * 1000; + _timeout = _preferences->getInt(preference_presence_detection_timeout, 0) * 1000; if(_timeout == 0) { _timeout = 60000; diff --git a/src/RestartReason.h b/src/RestartReason.h index 36c6e71..e7b403e 100644 --- a/src/RestartReason.h +++ b/src/RestartReason.h @@ -3,6 +3,7 @@ enum class RestartReason { RequestedViaMqtt, + RequestedViaWebServer, BLEBeaconWatchdog, RestartOnDisconnectWatchdog, RestartIntervalWatchdog, @@ -69,6 +70,8 @@ inline static String getRestartReason() { case RestartReason::RequestedViaMqtt: return "RequestedViaMqtt"; + case RestartReason::RequestedViaWebServer: + return "RequestedViaWebServer"; case RestartReason::BLEBeaconWatchdog: return "BLEBeaconWatchdog"; case RestartReason::RestartOnDisconnectWatchdog: diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 4904b37..de89d13 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -31,8 +31,8 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, EthServer* ethServer, Preferenc _partitionType(partitionType) #endif { - _hostname = _preferences->getString(preference_hostname); - String str = _preferences->getString(preference_cred_user); + _hostname = _preferences->getString(preference_hostname, ""); + String str = _preferences->getString(preference_cred_user, ""); if(str.length() > 0) { @@ -43,7 +43,7 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, EthServer* ethServer, Preferenc const char *user = str.c_str(); memcpy(&_credUser, user, str.length()); - str = _preferences->getString(preference_cred_password); + str = _preferences->getString(preference_cred_password, ""); const char *pass = str.c_str(); memcpy(&_credPassword, pass, str.length()); } @@ -68,17 +68,18 @@ WebCfgServer::WebCfgServer(NukiNetwork* network, EthServer* ethServer, Preferenc void WebCfgServer::initialize() { + _response.reserve(8192); _server.on("/", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; + _response = ""; #ifndef NUKI_HUB_UPDATER - buildHtml(response); + buildHtml(); #else - buildOtaHtml(response, _server.arg("errored") != ""); + buildOtaHtml(_server.arg("errored") != ""); #endif - _server.send(200, "text/html", response); + _server.send(200, "text/html", _response); }); _server.on("/style.css", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { @@ -102,9 +103,9 @@ void WebCfgServer::initialize() bool restart = processImport(message); if(restart) { - String response = ""; - buildConfirmHtml(response, message); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml(message); + _server.send(200, "text/html", _response); Log->println(F("Restarting")); waitAndProcess(true, 1000); @@ -112,9 +113,9 @@ void WebCfgServer::initialize() } else { - String response = ""; - buildConfirmHtml(response, message, 3); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml(message, 3); + _server.send(200, "text/html", _response); waitAndProcess(false, 1000); } }); @@ -128,73 +129,73 @@ void WebCfgServer::initialize() if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildImportExportHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildImportExportHtml(); + _server.send(200, "text/html", _response); }); _server.on("/status", HTTP_GET, [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildStatusHtml(response); - _server.send(200, "application/json", response); + _response = ""; + buildStatusHtml(); + _server.send(200, "application/json", _response); }); _server.on("/acclvl", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildAccLvlHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildAccLvlHtml(); + _server.send(200, "text/html", _response); }); _server.on("/advanced", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildAdvancedConfigHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildAdvancedConfigHtml(); + _server.send(200, "text/html", _response); }); _server.on("/cred", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildCredHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildCredHtml(); + _server.send(200, "text/html", _response); }); _server.on("/mqttconfig", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildMqttConfigHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildMqttConfigHtml(); + _server.send(200, "text/html", _response); }); _server.on("/nukicfg", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildNukiConfigHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildNukiConfigHtml(); + _server.send(200, "text/html", _response); }); _server.on("/gpiocfg", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildGpioConfigHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildGpioConfigHtml(); + _server.send(200, "text/html", _response); }); _server.on("/wifi", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildConfigureWifiHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildConfigureWifiHtml(); + _server.send(200, "text/html", _response); }); _server.on("/unpairlock", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { @@ -223,9 +224,9 @@ void WebCfgServer::initialize() } if(_allowRestartToPortal) { - String response = ""; - buildConfirmHtml(response, "Restarting. Connect to ESP access point to reconfigure Wi-Fi.", 0); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml("Restarting. Connect to ESP access point to reconfigure Wi-Fi.", 0); + _server.send(200, "text/html", _response); waitAndProcess(true, 2000); _network->reconfigureDevice(); } @@ -239,9 +240,9 @@ void WebCfgServer::initialize() bool restart = processArgs(message); if(restart) { - String response = ""; - buildConfirmHtml(response, message); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml(message); + _server.send(200, "text/html", _response); Log->println(F("Restarting")); waitAndProcess(true, 1000); @@ -249,9 +250,9 @@ void WebCfgServer::initialize() } else { - String response = ""; - buildConfirmHtml(response, message, 3); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml(message, 3); + _server.send(200, "text/html", _response); waitAndProcess(false, 1000); } }); @@ -262,9 +263,9 @@ void WebCfgServer::initialize() } processGpioArgs(); - String response = ""; - buildConfirmHtml(response, ""); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml(""); + _server.send(200, "text/html", _response); Log->println(F("Restarting")); waitAndProcess(true, 1000); @@ -274,16 +275,16 @@ void WebCfgServer::initialize() if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildInfoHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildInfoHtml(); + _server.send(200, "text/html", _response); }); _server.on("/debugon", [&]() { _preferences->putBool(preference_publish_debug_info, true); - String response = ""; - buildConfirmHtml(response, "OK"); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml("OK"); + _server.send(200, "text/html", _response); Log->println(F("Restarting")); waitAndProcess(true, 1000); @@ -292,9 +293,9 @@ void WebCfgServer::initialize() _server.on("/debugoff", [&]() { _preferences->putBool(preference_publish_debug_info, false); - String response = ""; - buildConfirmHtml(response, "OK"); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml("OK"); + _server.send(200, "text/html", _response); Log->println(F("Restarting")); waitAndProcess(true, 1000); @@ -303,35 +304,45 @@ void WebCfgServer::initialize() _server.on("/webserial", [&]() { _server.sendHeader("Location", (String)"http://" + _network->localIP() + ":81/webserial"); _server.send(302, "text/plain", ""); - }); + }); #endif _server.on("/ota", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildOtaHtml(response, _server.arg("errored") != ""); - _server.send(200, "text/html", response); + _response = ""; + buildOtaHtml(_server.arg("errored") != ""); + _server.send(200, "text/html", _response); }); _server.on("/otadebug", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildOtaHtml(response, _server.arg("errored") != "", true); - _server.send(200, "text/html", response); + _response = ""; + buildOtaHtml(_server.arg("errored") != "", true); + _server.send(200, "text/html", _response); }); _server.on("/reboottoota", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); } - String response = ""; - buildConfirmHtml(response, "Rebooting to other partition", 2); - _server.send(200, "text/html", response); + _response = ""; + buildConfirmHtml("Rebooting to other partition", 2); + _server.send(200, "text/html", _response); waitAndProcess(true, 1000); esp_ota_set_boot_partition(esp_ota_get_next_update_partition(NULL)); restartEsp(RestartReason::OTAReboot); }); + _server.on("/reboot", [&]() { + if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { + return _server.requestAuthentication(); + } + _response = ""; + buildConfirmHtml("Rebooting", 2); + _server.send(200, "text/html", _response); + waitAndProcess(true, 1000); + restartEsp(RestartReason::RequestedViaWebServer); + }); _server.on("/autoupdate", [&]() { if (_hasCredentials && !_server.authenticate(_credUser, _credPassword)) { return _server.requestAuthentication(); @@ -349,9 +360,9 @@ void WebCfgServer::initialize() } if (_ota.updateStarted() && _ota.updateCompleted()) { - String response = ""; - buildOtaCompletedHtml(response); - _server.send(200, "text/html", response); + _response = ""; + buildOtaCompletedHtml(); + _server.send(200, "text/html", _response); delay(2000); restartEsp(RestartReason::OTACompleted); } else { @@ -393,26 +404,25 @@ void WebCfgServer::update() _server.handleClient(); } -void WebCfgServer::buildOtaHtml(String &response, bool errored, bool debug) +void WebCfgServer::buildOtaHtml(bool errored, bool debug) { - buildHtmlHeader(response); + buildHtmlHeader(); - if(errored) response.concat("
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.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(""); + _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.concat("
Initiating Over-the-air update. This will take about two minutes, please be patient.
You will be forwarded automatically when the update is complete.
"); - - response.concat("

Update Nuki Hub

"); - response.concat("Click on the button to reboot and automatically update Nuki Hub and the Nuki Hub updater to the latest versions from GitHub"); - response.concat("
"); + _response.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; @@ -425,18 +435,18 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored, bool debug) String build_type = "debug"; #endif - response.concat("

"); - response.concat("

"); - response.concat("

"); - response.concat("

"); + _response.concat("

"); + _response.concat("

"); + _response.concat("

"); + _response.concat("

"); - response.concat("Current version: "); - response.concat(NUKI_HUB_VERSION); - response.concat(" ("); - response.concat(NUKI_HUB_BUILD); - response.concat("), "); - response.concat(NUKI_HUB_DATE); - response.concat("
"); + _response.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; @@ -451,9 +461,9 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored, bool debug) https.useHTTP10(true); if (https.begin(*client, GITHUB_OTA_MANIFEST_URL)) { - int httpResponseCode = https.GET(); + int http_responseCode = https.GET(); - if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY) + if (http_responseCode == HTTP_CODE_OK || http_responseCode == HTTP_CODE_MOVED_PERMANENTLY) { JsonDocument doc; DeserializationError jsonError = deserializeJson(doc, https.getStream()); @@ -461,35 +471,35 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored, bool debug) if (!jsonError) { manifestSuccess = true; - response.concat("Latest release version: "); - response.concat(doc["release"]["fullversion"].as()); - response.concat(" ("); - response.concat(doc["release"]["build"].as()); - response.concat("), "); - response.concat(doc["release"]["time"].as()); - response.concat("
"); - response.concat("Latest beta version: "); + _response.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.concat(doc["beta"]["fullversion"].as()); - response.concat(" ("); - response.concat(doc["beta"]["build"].as()); - response.concat("), "); - response.concat(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.concat(doc["beta"]["fullversion"].as()); - response.concat(""); + _response.concat(doc["beta"]["fullversion"].as()); + _response.concat(""); } - response.concat("
"); - response.concat("Latest development version: "); - response.concat(doc["master"]["fullversion"].as()); - response.concat(" ("); - response.concat(doc["master"]["build"].as()); - response.concat("), "); - response.concat(doc["master"]["time"].as()); - response.concat("
"); + _response.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("
"); } } https.end(); @@ -500,87 +510,87 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored, bool debug) if(!manifestSuccess) { - response.concat("currentverlatestverdevverbetaver"); + _response.concat("currentverlatestverdevverbetaver"); } #endif - response.concat("
"); + _response.concat("
"); if(_partitionType == 1) { - response.concat("

Manually update Nuki Hub

"); - response.concat("

Reboot to Nuki Hub Updater

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



"); - response.concat("

Update Nuki Hub Updater

"); - response.concat("Select the latest Nuki Hub updater binary to update the Nuki Hub updater"); - response.concat("
Choose the nuki_hub_updater.bin file to upload:
"); + _response.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.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.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.concat("


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

GitHub


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

"); - response.concat("

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


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

GitHub


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

"); + _response.concat("

"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildOtaCompletedHtml(String &response) +void WebCfgServer::buildOtaCompletedHtml() { - buildHtmlHeader(response); + buildHtmlHeader(); - response.concat("
Over-the-air update completed.
You will be forwarded automatically.
"); - response.concat(""); - response.concat(""); + _response.concat("
Over-the-air update completed.
You will be forwarded automatically.
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildHtmlHeader(String &response, String additionalHeader) +void WebCfgServer::buildHtmlHeader(String additionalHeader) { - response.concat(""); - response.concat(""); - if(strcmp(additionalHeader.c_str(), "") != 0) response.concat(additionalHeader); - response.concat(""); - response.concat("Nuki Hub"); + _response.concat(""); + _response.concat(""); + if(strcmp(additionalHeader.c_str(), "") != 0) _response.concat(additionalHeader); + _response.concat(""); + _response.concat("Nuki Hub"); srand(esp_timer_get_time() / 1000); } @@ -682,7 +692,7 @@ void WebCfgServer::handleOtaUpload() } } -void WebCfgServer::buildConfirmHtml(String &response, const String &message, uint32_t redirectDelay, bool redirect) +void WebCfgServer::buildConfirmHtml(const String &message, uint32_t redirectDelay, bool redirect) { String header; @@ -696,9 +706,9 @@ void WebCfgServer::buildConfirmHtml(String &response, const String &message, uin String delay(redirectDelay * 1000); header = ""; } - buildHtmlHeader(response, header); - response.concat(message); - response.concat(""); + buildHtmlHeader(header); + _response.concat(message); + _response.concat(""); } void WebCfgServer::sendCss() @@ -1167,13 +1177,13 @@ bool WebCfgServer::processArgs(String& message) configChanged = true; } } -#if PRESENCE_DETECTION_ENABLED + #if PRESENCE_DETECTION_ENABLED else if(key == "PRDTMO") { _preferences->putInt(preference_presence_detection_timeout, value.toInt()); configChanged = true; } -#endif + #endif else if(key == "RSBC") { _preferences->putInt(preference_restart_ble_beacon_lost, value.toInt()); @@ -1763,8 +1773,8 @@ bool WebCfgServer::processArgs(String& message) nukiBlePref.putBytes("secretKeyK", secretKeyK, 32); nukiBlePref.putBytes("authorizationId", authorizationId, 4); nukiBlePref.putBytes("securityPinCode", pincode, 2); - nukiBlePref.end(); + configChanged = true; } if(manPairOpn) @@ -1777,6 +1787,7 @@ bool WebCfgServer::processArgs(String& message) nukiBlePref.putBytes("authorizationId", authorizationIdOpn, 4); nukiBlePref.putBytes("securityPinCode", pincode, 2); nukiBlePref.end(); + configChanged = true; } if(pass1 != "" && pass1 == pass2) @@ -1905,7 +1916,6 @@ bool WebCfgServer::processImport(String& message) for(int i=0; iremove("bleAddressLock"); } if(!doc["secretKeyKLock"].isNull()) { @@ -1915,7 +1925,6 @@ bool WebCfgServer::processImport(String& message) for(int i=0; iremove("secretKeyKLock"); } if(!doc["authorizationIdLock"].isNull()) { @@ -1925,7 +1934,6 @@ bool WebCfgServer::processImport(String& message) for(int i=0; iremove("authorizationIdLock"); } nukiBlePref.end(); if(!doc["securityPinCodeLock"].isNull()) @@ -1942,7 +1950,6 @@ bool WebCfgServer::processImport(String& message) for(int i=0; iremove("bleAddressOpener"); } if(!doc["secretKeyKOpener"].isNull()) { @@ -1952,7 +1959,6 @@ bool WebCfgServer::processImport(String& message) for(int i=0; iremove("secretKeyKOpener"); } if(!doc["authorizationIdOpener"].isNull()) { @@ -1962,7 +1968,6 @@ bool WebCfgServer::processImport(String& message) for(int i=0; iremove("authorizationIdOpener"); } nukiBlePref.end(); if(!doc["securityPinCodeOpener"].isNull()) @@ -2009,59 +2014,59 @@ void WebCfgServer::processGpioArgs() _gpio->savePinConfiguration(pinConfiguration); } -void WebCfgServer::buildImportExportHtml(String &response) +void WebCfgServer::buildImportExportHtml() { - buildHtmlHeader(response); + buildHtmlHeader(); - response.concat("

Import configuration

"); - response.concat("

"); - response.concat("


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

Export configuration


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

"); - response.concat("

"); - response.concat("
Initiating config update. Please be patient.
You will be forwarded automatically when the import is complete.
"); - response.concat(""); - response.concat(""); + _response.concat("

Import configuration

"); + _response.concat("

"); + _response.concat("


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

Export configuration


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

"); + _response.concat("

"); + _response.concat("
Initiating config update. Please be patient.
You will be forwarded automatically when the import is complete.
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildHtml(String& response) +void WebCfgServer::buildHtml() { String header = ""; - buildHtmlHeader(response, header); + buildHtmlHeader(header); - response.concat("

Info

\n"); - response.concat(""); + _response.concat("

Info

\n"); + _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"); } } } @@ -2069,230 +2074,221 @@ void WebCfgServer::buildHtml(String& response) { 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.concat("

"); - response.concat("
    "); - 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"); - - // buildNavigationButton(response, "Edit", "/mqttconfig", _brokerConfigured ? "" : "(!) Please configure MQTT broker"); - // buildNavigationButton(response, "Edit", "/cred", _pinsConfigured ? "" : "(!) Please configure PIN"); - + 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->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"); } - if(_allowRestartToPortal) { - buildNavigationMenuEntry(response, "Configure Wi-Fi", "/wifi"); + buildNavigationMenuEntry("Configure Wi-Fi", "/wifi"); } - - response.concat("
    "); + buildNavigationMenuEntry("Reboot Nuki Hub", "/reboot"); + _response.concat("
"); } -void WebCfgServer::buildCredHtml(String &response) +void WebCfgServer::buildCredHtml() { - buildHtmlHeader(response); + buildHtmlHeader(); - response.concat("
"); - response.concat("

Credentials

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

"); - response.concat("

Nuki Lock PIN

"); - response.concat(""); - printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, "", true); - response.concat("
"); - response.concat("
"); - response.concat("
"); + _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.concat("

"); - response.concat("

Nuki Opener PIN

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

"); + _response.concat("

Nuki Opener PIN

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

Unpair Nuki Lock

"); - response.concat("
"); - response.concat(""); + _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.concat("
"); - response.concat("
"); + printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); + _response.concat(""); + _response.concat("
"); } if(_nukiOpener != nullptr) { - response.concat("

Unpair Nuki Opener

"); - response.concat("
"); - response.concat(""); + _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.concat("
"); - response.concat("
"); + printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); + _response.concat(""); + _response.concat("
"); } - response.concat("

Factory reset Nuki Hub

"); - response.concat("

This will reset all settings to default and unpair Nuki Lock and/or Opener. Optionally will also reset WiFi settings and reopen WiFi manager portal.

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

Factory reset Nuki Hub

"); + _response.concat("

This will reset all settings to default and unpair Nuki Lock and/or Opener. Optionally will also reset WiFi settings and reopen WiFi manager portal.

"); + _response.concat(""); + _response.concat("
"); String message = "Type "; message.concat(_confirmCode); message.concat(" to confirm factory reset"); - printInputField(response, "CONFIRMTOKEN", message.c_str(), "", 10, ""); - printCheckBox(response, "WIFI", "Also reset WiFi settings", false, ""); - response.concat("
"); - response.concat("
"); - response.concat(""); + printInputField("CONFIRMTOKEN", message.c_str(), "", 10, ""); + printCheckBox("WIFI", "Also reset WiFi settings", false, ""); + _response.concat(""); + _response.concat("
"); + _response.concat(""); } -void WebCfgServer::buildMqttConfigHtml(String &response) +void WebCfgServer::buildMqttConfigHtml() { - buildHtmlHeader(response); - response.concat("
"); - response.concat("

Basic MQTT and Network Configuration

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

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

Advanced MQTT and Network Configuration

"); - response.concat(""); - 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(_nukiOpener != nullptr) 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()); - 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, ""); - 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.concat("
"); - response.concat("* If no encryption is configured for the MQTT broker, leave empty. Only supported for Wi-Fi connections.

"); + _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(_nukiOpener != nullptr) 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()); + 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, ""); + 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("WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_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. Only supported for Wi-Fi connections.

"); - response.concat("

IP Address assignment

"); - response.concat(""); - 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.concat("
"); + _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(""); + _response.concat("
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildAdvancedConfigHtml(String &response) +void WebCfgServer::buildAdvancedConfigHtml() { - buildHtmlHeader(response); - response.concat("
"); - response.concat("

Advanced Configuration

"); - response.concat("

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

"); - response.concat(""); - response.concat(""); - printCheckBox(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.concat(""); - printInputField(response, "TSKNTWK", "Task size Network (min 12288, max 32768)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6, ""); - response.concat(""); - 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, "inputmaxauthlog"); - printInputField(response, "KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "inputmaxkeypad"); - printInputField(response, "TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "inputmaxtimecontrol"); - printCheckBox(response, "SHOWSECRETS", "Show Pairing secrets on Info page (for 120s after next boot)", _preferences->getBool(preference_show_secrets), ""); + 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.concat(_preferences->getBool(preference_enable_bootloop_reset, false) ? "Enabled" : "Disabled"); - response.concat("
Advised minimum char buffer size based on current settings
Advised minimum network task size based on current settings
"); + _response.concat(""); + 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, "inputmaxauthlog"); + printInputField("KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "inputmaxkeypad"); + printInputField("TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "inputmaxtimecontrol"); + printCheckBox("SHOWSECRETS", "Show Pairing secrets on Info page (for 120s after next boot)", _preferences->getBool(preference_show_secrets), ""); if(_nuki != nullptr) { - 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(_nukiOpener != nullptr) { - 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.concat("
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.concat("
"); - response.concat("
"); - response.concat(""); + _response.concat("
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildStatusHtml(String &response) +void WebCfgServer::buildStatusHtml() { JsonDocument json; char _resbuf[2048]; @@ -2357,7 +2353,7 @@ void WebCfgServer::buildStatusHtml(String &response) if(mqttDone && lockDone && openerDone && latestDone) json["stop"] = 1; serializeJson(json, _resbuf, sizeof(_resbuf)); - response = _resbuf; + _response = _resbuf; } String WebCfgServer::pinStateToString(uint8_t value) { @@ -2374,30 +2370,30 @@ String WebCfgServer::pinStateToString(uint8_t value) { } } -void WebCfgServer::buildAccLvlHtml(String &response) +void WebCfgServer::buildAccLvlHtml() { - buildHtmlHeader(response); + buildHtmlHeader(); uint32_t aclPrefs[17]; _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); - response.concat("
"); - response.concat(""); - response.concat("

Nuki General Access Control

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

Nuki General Access Control

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

"); + 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("PUBAUTH", "Publish authorization log (may reduce battery life)", _preferences->getBool(preference_publish_authdata), ""); + _response.concat("
"); if(_nuki != nullptr) { uint32_t basicLockConfigAclPrefs[16]; @@ -2405,71 +2401,71 @@ void WebCfgServer::buildAccLvlHtml(String &response) uint32_t advancedLockConfigAclPrefs[22]; _preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs)); - response.concat("

Nuki Lock Access Control

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

Nuki Lock Access Control

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

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

Nuki Lock Config Control (Requires PIN to be set)

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

Nuki Lock Config Control (Requires PIN to be set)

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

"); + 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("
"); } if(_nukiOpener != nullptr) { @@ -2478,127 +2474,127 @@ void WebCfgServer::buildAccLvlHtml(String &response) uint32_t advancedOpenerConfigAclPrefs[20]; _preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs)); - response.concat("

Nuki Opener Access Control

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

Nuki Opener Access Control

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

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

Nuki Opener Config Control (Requires PIN to be set)

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

Nuki Opener Config Control (Requires PIN to be set)

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

"); + 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("
"); } - response.concat("
"); - response.concat("
"); - response.concat(""); + _response.concat("
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildNukiConfigHtml(String &response) +void WebCfgServer::buildNukiConfigHtml() { - buildHtmlHeader(response); + buildHtmlHeader(); - response.concat("
"); - response.concat("

Basic Nuki Configuration

"); - response.concat(""); - printCheckBox(response, "LOCKENA", "Nuki Smartlock enabled", _preferences->getBool(preference_lock_enabled), ""); + _response.concat(""); + _response.concat("

Basic Nuki Configuration

"); + _response.concat("
"); + printCheckBox("LOCKENA", "Nuki Smartlock enabled", _preferences->getBool(preference_lock_enabled), ""); if(_preferences->getBool(preference_lock_enabled)) { - printInputField(response, "MQTTPATH", "MQTT Nuki Smartlock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180, ""); + printInputField("MQTTPATH", "MQTT Nuki Smartlock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180, ""); } - printCheckBox(response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled), ""); + printCheckBox("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, ""); + printInputField("MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180, ""); } - response.concat("

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

Advanced Nuki Configuration

"); - 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(_nuki != nullptr) printCheckBox(response, "REGAPP", "Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), ""); - if(_nukiOpener != nullptr) printCheckBox(response, "REGAPPOPN", "Opener: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_opener_as_app), ""); + 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(_nuki != nullptr) printCheckBox("REGAPP", "Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), ""); + if(_nukiOpener != nullptr) printCheckBox("REGAPPOPN", "Opener: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_opener_as_app), ""); #if PRESENCE_DETECTION_ENABLED - printInputField(response, "PRDTMO", "Presence detection timeout (seconds; -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10, ""); + printInputField("PRDTMO", "Presence detection timeout (seconds; -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10, ""); #endif - 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("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.concat("
"); - response.concat("
"); - response.concat("
"); - response.concat(""); + _response.concat(""); + _response.concat("
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildGpioConfigHtml(String &response) +void WebCfgServer::buildGpioConfigHtml() { - buildHtmlHeader(response); + buildHtmlHeader(); - response.concat("
"); - response.concat("

GPIO Configuration

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

GPIO Configuration

"); + _response.concat("
"); const auto& availablePins = _gpio->availablePins(); for(const auto& pin : availablePins) @@ -2606,273 +2602,379 @@ void WebCfgServer::buildGpioConfigHtml(String &response) String pinStr = String(pin); String pinDesc = "Gpio " + pinStr; - printDropDown(response, pinStr.c_str(), pinDesc.c_str(), getPreselectionForGpio(pin), getGpioOptions()); + printDropDown(pinStr.c_str(), pinDesc.c_str(), getPreselectionForGpio(pin), getGpioOptions()); } - response.concat("
"); - response.concat("
"); - response.concat("
"); - response.concat(""); + _response.concat(""); + _response.concat("
"); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildConfigureWifiHtml(String &response) +void WebCfgServer::buildConfigureWifiHtml() { - buildHtmlHeader(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(response, "Confirm", "/wifimanager"); + _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(""); + _response.concat(""); } -void WebCfgServer::buildInfoHtml(String &response) +void WebCfgServer::buildInfoHtml() { - DebugPreferences debugPreferences; - - buildHtmlHeader(response); - response.concat("

System Information

");
-
-    response.concat("Nuki Hub version: ");
-    response.concat(NUKI_HUB_VERSION);
-    response.concat("\n");
-    response.concat("Nuki Hub build: ");
-    response.concat(NUKI_HUB_BUILD);
-    response.concat("\n");
-    response.concat("Nuki Hub build type: ");
-    #ifndef DEBUG_NUKIHUB
-    response.concat("Release\n");
-    #else
-    response.concat("Debug\n");
-    #endif
-
-    response.concat(debugPreferences.preferencesToString(_preferences));
-
-    response.concat("MQTT connected: ");
-    response.concat(_network->mqttConnectionState() > 0 ? "Yes\n" : "No\n");
-
     uint32_t aclPrefs[17];
     _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs));
+    buildHtmlHeader();
+    _response.concat("

System Information

");
+    _response.concat("------------ NUKI HUB ------------");
+    _response.concat("\nVersion: ");
+    _response.concat(NUKI_HUB_VERSION);
+    _response.concat("\nBuild: ");
+    _response.concat(NUKI_HUB_BUILD);
+    #ifndef DEBUG_NUKIHUB
+    _response.concat("\nBuild type: Release");
+    #else
+    _response.concat("\nBuild type: Debug");
+    #endif
+    _response.concat("\nBuild date: ");
+    _response.concat(NUKI_HUB_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.concat("\nIP Address: ");
+        _response.concat(_network->localIP());
+        if(_network->networkDeviceName() == "Built-in Wi-Fi")
+        {
+            _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());
+        }
+        else
+        {
+            /*
+            preference_has_mac_saved
+            preference_has_mac_byte_0
+            preference_has_mac_byte_1
+            preference_has_mac_byte_2
+            */
+        }
+    }
+    _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.concat("\nDHCP enabled: No");
+        _response.concat("\nStatic IP address: ");
+        _response.concat(_preferences->getString(preference_ip_address, ""));
+        _response.concat("\nStatic IP subnet: ");
+        _response.concat(_preferences->getString(preference_ip_subnet, ""));
+        _response.concat("\nStatic IP gateway: ");
+        _response.concat(_preferences->getString(preference_ip_gateway, ""));
+        _response.concat("\nStatic IP DNS server: ");
+        _response.concat(_preferences->getString(preference_ip_dns_server, ""));
+    }
 
+    _response.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.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.concat("Disabled");
+        else _response.concat(_preferences->getInt(preference_rssi_publish_interval, 60));
+    }
+    _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(_nuki != nullptr)
     {
+        _response.concat("\nMQTT lock base topic: ");
+        _response.concat(_preferences->getString(preference_mqtt_lock_path, ""));
+    }
+    if(_nukiOpener != nullptr)
+    {
+        _response.concat("\nMQTT opener base topic: ");
+        _response.concat(_preferences->getString(preference_mqtt_lock_path, ""));
+    }
+    _response.concat("\nMQTT SSL CA: ");
+    _response.concat(_preferences->getString(preference_mqtt_ca, "").length() > 0 ? "***" : "Not set");
+    _response.concat("\nMQTT SSL CRT: ");
+    _response.concat(_preferences->getString(preference_mqtt_crt, "").length() > 0 ? "***" : "Not set");
+    _response.concat("\nMQTT SSL Key: ");
+    _response.concat(_preferences->getString(preference_mqtt_key, "").length() > 0 ? "***" : "Not set");
+    _response.concat("\n\n------------ BLUETOOTH ------------");
+    _response.concat("\nBluetooth TX power (dB): ");
+    _response.concat(_preferences->getInt(preference_ble_tx_power, 9));
+    _response.concat("\nBluetooth command nr of retries: ");
+    _response.concat(_preferences->getInt(preference_command_nr_of_retries, 3));
+    _response.concat("\nBluetooth command retry delay (ms): ");
+    _response.concat(_preferences->getInt(preference_command_retry_delay, 100));
+    _response.concat("\nSeconds until reboot when no BLE beacons recieved: ");
+    _response.concat(_preferences->getInt(preference_restart_ble_beacon_lost, 60));
+    _response.concat("\n\n------------ QUERY / PUBLISH SETTINGS ------------");
+    _response.concat("\nLock/Opener state query interval (s): ");
+    _response.concat(_preferences->getInt(preference_query_interval_lockstate, 1800));
+    _response.concat("\nPublish Nuki device authorization log: ");
+    _response.concat(_preferences->getBool(preference_publish_authdata, false) ? "Yes" : "No");
+    _response.concat("\nMax authorization log entries to retrieve: ");
+    _response.concat(_preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG));
+    _response.concat("\nBattery state query interval (s): ");
+    _response.concat(_preferences->getInt(preference_query_interval_battery, 1800));
+    _response.concat("\nMost non-JSON MQTT topics disabled: ");
+    _response.concat(_preferences->getBool(preference_disable_non_json, false) ? "Yes" : "No");
+    _response.concat("\nPublish Nuki device config: ");
+    _response.concat(_preferences->getBool(preference_conf_info_enabled, false) ? "Yes" : "No");
+    _response.concat("\nConfig query interval (s): ");
+    _response.concat(_preferences->getInt(preference_query_interval_configuration, 3600));
+    _response.concat("\nPublish Keypad info: ");
+    _response.concat(_preferences->getBool(preference_keypad_info_enabled, false) ? "Yes" : "No");
+    _response.concat("\nKeypad query interval (s): ");
+    _response.concat(_preferences->getInt(preference_query_interval_keypad, 1800));
+    _response.concat("\nEnable Keypad control: ");
+    _response.concat(_preferences->getBool(preference_keypad_control_enabled, false) ? "Yes" : "No");
+    _response.concat("\nPublish Keypad topic per entry: ");
+    _response.concat(_preferences->getBool(preference_keypad_topic_per_entry, false) ? "Yes" : "No");
+    _response.concat("\nPublish Keypad codes: ");
+    _response.concat(_preferences->getBool(preference_keypad_publish_code, false) ? "Yes" : "No");
+    _response.concat("\nMax keypad entries to retrieve: ");
+    _response.concat(_preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
+    _response.concat("\nPublish timecontrol info: ");
+    _response.concat(_preferences->getBool(preference_timecontrol_info_enabled, false) ? "Yes" : "No");
+    _response.concat("\nKeypad query interval (s): ");
+    _response.concat(_preferences->getInt(preference_query_interval_keypad, 1800));
+    _response.concat("\nEnable timecontrol control: ");
+    _response.concat(_preferences->getBool(preference_timecontrol_control_enabled, false) ? "Yes" : "No");
+    _response.concat("\nPublish timecontrol topic per entry: ");
+    _response.concat(_preferences->getBool(preference_timecontrol_topic_per_entry, false) ? "Yes" : "No");
+    _response.concat("\nMax timecontrol entries to retrieve: ");
+    _response.concat(_preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL));
+    _response.concat("\n\n------------ HOME ASSISTANT ------------");
+    _response.concat("\nHome Assistant auto discovery enabled: ");
+    if(_preferences->getString(preference_mqtt_hass_discovery, "").length() > 0)
+    {
+        _response.concat("Yes");
+        _response.concat("\nHome Assistant auto discovery topic: ");
+        _response.concat(_preferences->getString(preference_mqtt_hass_discovery, "") + "/");
+        _response.concat("\nNuki Hub configuration URL for HA: ");
+        _response.concat(_preferences->getString(preference_mqtt_hass_cu_url, "").length() > 0 ? _preferences->getString(preference_mqtt_hass_cu_url, "") : "http://" + _network->localIP());
+    }
+    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.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.concat("\nKeypad highest entries count: ");
+            _response.concat(_preferences->getInt(preference_lock_max_keypad_code_count, 0));
+        }
+        _response.concat("\nTimecontrol highest entries count: ");
+        _response.concat(_preferences->getInt(preference_lock_max_timecontrol_entry_count, 0));
+        _response.concat("\nRegister as: ");
+        _response.concat(_preferences->getBool(preference_register_as_app, false) ? "App" : "Bridge");
+        _response.concat("\n\n------------ HYBRID MODE ------------");
+        if(!_preferences->getBool(preference_official_hybrid, false)) _response.concat("\nHybrid mode enabled: No");
+        else
+        {
+            _response.concat("\nHybrid mode enabled: Yes");
+            _response.concat("\nHybrid mode connected: ");
+            _response.concat(_nuki->offConnected() ? "Yes": "No");
+            _response.concat("\nSending actions through official MQTT enabled: ");
+            _response.concat(_preferences->getBool(preference_official_hybrid_actions, false) ? "Yes" : "No");
+            if(_preferences->getBool(preference_official_hybrid_actions, false))
+            {
+                _response.concat("\nRetry actions through BLE enabled: ");
+                _response.concat(_preferences->getBool(preference_official_hybrid_retry, false) ? "Yes" : "No");
+            }
+            _response.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.concat("\n\n------------ NUKI LOCK ACL ------------");
+        _response.concat("\nLock: ");
+        _response.concat((int)aclPrefs[0] ? "Allowed" : "Disallowed");
+        _response.concat("\nUnlock: ");
+        _response.concat((int)aclPrefs[1] ? "Allowed" : "Disallowed");
+        _response.concat("\nUnlatch: ");
+        _response.concat((int)aclPrefs[2] ? "Allowed" : "Disallowed");
+        _response.concat("\nLock N Go: ");
+        _response.concat((int)aclPrefs[3] ? "Allowed" : "Disallowed");
+        _response.concat("\nLock N Go Unlatch: ");
+        _response.concat((int)aclPrefs[4] ? "Allowed" : "Disallowed");
+        _response.concat("\nFull Lock: ");
+        _response.concat((int)aclPrefs[5] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 1: ");
+        _response.concat((int)aclPrefs[6] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 2: ");
+        _response.concat((int)aclPrefs[7] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 3: ");
+        _response.concat((int)aclPrefs[8] ? "Allowed" : "Disallowed");
+        _response.concat("\n\n------------ NUKI LOCK CONFIG ACL ------------");
+        _response.concat("\nName: ");
+        _response.concat((int)basicLockConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        _response.concat("\nLatitude: ");
+        _response.concat((int)basicLockConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        _response.concat("\nLongitude: ");
+        _response.concat((int)basicLockConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        _response.concat("\nAuto Unlatch: ");
+        _response.concat((int)basicLockConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        _response.concat("\nPairing enabled: ");
+        _response.concat((int)basicLockConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        _response.concat("\nButton enabled: ");
+        _response.concat((int)basicLockConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        _response.concat("\nLED flash enabled: ");
+        _response.concat((int)basicLockConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        _response.concat("\nLED brightness: ");
+        _response.concat((int)basicLockConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        _response.concat("\nTimezone offset: ");
+        _response.concat((int)basicLockConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        _response.concat("\nDST mode: ");
+        _response.concat((int)basicLockConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 1: ");
+        _response.concat((int)basicLockConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 2: ");
+        _response.concat((int)basicLockConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 3: ");
+        _response.concat((int)basicLockConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        _response.concat("\nSingle Lock: ");
+        _response.concat((int)basicLockConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        _response.concat("\nAdvertising Mode: ");
+        _response.concat((int)basicLockConfigAclPrefs[14] ? "Allowed" : "Disallowed");
+        _response.concat("\nTimezone ID: ");
+        _response.concat((int)basicLockConfigAclPrefs[15] ? "Allowed" : "Disallowed");
+        _response.concat("\nUnlocked Position Offset Degrees: ");
+        _response.concat((int)advancedLockConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        _response.concat("\nLocked Position Offset Degrees: ");
+        _response.concat((int)advancedLockConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        _response.concat("\nSingle Locked Position Offset Degrees: ");
+        _response.concat((int)advancedLockConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        _response.concat("\nUnlocked To Locked Transition Offset Degrees: ");
+        _response.concat((int)advancedLockConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        _response.concat("\nLock n Go timeout: ");
+        _response.concat((int)advancedLockConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        _response.concat("\nSingle button press action: ");
+        _response.concat((int)advancedLockConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        _response.concat("\nDouble button press action: ");
+        _response.concat((int)advancedLockConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        _response.concat("\nDetached cylinder: ");
+        _response.concat((int)advancedLockConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        _response.concat("\nBattery type: ");
+        _response.concat((int)advancedLockConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        _response.concat("\nAutomatic battery type detection: ");
+        _response.concat((int)advancedLockConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        _response.concat("\nUnlatch duration: ");
+        _response.concat((int)advancedLockConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        _response.concat("\nAuto lock timeout: ");
+        _response.concat((int)advancedLockConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        _response.concat("\nAuto unlock disabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        _response.concat("\nNightmode enabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        _response.concat("\nNightmode start time: ");
+        _response.concat((int)advancedLockConfigAclPrefs[14] ? "Allowed" : "Disallowed");
+        _response.concat("\nNightmode end time: ");
+        _response.concat((int)advancedLockConfigAclPrefs[15] ? "Allowed" : "Disallowed");
+        _response.concat("\nNightmode auto lock enabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[16] ? "Allowed" : "Disallowed");
+        _response.concat("\nNightmode auto unlock disabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[17] ? "Allowed" : "Disallowed");
+        _response.concat("\nNightmode immediate lock on start: ");
+        _response.concat((int)advancedLockConfigAclPrefs[18] ? "Allowed" : "Disallowed");
+        _response.concat("\nAuto lock enabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[19] ? "Allowed" : "Disallowed");
+        _response.concat("\nImmediate auto lock enabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[20] ? "Allowed" : "Disallowed");
+        _response.concat("\nAuto update enabled: ");
+        _response.concat((int)advancedLockConfigAclPrefs[21] ? "Allowed" : "Disallowed");
 
-        response.concat("Lock firmware version: ");
-        response.concat(_nuki->firmwareVersion().c_str());
-        response.concat("\nLock hardware version: ");
-        response.concat(_nuki->hardwareVersion().c_str());
-        response.concat("\nLock paired: ");
-        response.concat(_nuki->isPaired() ? "Yes\n" : "No\n");
-        response.concat("Lock valid PIN set: ");
-        response.concat(_nuki->isPaired() ? _nuki->isPinValid() ? "Yes\n" : "No\n" : "-\n");
-        response.concat("Lock has door sensor: ");
-        response.concat(_nuki->hasDoorSensor() ? "Yes\n" : "No\n");
-        response.concat("Lock has keypad: ");
-        response.concat(_nuki->hasKeypad() ? "Yes\n" : "No\n");
-        response.concat("Lock ACL (Lock): ");
-        response.concat((int)aclPrefs[0] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Unlock): ");
-        response.concat((int)aclPrefs[1] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Unlatch): ");
-        response.concat((int)aclPrefs[2] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Lock N Go): ");
-        response.concat((int)aclPrefs[3] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Lock N Go Unlatch): ");
-        response.concat((int)aclPrefs[4] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Full Lock): ");
-        response.concat((int)aclPrefs[5] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Fob Action 1): ");
-        response.concat((int)aclPrefs[6] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Fob Action 2): ");
-        response.concat((int)aclPrefs[7] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock ACL (Fob Action 3): ");
-        response.concat((int)aclPrefs[8] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Name): ");
-        response.concat((int)basicLockConfigAclPrefs[0] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Latitude): ");
-        response.concat((int)basicLockConfigAclPrefs[1] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Longitude): ");
-        response.concat((int)basicLockConfigAclPrefs[2] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Auto Unlatch): ");
-        response.concat((int)basicLockConfigAclPrefs[3] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Pairing enabled): ");
-        response.concat((int)basicLockConfigAclPrefs[4] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Button enabled): ");
-        response.concat((int)basicLockConfigAclPrefs[5] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (LED flash enabled): ");
-        response.concat((int)basicLockConfigAclPrefs[6] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (LED brightness): ");
-        response.concat((int)basicLockConfigAclPrefs[7] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Timezone offset): ");
-        response.concat((int)basicLockConfigAclPrefs[8] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (DST mode): ");
-        response.concat((int)basicLockConfigAclPrefs[9] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Fob Action 1): ");
-        response.concat((int)basicLockConfigAclPrefs[10] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Fob Action 2): ");
-        response.concat((int)basicLockConfigAclPrefs[11] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Fob Action 3): ");
-        response.concat((int)basicLockConfigAclPrefs[12] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Single Lock): ");
-        response.concat((int)basicLockConfigAclPrefs[13] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Advertising Mode): ");
-        response.concat((int)basicLockConfigAclPrefs[14] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Timezone ID): ");
-        response.concat((int)basicLockConfigAclPrefs[15] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Unlocked Position Offset Degrees): ");
-        response.concat((int)advancedLockConfigAclPrefs[0] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Locked Position Offset Degrees): ");
-        response.concat((int)advancedLockConfigAclPrefs[1] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Single Locked Position Offset Degrees): ");
-        response.concat((int)advancedLockConfigAclPrefs[2] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Unlocked To Locked Transition Offset Degrees): ");
-        response.concat((int)advancedLockConfigAclPrefs[3] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Lock n Go timeout): ");
-        response.concat((int)advancedLockConfigAclPrefs[4] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Single button press action): ");
-        response.concat((int)advancedLockConfigAclPrefs[5] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Double button press action): ");
-        response.concat((int)advancedLockConfigAclPrefs[6] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Detached cylinder): ");
-        response.concat((int)advancedLockConfigAclPrefs[7] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Battery type): ");
-        response.concat((int)advancedLockConfigAclPrefs[8] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Automatic battery type detection): ");
-        response.concat((int)advancedLockConfigAclPrefs[9] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Unlatch duration): ");
-        response.concat((int)advancedLockConfigAclPrefs[10] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Auto lock timeout): ");
-        response.concat((int)advancedLockConfigAclPrefs[11] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Auto unlock disabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[12] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Nightmode enabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[13] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Nightmode start time): ");
-        response.concat((int)advancedLockConfigAclPrefs[14] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Nightmode end time): ");
-        response.concat((int)advancedLockConfigAclPrefs[15] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Nightmode auto lock enabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[16] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Nightmode auto unlock disabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[17] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Nightmode immediate lock on start): ");
-        response.concat((int)advancedLockConfigAclPrefs[18] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Auto lock enabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[19] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Immediate auto lock enabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[20] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Lock config ACL (Auto update enabled): ");
-        response.concat((int)advancedLockConfigAclPrefs[21] ? "Allowed\n" : "Disallowed\n");
-    }
-
-    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("Opener firmware version: ");
-        response.concat(_nukiOpener->firmwareVersion().c_str());
-        response.concat("\nOpener hardware version: ");
-        response.concat(_nukiOpener->hardwareVersion().c_str());        response.concat("\nOpener paired: ");
-        response.concat(_nukiOpener->isPaired() ? "Yes\n" : "No\n");
-        response.concat("Opener valid PIN set: ");
-        response.concat(_nukiOpener->isPaired() ? _nukiOpener->isPinValid() ? "Yes\n" : "No\n" : "-\n");
-        response.concat("Opener has keypad: ");
-        response.concat(_nukiOpener->hasKeypad() ? "Yes\n" : "No\n");
-        response.concat("Opener ACL (Activate Ring-to-Open): ");
-        response.concat((int)aclPrefs[9] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Deactivate Ring-to-Open): ");
-        response.concat((int)aclPrefs[10] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Electric Strike Actuation): ");
-        response.concat((int)aclPrefs[11] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Activate Continuous Mode): ");
-        response.concat((int)aclPrefs[12] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Deactivate Continuous Mode): ");
-        response.concat((int)aclPrefs[13] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Fob Action 1): ");
-        response.concat((int)aclPrefs[14] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Fob Action 2): ");
-        response.concat((int)aclPrefs[15] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener ACL (Fob Action 3): ");
-        response.concat((int)aclPrefs[16] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Name): ");
-        response.concat((int)basicOpenerConfigAclPrefs[0] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Latitude): ");
-        response.concat((int)basicOpenerConfigAclPrefs[1] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Longitude): ");
-        response.concat((int)basicOpenerConfigAclPrefs[2] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Pairing enabled): ");
-        response.concat((int)basicOpenerConfigAclPrefs[3] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Button enabled): ");
-        response.concat((int)basicOpenerConfigAclPrefs[4] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (LED flash enabled): ");
-        response.concat((int)basicOpenerConfigAclPrefs[5] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Timezone offset): ");
-        response.concat((int)basicOpenerConfigAclPrefs[6] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (DST mode): ");
-        response.concat((int)basicOpenerConfigAclPrefs[7] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Fob Action 1): ");
-        response.concat((int)basicOpenerConfigAclPrefs[8] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Fob Action 2): ");
-        response.concat((int)basicOpenerConfigAclPrefs[9] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Fob Action 3): ");
-        response.concat((int)basicOpenerConfigAclPrefs[10] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Operating Mode): ");
-        response.concat((int)basicOpenerConfigAclPrefs[11] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Advertising Mode): ");
-        response.concat((int)basicOpenerConfigAclPrefs[12] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Timezone ID): ");
-        response.concat((int)basicOpenerConfigAclPrefs[13] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Intercom ID): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[0] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (BUS mode Switch): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[1] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Short Circuit Duration): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[2] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Eletric Strike Delay): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[3] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Random Electric Strike Delay): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[4] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Electric Strike Duration): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[5] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Disable RTO after ring): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[6] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (RTO timeout): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[7] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Doorbell suppression): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[8] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Doorbell suppression duration): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[9] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Sound Ring): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[10] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Sound Open): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[11] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Sound RTO): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[12] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Sound CM): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[13] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Sound confirmation): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[14] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Sound level): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[15] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Single button press action): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[16] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Double button press action): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[17] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Battery type): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[18] ? "Allowed\n" : "Disallowed\n");
-        response.concat("Opener config ACL (Automatic battery type detection): ");
-        response.concat((int)advancedOpenerConfigAclPrefs[19] ? "Allowed\n" : "Disallowed\n");
-    }
-
-    if(_preferences->getBool(preference_show_secrets))
-    {
-        if(_nuki != nullptr)
+        if(_preferences->getBool(preference_show_secrets))
         {
             char tmp[16];
             unsigned char currentBleAddress[6];
@@ -2884,24 +2986,149 @@ void WebCfgServer::buildInfoHtml(String &response)
             nukiBlePref.getBytes("secretKeyK", secretKeyK, 32);
             nukiBlePref.getBytes("authorizationId", authorizationId, 4);
             nukiBlePref.end();
-            response.concat("Lock bleAddress: ");
-            for (int i = 0; i < 6; i++) {
-              sprintf(tmp, "%02x", currentBleAddress[i]);
-              response.concat(tmp);
+            _response.concat("\n\n------------ NUKI LOCK PAIRING ------------");
+            _response.concat("\nBLE Address: ");
+            for (int i = 0; i < 6; i++)
+            {
+                sprintf(tmp, "%02x", currentBleAddress[i]);
+                _response.concat(tmp);
             }
-            response.concat("\nLock secretKeyK: ");
-            for (int i = 0; i < 32; i++) {
-              sprintf(tmp, "%02x", secretKeyK[i]);
-              response.concat(tmp);
+            _response.concat("\nSecretKeyK: ");
+            for (int i = 0; i < 32; i++)
+            {
+                sprintf(tmp, "%02x", secretKeyK[i]);
+                _response.concat(tmp);
             }
-            response.concat("\nLock authorizationId: ");
-            for (int i = 0; i < 4; i++) {
-              sprintf(tmp, "%02x", authorizationId[i]);
-              response.concat(tmp);
+            _response.concat("\nAuthorizationId: ");
+            for (int i = 0; i < 4; i++)
+            {
+                sprintf(tmp, "%02x", authorizationId[i]);
+                _response.concat(tmp);
             }
-            response.concat("\n");
         }
-        if(_nukiOpener != nullptr)
+    }
+
+    _response.concat("\n\n------------ NUKI OPENER ------------");
+    if(_nukiOpener == nullptr || !_preferences->getBool(preference_opener_enabled, true)) _response.concat("\nOpener enabled: No");
+    else
+    {
+        _response.concat("\nOpener enabled: Yes");
+        _response.concat("\nPaired: ");
+        _response.concat(_nukiOpener->isPaired() ? "Yes" : "No");
+        _response.concat("\nNuki Hub device ID: ");
+        _response.concat(_preferences->getUInt(preference_device_id_opener, 0));
+        _response.concat("\nNuki device ID: ");
+        _response.concat(_preferences->getUInt(preference_nuki_id_opener, 0) > 0 ? "***" : "Not set");
+        _response.concat("\nFirmware version: ");
+        _response.concat(_nukiOpener->firmwareVersion().c_str());
+        _response.concat("\nHardware version: ");
+        _response.concat(_nukiOpener->hardwareVersion().c_str());
+        _response.concat("\nOpener valid PIN set: ");
+        _response.concat(_nukiOpener->isPaired() ? _nukiOpener->isPinValid() ? "Yes" : "No" : "-");
+        _response.concat("\nOpener has keypad: ");
+        _response.concat(_nukiOpener->hasKeypad() ? "Yes" : "No");
+        if(_nuki->hasKeypad())
+        {
+            _response.concat("\nKeypad highest entries count: ");
+            _response.concat(_preferences->getInt(preference_opener_max_keypad_code_count, 0));
+        }
+        _response.concat("\nTimecontrol highest entries count: ");
+        _response.concat(_preferences->getInt(preference_opener_max_timecontrol_entry_count, 0));
+        _response.concat("\nRegister as: ");
+        _response.concat(_preferences->getBool(preference_register_opener_as_app, false) ? "App" : "Bridge");
+        _response.concat("\nNuki Opener Lock/Unlock action set to Continuous mode in Home Assistant: ");
+        _response.concat(_preferences->getBool(preference_opener_continuous_mode, false) ? "Yes" : "No");
+        uint32_t basicOpenerConfigAclPrefs[14];
+        _preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs));
+        uint32_t advancedOpenerConfigAclPrefs[20];
+        _preferences->getBytes(preference_conf_opener_advanced_acl, &advancedOpenerConfigAclPrefs, sizeof(advancedOpenerConfigAclPrefs));
+        _response.concat("\n\n------------ NUKI OPENER ACL ------------");
+        _response.concat("\nActivate Ring-to-Open: ");
+        _response.concat((int)aclPrefs[9] ? "Allowed" : "Disallowed");
+        _response.concat("\nDeactivate Ring-to-Open: ");
+        _response.concat((int)aclPrefs[10] ? "Allowed" : "Disallowed");
+        _response.concat("\nElectric Strike Actuation: ");
+        _response.concat((int)aclPrefs[11] ? "Allowed" : "Disallowed");
+        _response.concat("\nActivate Continuous Mode: ");
+        _response.concat((int)aclPrefs[12] ? "Allowed" : "Disallowed");
+        _response.concat("\nDeactivate Continuous Mode: ");
+        _response.concat((int)aclPrefs[13] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 1: ");
+        _response.concat((int)aclPrefs[14] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 2: ");
+        _response.concat((int)aclPrefs[15] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 3: ");
+        _response.concat((int)aclPrefs[16] ? "Allowed" : "Disallowed");
+        _response.concat("\n\n------------ NUKI OPENER CONFIG ACL ------------");
+        _response.concat("\nName: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        _response.concat("\nLatitude: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        _response.concat("\nLongitude: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        _response.concat("\nPairing enabled: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        _response.concat("\nButton enabled: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        _response.concat("\nLED flash enabled: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        _response.concat("\nTimezone offset: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        _response.concat("\nDST mode: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 1: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 2: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        _response.concat("\nFob Action 3: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        _response.concat("\nOperating Mode: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        _response.concat("\nAdvertising Mode: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        _response.concat("\nTimezone ID: ");
+        _response.concat((int)basicOpenerConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        _response.concat("\nIntercom ID: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[0] ? "Allowed" : "Disallowed");
+        _response.concat("\nBUS mode Switch: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[1] ? "Allowed" : "Disallowed");
+        _response.concat("\nShort Circuit Duration: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[2] ? "Allowed" : "Disallowed");
+        _response.concat("\nEletric Strike Delay: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[3] ? "Allowed" : "Disallowed");
+        _response.concat("\nRandom Electric Strike Delay: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[4] ? "Allowed" : "Disallowed");
+        _response.concat("\nElectric Strike Duration: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[5] ? "Allowed" : "Disallowed");
+        _response.concat("\nDisable RTO after ring: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[6] ? "Allowed" : "Disallowed");
+        _response.concat("\nRTO timeout: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[7] ? "Allowed" : "Disallowed");
+        _response.concat("\nDoorbell suppression: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[8] ? "Allowed" : "Disallowed");
+        _response.concat("\nDoorbell suppression duration: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[9] ? "Allowed" : "Disallowed");
+        _response.concat("\nSound Ring: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[10] ? "Allowed" : "Disallowed");
+        _response.concat("\nSound Open: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[11] ? "Allowed" : "Disallowed");
+        _response.concat("\nSound RTO: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[12] ? "Allowed" : "Disallowed");
+        _response.concat("\nSound CM: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[13] ? "Allowed" : "Disallowed");
+        _response.concat("\nSound confirmation: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[14] ? "Allowed" : "Disallowed");
+        _response.concat("\nSound level: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[15] ? "Allowed" : "Disallowed");
+        _response.concat("\nSingle button press action: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[16] ? "Allowed" : "Disallowed");
+        _response.concat("\nDouble button press action: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[17] ? "Allowed" : "Disallowed");
+        _response.concat("\nBattery type: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[18] ? "Allowed" : "Disallowed");
+        _response.concat("\nAutomatic battery type detection: ");
+        _response.concat((int)advancedOpenerConfigAclPrefs[19] ? "Allowed" : "Disallowed");
+        if(_preferences->getBool(preference_show_secrets))
         {
             char tmp[16];
             unsigned char currentBleAddressOpn[6];
@@ -2913,70 +3140,41 @@ void WebCfgServer::buildInfoHtml(String &response)
             nukiBlePref.getBytes("secretKeyK", secretKeyKOpn, 32);
             nukiBlePref.getBytes("authorizationId", authorizationIdOpn, 4);
             nukiBlePref.end();
-            response.concat("Opener bleAddress: ");
-            for (int i = 0; i < 6; i++) {
-              sprintf(tmp, "%02x", currentBleAddressOpn[i]);
-              response.concat(tmp);
+            _response.concat("\n\n------------ NUKI OPENER PAIRING ------------");
+            _response.concat("\nBLE Address: ");
+            for (int i = 0; i < 6; i++)
+            {
+                sprintf(tmp, "%02x", currentBleAddressOpn[i]);
+                _response.concat(tmp);
             }
-            response.concat("\nOpener secretKeyK: ");
-            for (int i = 0; i < 32; i++) {
-              sprintf(tmp, "%02x", secretKeyKOpn[i]);
-              response.concat(tmp);
+            _response.concat("\nSecretKeyK: ");
+            for (int i = 0; i < 32; i++)
+            {
+                sprintf(tmp, "%02x", secretKeyKOpn[i]);
+                _response.concat(tmp);
             }
-            response.concat("\nOpener authorizationId: ");
-            for (int i = 0; i < 4; i++) {
-              sprintf(tmp, "%02x", authorizationIdOpn[i]);
-              response.concat(tmp);
+            _response.concat("\nAuthorizationId: ");
+            for (int i = 0; i < 4; i++)
+            {
+                sprintf(tmp, "%02x", authorizationIdOpn[i]);
+                _response.concat(tmp);
             }
-            response.concat("\n");
         }
     }
 
-    response.concat("Network device: ");
-    response.concat(_network->networkDeviceName());
-    response.concat("\n");
+    _response.concat("\n\n------------ GPIO ------------");
+    _gpio->getConfigurationText(_response, _gpio->pinConfiguration());
 
-    if(_network->networkDeviceName() == "Built-in Wi-Fi")
-    {
-        response.concat("BSSID of AP: ");
-        response.concat(_network->networkBSSID());
-        response.concat("\n");
-    }
-
-    response.concat("Uptime: ");
-    response.concat(esp_timer_get_time() / 1000 / 1000 / 60);
-    response.concat(" minutes\n");
-
-    response.concat("Heap: ");
-    response.concat(esp_get_free_heap_size());
-    response.concat("\n");
-
-    response.concat("Stack watermarks: nw: ");
-    response.concat(uxTaskGetStackHighWaterMark(networkTaskHandle));
-    response.concat(", nuki: ");
-    response.concat(uxTaskGetStackHighWaterMark(nukiTaskHandle));
-    response.concat("\n");
-
-    _gpio->getConfigurationText(response, _gpio->pinConfiguration());
-
-    response.concat("Restart reason FW: ");
-    response.concat(getRestartReason());
-    response.concat( "\n");
-
-    response.concat("Restart reason ESP: ");
-    response.concat(getEspRestartReason());
-    response.concat("\n");
-
-    response.concat("
"); + _response.concat("
"); } void WebCfgServer::processUnpair(bool opener) { - String response = ""; + _response = ""; if(_server.args() == 0) { - buildConfirmHtml(response, "Confirm code is invalid.", 3); - _server.send(200, "text/html", response); + buildConfirmHtml("Confirm code is invalid.", 3); + _server.send(200, "text/html", _response); return; } else @@ -2986,14 +3184,14 @@ void WebCfgServer::processUnpair(bool opener) if(key != "CONFIRMTOKEN" || value != _confirmCode) { - buildConfirmHtml(response, "Confirm code is invalid.", 3); - _server.send(200, "text/html", response); + buildConfirmHtml("Confirm code is invalid.", 3); + _server.send(200, "text/html", _response); return; } } - buildConfirmHtml(response, opener ? "Unpairing Nuki Opener and restarting." : "Unpairing Nuki Lock and restarting.", 3); - _server.send(200, "text/html", response); + buildConfirmHtml(opener ? "Unpairing Nuki Opener and restarting." : "Unpairing Nuki Lock and restarting.", 3); + _server.send(200, "text/html", _response); if(!opener && _nuki != nullptr) { _nuki->disableHASS(); @@ -3010,7 +3208,6 @@ void WebCfgServer::processUnpair(bool opener) void WebCfgServer::processUpdate() { - String response = ""; String key = _server.argName(0); String key2 = _server.argName(1); String key3 = _server.argName(2); @@ -3019,8 +3216,8 @@ void WebCfgServer::processUpdate() if(key3 != "token" || value3 != _confirmCode) { - buildConfirmHtml(response, "Confirm code is invalid.", 3, true); - _server.send(200, "text/html", response); + buildConfirmHtml("Confirm code is invalid.", 3, true); + _server.send(200, "text/html", _response); return; } @@ -3028,13 +3225,13 @@ void WebCfgServer::processUpdate() { if(key2 == "debug") { - buildConfirmHtml(response, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG BETA version", 2, true); + buildConfirmHtml("Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG BETA version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL_DBG); _preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL_DBG); } else { - buildConfirmHtml(response, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest BETA version", 2, true); + buildConfirmHtml("Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest BETA version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_BETA_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_BETA_RELEASE_BINARY_URL); } @@ -3043,13 +3240,13 @@ void WebCfgServer::processUpdate() { if(key2 == "debug") { - buildConfirmHtml(response, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG DEVELOPMENT version", 2, true); + buildConfirmHtml("Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG DEVELOPMENT version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL_DBG); _preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL_DBG); } else { - buildConfirmHtml(response, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEVELOPMENT version", 2, true); + buildConfirmHtml("Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEVELOPMENT version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_MASTER_RELEASE_BINARY_URL); } @@ -3058,18 +3255,18 @@ void WebCfgServer::processUpdate() { if(key2 == "debug") { - buildConfirmHtml(response, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG RELEASE version", 2, true); + buildConfirmHtml("Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest DEBUG RELEASE version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL_DBG); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_UPDATER_BINARY_URL_DBG); } else { - buildConfirmHtml(response, "Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest RELEASE version", 2, true); + buildConfirmHtml("Rebooting to update Nuki Hub and Nuki Hub updater
Updating to latest RELEASE version", 2, true); _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); } } - _server.send(200, "text/html", response); + _server.send(200, "text/html", _response); waitAndProcess(true, 1000); restartEsp(RestartReason::OTAReboot); } @@ -3077,11 +3274,11 @@ void WebCfgServer::processUpdate() void WebCfgServer::processFactoryReset() { bool resetWifi = false; - String response = ""; + _response = ""; if(_server.args() == 0) { - buildConfirmHtml(response, "Confirm code is invalid.", 3); - _server.send(200, "text/html", response); + buildConfirmHtml("Confirm code is invalid.", 3); + _server.send(200, "text/html", _response); return; } else @@ -3091,8 +3288,8 @@ void WebCfgServer::processFactoryReset() if(key != "CONFIRMTOKEN" || value != _confirmCode) { - buildConfirmHtml(response, "Confirm code is invalid.", 3); - _server.send(200, "text/html", response); + buildConfirmHtml("Confirm code is invalid.", 3); + _server.send(200, "text/html", _response); return; } @@ -3102,12 +3299,12 @@ void WebCfgServer::processFactoryReset() if(key2 == "WIFI" && value2 == "1") { resetWifi = true; - buildConfirmHtml(response, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener and resetting WiFi.", 3); + buildConfirmHtml("Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener and resetting WiFi.", 3); } - else buildConfirmHtml(response, "Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener.", 3); + else buildConfirmHtml("Factory resetting Nuki Hub, unpairing Nuki Lock and Nuki Opener.", 3); } - _server.send(200, "text/html", response); + _server.send(200, "text/html", _response); waitAndProcess(false, 2000); if(_nuki != nullptr) @@ -3137,8 +3334,7 @@ void WebCfgServer::processFactoryReset() restartEsp(RestartReason::NukiHubReset); } -void WebCfgServer::printInputField(String& response, - const char *token, +void WebCfgServer::printInputField(const char *token, const char *description, const char *value, const size_t& maxLength, @@ -3150,40 +3346,39 @@ void WebCfgServer::printInputField(String& response, itoa(maxLength, maxLengthStr, 10); - response.concat(""); - response.concat(description); + _response.concat(""); + _response.concat(description); if(showLengthRestriction) { - response.concat(" (Max. "); - response.concat(maxLength); - response.concat(" characters)"); + _response.concat(" (Max. "); + _response.concat(maxLength); + _response.concat(" characters)"); } - response.concat(""); - response.concat(""); + _response.concat(""); - response.concat(""); + _response.concat("\" name=\""); + _response.concat(token); + _response.concat("\" size=\"25\" maxlength=\""); + _response.concat(maxLengthStr); + _response.concat("\"/>"); + _response.concat(""); } -void WebCfgServer::printInputField(String& response, - const char *token, +void WebCfgServer::printInputField(const char *token, const char *description, const int value, size_t maxLength, @@ -3191,148 +3386,147 @@ void WebCfgServer::printInputField(String& response, { char valueStr[20]; itoa(value, valueStr, 10); - printInputField(response, token, description, valueStr, maxLength, id); + printInputField(token, description, valueStr, maxLength, id); } -void WebCfgServer::printCheckBox(String &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.concat(""); - response.concat(description); - response.concat(""); + _response.concat(""); + _response.concat(description); + _response.concat(""); - response.concat(""); + _response.concat(""); - response.concat(""); + _response.concat("\" value=\"1\""); + _response.concat(value ? " checked=\"checked\"" : ""); + _response.concat("/>"); } -void WebCfgServer::printTextarea(String& response, - const char *token, - const char *description, - const char *value, - const size_t& maxLength, - const bool& enabled, - const bool& showLengthRestriction) +void WebCfgServer::printTextarea(const char *token, + const char *description, + const char *value, + const size_t& maxLength, + const bool& enabled, + const bool& showLengthRestriction) { char maxLengthStr[20]; itoa(maxLength, maxLengthStr, 10); - response.concat(""); - response.concat(description); + _response.concat(""); + _response.concat(description); if(showLengthRestriction) { - response.concat(" (Max. "); - response.concat(maxLength); - response.concat(" characters)"); + _response.concat(" (Max. "); + _response.concat(maxLength); + _response.concat(" characters)"); } - response.concat(""); - response.concat(" "); - response.concat(""); + _response.concat(" name=\""); + _response.concat(token); + _response.concat("\" maxlength=\""); + _response.concat(maxLengthStr); + _response.concat("\">"); + _response.concat(value); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::printDropDown(String &response, const char *token, const char *description, const String preselectedValue, const std::vector> options) +void WebCfgServer::printDropDown(const char *token, const char *description, const String preselectedValue, const std::vector> options) { - response.concat(""); - response.concat(description); - response.concat(""); + _response.concat(""); + _response.concat(description); + _response.concat(""); - response.concat(""); for(const auto& option : options) { if(option.first == preselectedValue) { - response.concat(""); + _response.concat(option.first); + _response.concat("\">"); + _response.concat(option.second); + _response.concat(""); } - response.concat(""); - response.concat(""); + _response.concat(""); + _response.concat(""); } -void WebCfgServer::buildNavigationButton(String &response, const char *caption, const char *targetPath, const char* labelText) +void WebCfgServer::buildNavigationButton(const char *caption, const char *targetPath, const char* labelText) { - response.concat("
"); - response.concat(" "); - response.concat(labelText); - response.concat("
"); + _response.concat("
"); + _response.concat(" "); + _response.concat(labelText); + _response.concat("
"); } -void WebCfgServer::buildNavigationMenuEntry(String &response, const char *title, const char *targetPath, const char* warningMessage) +void WebCfgServer::buildNavigationMenuEntry(const char *title, const char *targetPath, const char* warningMessage) { - response.concat(""); - response.concat("
  • "); - response.concat(title); + _response.concat(""); + _response.concat("
  • "); + _response.concat(title); if(strcmp(warningMessage, "") != 0){ - response.concat(""); - response.concat(warningMessage); - response.concat(""); + _response.concat(""); + _response.concat(warningMessage); + _response.concat(""); } - response.concat("
  • "); + _response.concat(""); } -void WebCfgServer::printParameter(String& 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.concat(""); - response.concat(""); - response.concat(description); - response.concat(""); - if(strcmp(id, "") == 0) response.concat(""); + _response.concat(""); + _response.concat(""); + _response.concat(description); + _response.concat(""); + if(strcmp(id, "") == 0) _response.concat(""); else { - response.concat(""); + _response.concat(""); } - if(strcmp(link, "") == 0) response.concat(value); + if(strcmp(link, "") == 0) _response.concat(value); else { - response.concat(" "); - response.concat(value); - response.concat(""); + _response.concat(" "); + _response.concat(value); + _response.concat(""); } - response.concat(""); - response.concat(""); + _response.concat(""); + _response.concat(""); } diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index fbcf541..413bb1f 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -49,34 +49,34 @@ private: bool processArgs(String& message); bool processImport(String& message); void processGpioArgs(); - void buildHtml(String& response); - void buildAccLvlHtml(String& response); - void buildCredHtml(String& response); - void buildImportExportHtml(String& response); - void buildMqttConfigHtml(String& response); - void buildStatusHtml(String& response); - void buildAdvancedConfigHtml(String& response); - void buildNukiConfigHtml(String& response); - void buildGpioConfigHtml(String& response); - void buildConfigureWifiHtml(String& response); - void buildInfoHtml(String& response); + void buildHtml(); + void buildAccLvlHtml(); + void buildCredHtml(); + void buildImportExportHtml(); + void buildMqttConfigHtml(); + void buildStatusHtml(); + void buildAdvancedConfigHtml(); + void buildNukiConfigHtml(); + void buildGpioConfigHtml(); + void buildConfigureWifiHtml(); + void buildInfoHtml(); void processUnpair(bool opener); void processUpdate(); void processFactoryReset(); - void printInputField(String& response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* id, const bool& isPassword = false, const bool& showLengthRestriction = false); - void printInputField(String& response, const char* token, const char* description, const int value, size_t maxLength, const char* id); - void printCheckBox(String& response, const char* token, const char* description, const bool value, const char* htmlClass); - void printTextarea(String& response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false); - void printDropDown(String &response, const char *token, const char *description, const String preselectedValue, std::vector> options); - void buildNavigationButton(String& response, const char* caption, const char* targetPath, const char* labelText = ""); - void buildNavigationMenuEntry(String &response, const char *title, const char *targetPath, const char* warningMessage = ""); + void printInputField(const char* token, const char* description, const char* value, const size_t& maxLength, const char* id, const bool& isPassword = false, const bool& showLengthRestriction = false); + void printInputField(const char* token, const char* description, const int value, size_t maxLength, const char* id); + 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); + 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; String getPreselectionForGpio(const uint8_t& pin); String pinStateToString(uint8_t value); - void printParameter(String& 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; @@ -87,12 +87,12 @@ private: String generateConfirmCode(); String _confirmCode = "----"; - void buildConfirmHtml(String& response, const String &message, uint32_t redirectDelay = 5, bool redirect = false); - void buildOtaHtml(String& response, bool errored, bool debug = false); - void buildOtaCompletedHtml(String& response); + void buildConfirmHtml(const String &message, uint32_t redirectDelay = 5, bool redirect = false); + void buildOtaHtml(bool errored, bool debug = false); + void buildOtaCompletedHtml(); void sendCss(); void sendFavicon(); - void buildHtmlHeader(String& response, String additionalHeader = ""); + void buildHtmlHeader(String additionalHeader = ""); void waitAndProcess(const bool blocking, const uint32_t duration); void handleOtaUpload(); @@ -109,5 +109,6 @@ private: uint32_t _transferredSize = 0; int64_t _otaStartTs = 0; String _hostname; + String _response; bool _enabled = true; }; diff --git a/src/main.cpp b/src/main.cpp index 8e33944..dbd6fbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,7 +121,8 @@ void setReroute(){ void networkTask(void *pvParameters) { int64_t networkLoopTs = 0; - bool secrets = preferences->getBool(preference_show_secrets); + bool secrets = preferences->getBool(preference_show_secrets, false); + bool webEnabled = preferences->getBool(preference_webserver_enabled, true); bool reroute = true; while(true) @@ -436,7 +437,7 @@ void setup() gpio = new Gpio(preferences); String gpioDesc; gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r"); - Serial.print(gpioDesc.c_str()); + Log->print(gpioDesc.c_str()); bleScanner = new BleScanner::Scanner(); // Scan interval and window according to Nuki recommendations: @@ -498,7 +499,7 @@ void setup() webserialserver.begin(); #endif - if((partitionType==1 && preferences->getString(preference_ota_updater_url).length() > 0) || (partitionType==2 && preferences->getString(preference_ota_main_url).length() > 0)) setupTasks(true); + if((partitionType==1 && preferences->getString(preference_ota_updater_url, "").length() > 0) || (partitionType==2 && preferences->getString(preference_ota_main_url, "").length() > 0)) setupTasks(true); else setupTasks(false); } diff --git a/src/networkDevices/IPConfiguration.cpp b/src/networkDevices/IPConfiguration.cpp index 70eeb19..81bd4b4 100644 --- a/src/networkDevices/IPConfiguration.cpp +++ b/src/networkDevices/IPConfiguration.cpp @@ -5,16 +5,16 @@ IPConfiguration::IPConfiguration(Preferences *preferences) : _preferences(preferences) { - if(_preferences->getString(preference_ip_address).length() <= 0) + if(_preferences->getString(preference_ip_address, "").length() <= 0) { Log->println("IP address empty, falling back to DHCP."); _preferences->putBool(preference_ip_dhcp_enabled, true); } - _ipAddress.fromString(_preferences->getString(preference_ip_address)); - _subnet.fromString(_preferences->getString(preference_ip_subnet)); - _gateway.fromString(_preferences->getString(preference_ip_gateway)); - _dnsServer.fromString(_preferences->getString(preference_ip_dns_server)); + _ipAddress.fromString(_preferences->getString(preference_ip_address, "")); + _subnet.fromString(_preferences->getString(preference_ip_subnet, "")); + _gateway.fromString(_preferences->getString(preference_ip_gateway, "")); + _dnsServer.fromString(_preferences->getString(preference_ip_dns_server, "")); Log->print(F("IP configuration: ")); if(dhcpEnabled()) @@ -32,7 +32,7 @@ IPConfiguration::IPConfiguration(Preferences *preferences) bool IPConfiguration::dhcpEnabled() const { - return _preferences->getBool(preference_ip_dhcp_enabled); + return _preferences->getBool(preference_ip_dhcp_enabled, true); } const IPAddress IPConfiguration::ipAddress() const diff --git a/src/networkDevices/W5500Device.cpp b/src/networkDevices/W5500Device.cpp index 3e4de3a..5934e34 100644 --- a/src/networkDevices/W5500Device.cpp +++ b/src/networkDevices/W5500Device.cpp @@ -197,7 +197,7 @@ void W5500Device::initializeMacAddress(byte *mac) mac[1] = 0x08; // wiznet prefix mac[2] = 0xDC; // wiznet prefix - if(_preferences->getBool(preference_has_mac_saved)) + if(_preferences->getBool(preference_has_mac_saved, false)) { mac[3] = _preferences->getChar(preference_has_mac_byte_0); mac[4] = _preferences->getChar(preference_has_mac_byte_1); diff --git a/src/networkDevices/WifiDevice.cpp b/src/networkDevices/WifiDevice.cpp index b3072ee..db3e8f0 100644 --- a/src/networkDevices/WifiDevice.cpp +++ b/src/networkDevices/WifiDevice.cpp @@ -13,12 +13,12 @@ RTC_NOINIT_ATTR char WiFiDevice_reconfdetect[17]; WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration) : NetworkDevice(hostname, ipConfiguration), _preferences(preferences), - _wm(preferences->getString(preference_cred_user).c_str(), preferences->getString(preference_cred_password).c_str()) + _wm(preferences->getString(preference_cred_user, "").c_str(), preferences->getString(preference_cred_password, "").c_str()) { _startAp = strcmp(WiFiDevice_reconfdetect, "reconfigure_wifi") == 0; #ifndef NUKI_HUB_UPDATER - _restartOnDisconnect = preferences->getBool(preference_restart_on_disconnect); + _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); @@ -75,7 +75,7 @@ void WifiDevice::initialize() std::vector wm_menu; wm_menu.push_back("wifi"); wm_menu.push_back("exit"); - _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled)); + _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); @@ -167,14 +167,14 @@ ReconnectStatus WifiDevice::reconnect(bool force) _isReconnecting = false; } - if(!isConnected() && _disconnectTs > (esp_timer_get_time() / 1000) - 120000) _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled)); + 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 || !_preferences->getBool(preference_network_wifi_fallback_disabled)); + _wm.setEnableConfigPortal(_startAp || !_preferences->getBool(preference_network_wifi_fallback_disabled, false)); } void WifiDevice::onDisconnected() diff --git a/updater/pio_package.py b/updater/pio_package.py index 01143bf..b163694 100644 --- a/updater/pio_package.py +++ b/updater/pio_package.py @@ -1,9 +1,7 @@ """ PlatformIO POST script execution to copy updater """ Import("env") -import glob -import os -import shutil +import glob, re, shutil, os from pathlib import Path def get_board_name(env): @@ -42,3 +40,13 @@ def remove_files(source, target, env): env.AddPostAction("$BUILD_DIR/firmware.bin", copy_files) env.AddPostAction("$BUILD_DIR/firmware.bin", remove_files) + +regex = r"\#define NUKI_HUB_DATE \"(.*)\"" +content_new = "" + +with open ('../src/Config.h', 'r' ) as readfile: + file_content = readfile.read() + content_new = re.sub(regex, "#define NUKI_HUB_DATE \"unknownbuilddate\"", file_content, flags = re.M) + +with open('../src/Config.h', 'w') as writefile: + writefile.write(content_new) diff --git a/updater/pio_package_pre.py b/updater/pio_package_pre.py index 0b08ca1..32e9045 100644 --- a/updater/pio_package_pre.py +++ b/updater/pio_package_pre.py @@ -1,6 +1,15 @@ +Import("env") import re, shutil, os from datetime import datetime, timezone +def recursive_purge(dir, pattern): + if os.path.isdir(dir): + for f in os.listdir(dir): + if os.path.isdir(os.path.join(dir, f)): + recursive_purge(os.path.join(dir, f), pattern) + elif re.search(pattern, os.path.join(dir, f)): + os.remove(os.path.join(dir, f)) + regex = r"\#define NUKI_HUB_DATE \"(.*)\"" content_new = "" @@ -12,3 +21,12 @@ with open('../src/Config.h', 'w') as writefile: writefile.write(content_new) shutil.copy("../src/main.cpp", "src/main.cpp") +recursive_purge("managed_components", ".component_hash") + +if env.get('BOARD_MCU') == "esp32": + board = "esp32dev" +else: + board = env.get('BOARD_MCU') + +if os.path.exists("sdkconfig.updater_" + board): + os.remove("sdkconfig." + board) \ No newline at end of file diff --git a/updater/sdkconfig.defaults b/updater/sdkconfig.defaults index 5fc3cb8..d643fde 100644 --- a/updater/sdkconfig.defaults +++ b/updater/sdkconfig.defaults @@ -12,4 +12,11 @@ CONFIG_ARDUINO_SELECTIVE_COMPILATION=y CONFIG_ARDUINO_SELECTIVE_HTTPClient=n CONFIG_ARDUINO_SELECTIVE_WebServer=n CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y -CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y \ No newline at end of file +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y +CONFIG_LOG_COLORS=n +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=n +CONFIG_LOG_MAXIMUM_LEVEL=4 +CONFIG_LOG_DEFAULT_LEVEL_NONE=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL=1 \ No newline at end of file diff --git a/webflash/manifest.json b/webflash/manifest.json deleted file mode 100644 index 08171a4..0000000 --- a/webflash/manifest.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "nukihub", - "new_install_prompt_erase": true, - "builds": [ - { - "chipFamily": "ESP32", - "parts": [ - { "path": "webflash_nuki_hub_esp32.bin", "offset": 0 } - ] - }, - { - "chipFamily": "ESP32-S3", - "parts": [ - { "path": "webflash_nuki_hub_esp32s3.bin", "offset": 0 } - ] - }, - { - "chipFamily": "ESP32-C3", - "parts": [ - { "path": "webflash_nuki_hub_esp32c3.bin", "offset": 0 } - ] - }, - { - "chipFamily": "ESP32-C6", - "parts": [ - { "path": "webflash_nuki_hub_esp32c6.bin", "offset": 0 } - ] - } - ] -} diff --git a/webflash/webflash_nuki_hub_esp32.bin b/webflash/webflash_nuki_hub_esp32.bin deleted file mode 100755 index 245b6fd..0000000 Binary files a/webflash/webflash_nuki_hub_esp32.bin and /dev/null differ diff --git a/webflash/webflash_nuki_hub_esp32c3.bin b/webflash/webflash_nuki_hub_esp32c3.bin deleted file mode 100755 index a8a7129..0000000 Binary files a/webflash/webflash_nuki_hub_esp32c3.bin and /dev/null differ diff --git a/webflash/webflash_nuki_hub_esp32s3.bin b/webflash/webflash_nuki_hub_esp32s3.bin deleted file mode 100755 index 53c6c3f..0000000 Binary files a/webflash/webflash_nuki_hub_esp32s3.bin and /dev/null differ