diff --git a/Config.h b/Config.h index bcfe984..03a030f 100644 --- a/Config.h +++ b/Config.h @@ -1,6 +1,6 @@ #pragma once -#define NUKI_HUB_VERSION "8.5" +#define NUKI_HUB_VERSION "8.6" #define MQTT_QOS_LEVEL 1 #define MQTT_CLEAN_SESSIONS false \ No newline at end of file diff --git a/NetworkLock.cpp b/NetworkLock.cpp index d5138ba..e317dfe 100644 --- a/NetworkLock.cpp +++ b/NetworkLock.cpp @@ -546,7 +546,6 @@ void NetworkLock::publishKeypadEntry(const String topic, NukiLock::KeypadEntry e publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount); } - void NetworkLock::publishULong(const char *topic, const unsigned long value) { return _network->publishULong(_mqttPath, topic, value); diff --git a/NetworkLock.h b/NetworkLock.h index 0b00831..49c68f1 100644 --- a/NetworkLock.h +++ b/NetworkLock.h @@ -41,6 +41,8 @@ public: void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override; private: + bool comparePrefixedPath(const char* fullPath, const char* subPath); + void publishFloat(const char* topic, const float value, const uint8_t precision = 2); void publishInt(const char* topic, const int value); void publishUInt(const char* topic, const unsigned int value); @@ -50,7 +52,6 @@ private: bool publishString(const char* topic, const std::string& value); bool publishString(const char* topic, const char* value); void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry); - bool comparePrefixedPath(const char* fullPath, const char* subPath); String concat(String a, String b); diff --git a/NetworkOpener.cpp b/NetworkOpener.cpp index 9fe4898..51b838f 100644 --- a/NetworkOpener.cpp +++ b/NetworkOpener.cpp @@ -42,6 +42,20 @@ void NetworkOpener::initialize() { _network->subscribe(_mqttPath, topic); } + + if(_preferences->getBool(preference_keypad_control_enabled)) + { + _network->subscribe(_mqttPath, mqtt_topic_keypad_command_action); + _network->subscribe(_mqttPath, mqtt_topic_keypad_command_id); + _network->subscribe(_mqttPath, mqtt_topic_keypad_command_name); + _network->subscribe(_mqttPath, mqtt_topic_keypad_command_code); + _network->subscribe(_mqttPath, mqtt_topic_keypad_command_enabled); + _network->initTopic(_mqttPath, mqtt_topic_keypad_command_action, "--"); + _network->initTopic(_mqttPath, mqtt_topic_keypad_command_id, "0"); + _network->initTopic(_mqttPath, mqtt_topic_keypad_command_name, "--"); + _network->initTopic(_mqttPath, mqtt_topic_keypad_command_code, "000000"); + _network->initTopic(_mqttPath, mqtt_topic_keypad_command_enabled, "1"); + } } void NetworkOpener::update() @@ -76,6 +90,46 @@ void NetworkOpener::onMqttDataReceived(const char* topic, byte* payload, const u publishString(mqtt_topic_lock_action, success ? "ack" : "unknown_action"); } + if(processActions && comparePrefixedPath(topic, mqtt_topic_keypad_command_action)) + { + if(_keypadCommandReceivedReceivedCallback != nullptr) + { + if(strcmp(value, "--") == 0) return; + + _keypadCommandReceivedReceivedCallback(value, _keypadCommandId, _keypadCommandName, _keypadCommandCode, _keypadCommandEnabled); + + _keypadCommandId = 0; + _keypadCommandName = "--"; + _keypadCommandCode = "000000"; + _keypadCommandEnabled = 1; + + if(strcmp(value, "--") != 0) + { + publishString(mqtt_topic_keypad_command_action, "--"); + } + publishInt(mqtt_topic_keypad_command_id, _keypadCommandId); + publishString(mqtt_topic_keypad_command_name, _keypadCommandName); + publishString(mqtt_topic_keypad_command_code, _keypadCommandCode); + publishInt(mqtt_topic_keypad_command_enabled, _keypadCommandEnabled); + } + } + else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_id)) + { + _keypadCommandId = atoi(value); + } + else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_name)) + { + _keypadCommandName = value; + } + else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_code)) + { + _keypadCommandCode = value; + } + else if(comparePrefixedPath(topic, mqtt_topic_keypad_command_enabled)) + { + _keypadCommandEnabled = atoi(value); + } + for(auto configTopic : _configTopics) { if(comparePrefixedPath(topic, configTopic)) @@ -399,6 +453,36 @@ void NetworkOpener::removeHASSConfig(char* uidString) _network->removeHASSConfig(uidString); } +void NetworkOpener::publishKeypad(const std::list& entries, uint maxKeypadCodeCount) +{ + uint index = 0; + for(const auto& entry : entries) + { + String basePath = mqtt_topic_keypad; + basePath.concat("/code_"); + basePath.concat(std::to_string(index).c_str()); + publishKeypadEntry(basePath, entry); + + ++index; + } + while(index < maxKeypadCodeCount) + { + NukiLock::KeypadEntry entry; + memset(&entry, 0, sizeof(entry)); + String basePath = mqtt_topic_keypad; + basePath.concat("/code_"); + basePath.concat(std::to_string(index).c_str()); + publishKeypadEntry(basePath, entry); + + ++index; + } +} + +void NetworkOpener::publishKeypadCommandResult(const char* result) +{ + publishString(mqtt_topic_keypad_command_result, result); +} + void NetworkOpener::setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char *)) { _lockActionReceivedCallback = lockActionReceivedCallback; @@ -409,6 +493,11 @@ void NetworkOpener::setConfigUpdateReceivedCallback(void (*configUpdateReceivedC _configUpdateReceivedCallback = configUpdateReceivedCallback; } +void NetworkOpener::setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled)) +{ + _keypadCommandReceivedReceivedCallback = keypadCommandReceivedReceivedCallback; +} + void NetworkOpener::publishFloat(const char *topic, const float value, const uint8_t precision) { _network->publishFloat(_mqttPath, topic, value, precision); @@ -450,6 +539,24 @@ void NetworkOpener::publishString(const char* topic, const char* value) _network->publishString(_mqttPath, topic, value); } +void NetworkOpener::publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry) +{ + char codeName[sizeof(entry.name) + 1]; + memset(codeName, 0, sizeof(codeName)); + memcpy(codeName, entry.name, sizeof(entry.name)); + + publishInt(concat(topic, "/id").c_str(), entry.codeId); + publishBool(concat(topic, "/enabled").c_str(), entry.enabled); + publishString(concat(topic, "/name").c_str(), codeName); + publishInt(concat(topic, "/createdYear").c_str(), entry.dateCreatedYear); + publishInt(concat(topic, "/createdMonth").c_str(), entry.dateCreatedMonth); + publishInt(concat(topic, "/createdDay").c_str(), entry.dateCreatedDay); + publishInt(concat(topic, "/createdHour").c_str(), entry.dateCreatedHour); + publishInt(concat(topic, "/createdMin").c_str(), entry.dateCreatedMin); + publishInt(concat(topic, "/createdSec").c_str(), entry.dateCreatedSec); + publishInt(concat(topic, "/lockCount").c_str(), entry.lockCount); +} + void NetworkOpener::buildMqttPath(const char* path, char* outPath) { int offset = 0; @@ -486,3 +593,10 @@ bool NetworkOpener::comparePrefixedPath(const char *fullPath, const char *subPat return strcmp(fullPath, prefixedPath) == 0; } + +String NetworkOpener::concat(String a, String b) +{ + String c = a; + c.concat(b); + return c; +} diff --git a/NetworkOpener.h b/NetworkOpener.h index 873a5ee..82b9d41 100644 --- a/NetworkOpener.h +++ b/NetworkOpener.h @@ -32,9 +32,12 @@ public: void publishBleAddress(const std::string& address); void publishHASSConfig(char* deviceType, const char* baseTopic, char* name, char* uidString, char* lockAction, char* unlockAction, char* openAction, char* lockedState, char* unlockedState); void removeHASSConfig(char* uidString); + void publishKeypad(const std::list& entries, uint maxKeypadCodeCount); + void publishKeypadCommandResult(const char* result); void setLockActionReceivedCallback(bool (*lockActionReceivedCallback)(const char* value)); void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char* path, const char* value)); + void setKeypadCommandReceivedCallback(void (*keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled)); void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) override; @@ -48,11 +51,14 @@ private: void publishString(const char* topic, const String& value); void publishString(const char* topic, const std::string& value); void publishString(const char* topic, const char* value); + void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry); void buildMqttPath(const char* path, char* outPath); void subscribe(const char* path); void logactionCompletionStatusToString(uint8_t value, char* out); + String concat(String a, String b); + Preferences* _preferences; Network* _network = nullptr; @@ -64,8 +70,13 @@ private: bool _firstTunerStatePublish = true; bool _haEnabled= false; + String _keypadCommandName = ""; + String _keypadCommandCode = ""; + uint _keypadCommandId = 0; + int _keypadCommandEnabled = 1; unsigned long _resetLockStateTs = 0; bool (*_lockActionReceivedCallback)(const char* value) = nullptr; void (*_configUpdateReceivedCallback)(const char* path, const char* value) = nullptr; + void (*_keypadCommandReceivedReceivedCallback)(const char* command, const uint& id, const String& name, const String& code, const int& enabled) = nullptr; }; diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index 091d1c2..26a0c39 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -25,6 +25,7 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, uint32_t id, network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback); network->setConfigUpdateReceivedCallback(nukiOpenerInst->onConfigUpdateReceivedCallback); + network->setKeypadCommandReceivedCallback(nukiOpenerInst->onKeypadCommandReceivedCallback); } @@ -40,10 +41,12 @@ void NukiOpenerWrapper::initialize() _nukiOpener.registerBleScanner(_bleScanner); _intervalLockstate = _preferences->getInt(preference_query_interval_lockstate); - _intervalConfig = _preferences->getInt(preference_query_interval_battery); - _intervalBattery = _preferences->getInt(preference_query_interval_battery); _intervalConfig = _preferences->getInt(preference_query_interval_configuration); + _intervalBattery = _preferences->getInt(preference_query_interval_battery); + _intervalKeypad = _preferences->getInt(preference_query_interval_keypad); + _keypadEnabled = _preferences->getBool(preference_keypad_control_enabled); _publishAuthData = _preferences->getBool(preference_publish_authdata); + _maxKeypadCodeCount = _preferences->getUInt(preference_opener_max_keypad_code_count); _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries); @@ -71,6 +74,11 @@ void NukiOpenerWrapper::initialize() _intervalBattery = 60 * 30; _preferences->putInt(preference_query_interval_battery, _intervalBattery); } + if(_intervalKeypad == 0) + { + _intervalKeypad = 60 * 30; + _preferences->putInt(preference_query_interval_keypad, _intervalKeypad); + } /* if(_intervalKeypad == 0) { @@ -172,6 +180,12 @@ void NukiOpenerWrapper::update() } } + if(_hasKeypad && _keypadEnabled && (_nextKeypadUpdateTs == 0 || ts > _nextKeypadUpdateTs)) + { + _nextKeypadUpdateTs = ts + _intervalKeypad * 1000; + updateKeypad(); + } + if(_nextLockAction != (NukiOpener::LockAction)0xff && ts > _nextRetryTs) { Nuki::CmdResult cmdResult = _nukiOpener.lockAction(_nextLockAction, 0, 0); @@ -296,6 +310,8 @@ void NukiOpenerWrapper::updateConfig() { readConfig(); readAdvancedConfig(); + _configRead = true; + _hasKeypad = _nukiConfig.hasKeypad > 0; _network->publishConfig(_nukiConfig); _network->publishAdvancedConfig(_nukiAdvancedConfig); } @@ -328,6 +344,35 @@ void NukiOpenerWrapper::updateAuthData() postponeBleWatchdog(); } +void NukiOpenerWrapper::updateKeypad() +{ + Nuki::CmdResult result = _nukiOpener.retrieveKeypadEntries(0, 0xffff); + if(result == 1) + { + std::list entries; + _nukiOpener.getKeypadEntries(&entries); + + entries.sort([](const NukiLock::KeypadEntry& a, const NukiLock::KeypadEntry& b) { return a.codeId < b.codeId; }); + + uint keypadCount = entries.size(); + if(keypadCount > _maxKeypadCodeCount) + { + _maxKeypadCodeCount = keypadCount; + _preferences->putUInt(preference_opener_max_keypad_code_count, _maxKeypadCodeCount); + } + + _network->publishKeypad(entries, _maxKeypadCodeCount); + + _keypadCodeIds.clear(); + _keypadCodeIds.reserve(entries.size()); + for(const auto& entry : entries) + { + _keypadCodeIds.push_back(entry.codeId); + } + } + postponeBleWatchdog(); +} + void NukiOpenerWrapper::postponeBleWatchdog() { _disableBleWatchdogTs = millis() + 15000; @@ -358,6 +403,10 @@ void NukiOpenerWrapper::onConfigUpdateReceivedCallback(const char *topic, const nukiOpenerInst->onConfigUpdateReceived(topic, value); } +void NukiOpenerWrapper::onKeypadCommandReceivedCallback(const char *command, const uint &id, const String &name, const String &code, const int& enabled) +{ + nukiOpenerInst->onKeypadCommandReceived(command, id, name, code, enabled); +} void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *value) { @@ -384,6 +433,117 @@ void NukiOpenerWrapper::onConfigUpdateReceived(const char *topic, const char *va } } +void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint &id, const String &name, const String &code, const int& enabled) +{ + if(!_hasKeypad) + { + if(_configRead) + { + _network->publishKeypadCommandResult("KeypadNotAvailable"); + } + return; + } + if(!_keypadEnabled) + { + return; + } + + bool idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), id) != _keypadCodeIds.end(); + int codeInt = code.toInt(); + bool codeValid = codeInt > 100000 && codeInt < 1000000 && (code.indexOf('0') == -1); + NukiLock::CmdResult result = (NukiLock::CmdResult)-1; + + if(strcmp(command, "add") == 0) + { + if(name == "" || name == "--") + { + _network->publishKeypadCommandResult("MissingParameterName"); + return; + } + if(codeInt == 0) + { + _network->publishKeypadCommandResult("MissingParameterCode"); + return; + } + if(!codeValid) + { + _network->publishKeypadCommandResult("CodeInvalid"); + return; + } + + NukiLock::NewKeypadEntry entry; + memset(&entry, 0, sizeof(entry)); + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + entry.code = codeInt; + result = _nukiOpener.addKeypadEntry(entry); + Log->print("Add keypad code: "); Log->println((int)result); + updateKeypad(); + } + else if(strcmp(command, "delete") == 0) + { + if(!idExists) + { + _network->publishKeypadCommandResult("UnknownId"); + return; + } + result = _nukiOpener.deleteKeypadEntry(id); + Log->print("Delete keypad code: "); Log->println((int)result); + updateKeypad(); + } + else if(strcmp(command, "update") == 0) + { + if(name == "" || name == "--") + { + _network->publishKeypadCommandResult("MissingParameterName"); + return; + } + if(codeInt == 0) + { + _network->publishKeypadCommandResult("MissingParameterCode"); + return; + } + if(!codeValid) + { + _network->publishKeypadCommandResult("CodeInvalid"); + return; + } + if(!idExists) + { + _network->publishKeypadCommandResult("UnknownId"); + return; + } + + NukiLock::UpdatedKeypadEntry entry; + memset(&entry, 0, sizeof(entry)); + entry.codeId = id; + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + entry.code = codeInt; + entry.enabled = enabled == 0 ? 0 : 1; + result = _nukiOpener.updateKeypadEntry(entry); + Log->print("Update keypad code: "); Log->println((int)result); + updateKeypad(); + } + else if(command == "--") + { + return; + } + else + { + _network->publishKeypadCommandResult("UnknownCommand"); + return; + } + + if((int)result != -1) + { + char resultStr[15]; + memset(&resultStr, 0, sizeof(resultStr)); + NukiOpener::cmdResultToString(result, resultStr); + _network->publishKeypadCommandResult(resultStr); + } +} + const NukiOpener::OpenerState &NukiOpenerWrapper::keyTurnerState() { return _keyTurnerState; diff --git a/NukiOpenerWrapper.h b/NukiOpenerWrapper.h index 8d2710c..92d8f9b 100644 --- a/NukiOpenerWrapper.h +++ b/NukiOpenerWrapper.h @@ -33,12 +33,15 @@ public: private: static bool onLockActionReceivedCallback(const char* value); static void onConfigUpdateReceivedCallback(const char* topic, const char* value); + static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled); void onConfigUpdateReceived(const char* topic, const char* value); + void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled); void updateKeyTurnerState(); void updateBatteryState(); void updateConfig(); void updateAuthData(); + void updateKeypad(); void postponeBleWatchdog(); void readConfig(); @@ -56,6 +59,7 @@ private: int _intervalLockstate = 0; // seconds int _intervalBattery = 0; // seconds int _intervalConfig = 60 * 60; // seconds + int _intervalKeypad = 0; // seconds int _restartBeaconTimeout = 0; // seconds bool _publishAuthData = false; bool _clearAuthData = false; @@ -63,6 +67,7 @@ private: int _retryDelay = 0; int _retryCount = 0; unsigned long _nextRetryTs = 0; + std::vector _keypadCodeIds; NukiOpener::OpenerState _lastKeyTurnerState; NukiOpener::OpenerState _keyTurnerState; @@ -79,10 +84,15 @@ private: bool _paired = false; bool _statusUpdated = false; + bool _hasKeypad = false; + bool _keypadEnabled = false; + uint _maxKeypadCodeCount = 0; + bool _configRead = false; long _rssiPublishInterval = 0; unsigned long _nextLockStateUpdateTs = 0; unsigned long _nextBatteryReportTs = 0; unsigned long _nextConfigUpdateTs = 0; + unsigned long _nextKeypadUpdateTs = 0; unsigned long _nextPairTs = 0; long _nextRssiTs = 0; unsigned long _lastRssi = 0; diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index ddaf762..e287826 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -47,7 +47,7 @@ void NukiWrapper::initialize(const bool& firstStart) _intervalKeypad = _preferences->getInt(preference_query_interval_keypad); _keypadEnabled = _preferences->getBool(preference_keypad_control_enabled); _publishAuthData = _preferences->getBool(preference_publish_authdata); - _maxKeypadCodeCount = _preferences->getUInt(preference_max_keypad_code_count); + _maxKeypadCodeCount = _preferences->getUInt(preference_lock_max_keypad_code_count); _restartBeaconTimeout = _preferences->getInt(preference_restart_ble_beacon_lost); _hassEnabled = _preferences->getString(preference_mqtt_hass_discovery) != ""; _nrOfRetries = _preferences->getInt(preference_command_nr_of_retries); @@ -357,7 +357,7 @@ void NukiWrapper::updateKeypad() if(keypadCount > _maxKeypadCodeCount) { _maxKeypadCodeCount = keypadCount; - _preferences->putUInt(preference_max_keypad_code_count, _maxKeypadCodeCount); + _preferences->putUInt(preference_lock_max_keypad_code_count, _maxKeypadCodeCount); } _network->publishKeypad(entries, _maxKeypadCodeCount); @@ -403,13 +403,11 @@ void NukiWrapper::onConfigUpdateReceivedCallback(const char *topic, const char * nukiInst->onConfigUpdateReceived(topic, value); } - void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uint &id, const String &name, const String &code, const int& enabled) { nukiInst->onKeypadCommandReceived(command, id, name, code, enabled); } - void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value) { if(strcmp(topic, mqtt_topic_config_button_enabled) == 0) diff --git a/NukiWrapper.h b/NukiWrapper.h index eb050e5..af94abf 100644 --- a/NukiWrapper.h +++ b/NukiWrapper.h @@ -85,8 +85,8 @@ private: bool _statusUpdated = false; bool _hasKeypad = false; bool _keypadEnabled = false; - bool _configRead = false; uint _maxKeypadCodeCount = 0; + bool _configRead = false; int _nrOfRetries = 0; int _retryDelay = 0; int _retryCount = 0; diff --git a/PreferencesKeys.h b/PreferencesKeys.h index c66dfb3..0403cb7 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -13,7 +13,8 @@ #define preference_mqtt_lock_path "mqttpath" #define preference_opener_enabled "openerena" #define preference_mqtt_opener_path "mqttoppath" -#define preference_max_keypad_code_count "maxkpad" +#define preference_lock_max_keypad_code_count "maxkpad" +#define preference_opener_max_keypad_code_count "opmaxkpad" #define preference_mqtt_ca "mqttca" #define preference_mqtt_crt "mqttcrt" #define preference_mqtt_key "mqttkey" @@ -50,7 +51,7 @@ class DebugPreferences private: std::vector _keys = { - preference_started_before, preference_deviceId, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_mqtt_opener_path, preference_max_keypad_code_count, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_network_hardware, preference_network_hardware_gpio, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_restart_timer, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_register_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, preference_cred_password, preference_publish_authdata, preference_gpio_locking_enabled, preference_publish_debug_info, preference_presence_detection_timeout, preference_has_mac_saved, preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2, + preference_started_before, preference_deviceId, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_network_hardware, preference_network_hardware_gpio, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_restart_timer, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_register_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, preference_cred_password, preference_publish_authdata, preference_gpio_locking_enabled, preference_publish_debug_info, preference_presence_detection_timeout, preference_has_mac_saved, preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2, }; std::vector _redact = { diff --git a/webflash/nuki_hub.bin b/webflash/nuki_hub.bin index be67bd1..91e3f03 100644 Binary files a/webflash/nuki_hub.bin and b/webflash/nuki_hub.bin differ