From 3db4c0699e1a2237c4669b2108650a5649dce022 Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 17 Mar 2024 21:59:57 +0100 Subject: [PATCH 1/7] Nuki ID + README + ACL fix --- NukiOpenerWrapper.cpp | 28 +++++++++++++++++++--------- NukiWrapper.cpp | 25 ++++++++++++++----------- PreferencesKeys.h | 7 ++++--- README.md | 4 +++- main.cpp | 4 ++++ 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index 3d3b3b8..974297b 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -314,6 +314,7 @@ void NukiOpenerWrapper::unpair() { _nukiOpener.unPairNuki(); _deviceId->assignNewId(); + _preferences->remove(preference_nuki_id_opener); _paired = false; } @@ -355,8 +356,8 @@ void NukiOpenerWrapper::updateKeyTurnerState() { Log->println(F("Nuki opener: Ring detected (Open)")); _network->publishRing(false); - } - + } + _network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState); updateGpioOutputs(); @@ -399,11 +400,19 @@ void NukiOpenerWrapper::updateConfig() if(_nukiConfigValid) { - _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]); - _network->publishConfig(_nukiConfig); + if(_preferences->getUInt(preference_nuki_id_opener, 0) == 0) + { + _preferences->putUInt(preference_nuki_id_opener, _nukiConfig.nukiId); + } + + if(_preferences->getUInt(preference_nuki_id_opener, 0) == _nukiConfig.nukiId) + { + _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]); + _network->publishConfig(_nukiConfig); + } } - if(_nukiAdvancedConfigValid) + if(_nukiAdvancedConfigValid && _preferences->getUInt(preference_nuki_id_opener, 0) == _nukiConfig.nukiId) { _network->publishAdvancedConfig(_nukiAdvancedConfig); } @@ -442,7 +451,7 @@ void NukiOpenerWrapper::updateAuthData() void NukiOpenerWrapper::updateKeypad() { if(_preferences->getBool(preference_keypad_info_enabled)) return; - + Log->print(F("Querying opener keypad: ")); Nuki::CmdResult result = _nukiOpener.retrieveKeypadEntries(0, 0xffff); printCommandResult(result); @@ -498,12 +507,12 @@ LockActionResult NukiOpenerWrapper::onLockActionReceivedCallback(const char *val { return LockActionResult::UnknownAction; } - + nukiOpenerPreferences = new Preferences(); nukiOpenerPreferences->begin("nukihub", true); uint32_t aclPrefs[17]; nukiOpenerPreferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); - + if((action == NukiOpener::LockAction::ActivateRTO && (int)aclPrefs[9] == 1) || (action == NukiOpener::LockAction::DeactivateRTO && (int)aclPrefs[10] == 1) || (action == NukiOpener::LockAction::ElectricStrikeActuation && (int)aclPrefs[11] == 1) || (action == NukiOpener::LockAction::ActivateCM && (int)aclPrefs[12] == 1) || (action == NukiOpener::LockAction::DeactivateCM && (int)aclPrefs[13] == 1) || (action == NukiOpener::LockAction::FobAction1 && (int)aclPrefs[14] == 1) || (action == NukiOpener::LockAction::FobAction2 && (int)aclPrefs[15] == 1) || (action == NukiOpener::LockAction::FobAction3 && (int)aclPrefs[16] == 1)) { nukiOpenerPreferences->end(); @@ -752,6 +761,7 @@ void NukiOpenerWrapper::readAdvancedConfig() void NukiOpenerWrapper::setupHASS() { if(!_nukiConfigValid) return; + if(_preferences->getUInt(preference_nuki_id_opener, 0) != _nukiConfig.nukiId) return; String baseTopic = _preferences->getString(preference_mqtt_opener_path); char uidString[20]; diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index d5e9766..91b7913 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -303,6 +303,7 @@ void NukiWrapper::unpair() { _nukiLock.unPairNuki(); _deviceId->assignNewId(); + _preferences->remove(preference_nuki_id_lock); _paired = false; } @@ -364,11 +365,19 @@ void NukiWrapper::updateConfig() _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; if(_nukiConfigValid) { - _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]); - _network->publishConfig(_nukiConfig); + if(_preferences->getUInt(preference_nuki_id_lock, 0) == 0) + { + _preferences->putUInt(preference_nuki_id_lock, _nukiConfig.nukiId); + } + + if(_preferences->getUInt(preference_nuki_id_lock, 0) == _nukiConfig.nukiId) + { + _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]); + _network->publishConfig(_nukiConfig); + } } - if(_nukiAdvancedConfigValid) + if(_nukiAdvancedConfigValid && _preferences->getUInt(preference_nuki_id_lock, 0) == _nukiConfig.nukiId) { _network->publishAdvancedConfig(_nukiAdvancedConfig); } @@ -560,13 +569,6 @@ void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value) _nukiLock.enableAutoLock(newValue); _nextConfigUpdateTs = millis() + 300; } - else if(strcmp(topic, mqtt_topic_config_auto_lock) == 0) - { - bool newValue = atoi(value) > 0; - if(!_nukiAdvancedConfigValid || _nukiAdvancedConfig.autoLockEnabled == newValue) return; - _nukiLock.enableAutoLock(newValue); - _nextConfigUpdateTs = millis() + 300; - } } void NukiWrapper::onKeypadCommandReceived(const char *command, const uint &id, const String &name, const String &code, const int& enabled) @@ -732,6 +734,7 @@ void NukiWrapper::readAdvancedConfig() void NukiWrapper::setupHASS() { if(!_nukiConfigValid) return; + if(_preferences->getUInt(preference_nuki_id_lock, 0) != _nukiConfig.nukiId) return; String baseTopic = _preferences->getString(preference_mqtt_lock_path); char uidString[20]; diff --git a/PreferencesKeys.h b/PreferencesKeys.h index 06ec03a..1fa1487 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -6,6 +6,8 @@ #define preference_config_version "confVersion" #define preference_device_id_lock "deviceId" #define preference_device_id_opener "deviceIdOp" +#define preference_nuki_id_lock "nukiId" +#define preference_nuki_id_opener "nukidOp" #define preference_mqtt_broker "mqttbroker" #define preference_mqtt_broker_port "mqttport" #define preference_mqtt_user "mqttuser" @@ -68,9 +70,8 @@ class DebugPreferences private: std::vector _keys = { - preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_mqtt_broker, - preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, - preference_mqtt_lock_path, preference_opener_enabled, preference_opener_continuous_mode, preference_mqtt_opener_path, + preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, + preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_continuous_mode, 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_mqtt_hass_cu_url, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, diff --git a/README.md b/README.md index edb09b3..f8d13db 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ Feel free to join us on Discord: https://discord.gg/feB9FnMY ## Supported devices Supported ESP32 devices: -- Any dual-core ESP32, except the ESP32-S3 (because of compilation issues) +- All dual-core ESP32 models with WIFI and BLE which are supported by Arduino Core 2.0.14 should work, but builds are currently only provided for the ESP32 and not for the ESP32-S3 or ESP32-C3. +- The ESP32-S2 has no BLE and as such can't run Nuki Hub. +- The ESP32-C6 and ESP32-H2 are not supported by Arduino Core 2.0.14 as such can't run Nuki Hub (at this time). Supported Nuki devices: - Nuki Smart Lock 1.0 diff --git a/main.cpp b/main.cpp index b945f8a..6be9e57 100644 --- a/main.cpp +++ b/main.cpp @@ -146,6 +146,10 @@ bool initPreferences() { preferences->putBool(preference_started_before, true); preferences->putBool(preference_lock_enabled, true); + preferences->putBool(preference_admin_enabled, true); + + uint32_t aclPrefs[17] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + preferences->putBytes(preference_acl, (byte*)(&aclPrefs), sizeof(aclPrefs)); } else { From 516af39fe6690cd09e31b1d89288f144db2ee36e Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 17 Mar 2024 22:40:22 +0100 Subject: [PATCH 2/7] ACL Info WebCfg + Force Keypad/DoorSensor --- NukiWrapper.cpp | 8 +++++++- PreferencesKeys.h | 9 ++++++--- WebCfgServer.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 91b7913..d484f5e 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -739,8 +739,12 @@ void NukiWrapper::setupHASS() String baseTopic = _preferences->getString(preference_mqtt_lock_path); char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); + + bool HASSkeypad = _hasKeypad; + + if(_preferences->getBool(preference_lock_force_keypad)) HASSkeypad = true; - _network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), _hasKeypad, _publishAuthData, "lock", "unlock", "unlatch"); + _network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), HASSkeypad, _publishAuthData, "lock", "unlock", "unlatch"); _hassSetupCompleted = true; Log->println("HASS setup for lock completed."); @@ -748,6 +752,8 @@ void NukiWrapper::setupHASS() bool NukiWrapper::hasDoorSensor() const { + if(_preferences->getBool(preference_lock_force_doorsensor)) return true; + return _keyTurnerState.doorSensorState == Nuki::DoorSensorState::DoorClosed || _keyTurnerState.doorSensorState == Nuki::DoorSensorState::DoorOpened || _keyTurnerState.doorSensorState == Nuki::DoorSensorState::Calibrating; diff --git a/PreferencesKeys.h b/PreferencesKeys.h index 1fa1487..3b7030b 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -21,6 +21,9 @@ #define preference_check_updates "checkupdates" #define preference_lock_max_keypad_code_count "maxkpad" #define preference_opener_max_keypad_code_count "opmaxkpad" +#define preference_lock_force_keypad "forcekpad" +#define preference_opener_force_keypad "opforcekpad" +#define preference_lock_force_doorsensor "forcedrsnsr" #define preference_mqtt_ca "mqttca" #define preference_mqtt_crt "mqttcrt" #define preference_mqtt_key "mqttkey" @@ -72,8 +75,8 @@ private: { preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_continuous_mode, 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_mqtt_hass_cu_url, + preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_lock_force_keypad, preference_opener_force_keypad, + preference_lock_force_doorsensor, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_network_wifi_fallback_disabled, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, @@ -95,7 +98,7 @@ private: { preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_admin_enabled, preference_keypad_info_enabled, - preference_register_as_app, preference_ip_dhcp_enabled, + preference_lock_force_keypad, preference_opener_force_keypad, preference_lock_force_doorsensor, preference_register_as_app, preference_ip_dhcp_enabled, preference_publish_authdata, preference_has_mac_saved, preference_publish_debug_info, preference_network_wifi_fallback_disabled }; diff --git a/WebCfgServer.cpp b/WebCfgServer.cpp index ea43d99..4754561 100644 --- a/WebCfgServer.cpp +++ b/WebCfgServer.cpp @@ -635,7 +635,7 @@ bool WebCfgServer::processArgs(String& message) _preferences->putString(preference_cred_password, ""); configChanged = true; } - + if(aclLvlChanged) { _preferences->putBytes(preference_acl, (byte*)(&aclPrefs), sizeof(aclPrefs)); @@ -939,7 +939,7 @@ void WebCfgServer::buildAccLvlHtml(String &response) response.concat("

Nuki General Access Control

"); response.concat(""); printCheckBox(response, "ACLCNF", "Change Nuki configuration", _preferences->getBool(preference_admin_enabled)); - if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) + if((_nuki != nullptr && (_preferences->getString(preference_lock_force_keypad) || _nuki->hasKeypad())) || (_nukiOpener != nullptr && (_preferences->getString(preference_opener_force_keypad) || _nukiOpener->hasKeypad()))) { printCheckBox(response, "KPPUB", "Publish keypad codes information", _preferences->getBool(preference_keypad_info_enabled)); printCheckBox(response, "KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled)); @@ -993,11 +993,14 @@ void WebCfgServer::buildNukiConfigHtml(String &response) if(_preferences->getBool(preference_lock_enabled)) { printInputField(response, "MQTTPATH", "MQTT Nuki Smartlock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180); + printCheckBox(response, "LCKFORCEKPAD", "Force Lock Keypad availability", _preferences->getBool(preference_lock_force_keypad)); + printCheckBox(response, "LCKFORCEDRSNSR", "Force Lock Door Sensor availability", _preferences->getBool(preference_lock_force_doorsensor)); } printCheckBox(response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled)); if(_preferences->getBool(preference_opener_enabled)) { printInputField(response, "MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180); + printCheckBox(response, "OPFORCEKPAD", "Force Opener Keypad availability", _preferences->getBool(preference_opener_force_keypad)); } response.concat("
SettingEnabled

"); @@ -1007,7 +1010,7 @@ void WebCfgServer::buildNukiConfigHtml(String &response) printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10); printInputField(response, "CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10); printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10); - if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) + if((_nuki != nullptr && (_preferences->getString(preference_lock_force_keypad) || _nuki->hasKeypad())) || (_nukiOpener != nullptr && (_preferences->getString(preference_opener_force_keypad) || _nukiOpener->hasKeypad()))) { printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10); } @@ -1089,6 +1092,9 @@ void WebCfgServer::buildInfoHtml(String &response) response.concat("MQTT connected: "); response.concat(_network->mqttConnectionState() > 0 ? "Yes\n" : "No\n"); + uint32_t aclPrefs[17]; + _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); + if(_nuki != nullptr) { response.concat("Lock firmware version: "); @@ -1103,7 +1109,26 @@ void WebCfgServer::buildInfoHtml(String &response) 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"); } + if(_nukiOpener != nullptr) { response.concat("Opener firmware version: "); @@ -1115,6 +1140,22 @@ void WebCfgServer::buildInfoHtml(String &response) response.concat(_nukiOpener->isPaired() ? _nukiOpener->isPinSet() ? "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("Network device: "); From ac375df39f87390f882bad585f316a3d548e2846 Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 17 Mar 2024 22:47:42 +0100 Subject: [PATCH 3/7] Redact Nuki ID --- PreferencesKeys.h | 1 + 1 file changed, 1 insertion(+) diff --git a/PreferencesKeys.h b/PreferencesKeys.h index 3b7030b..aabfa9a 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -93,6 +93,7 @@ private: preference_mqtt_user, preference_mqtt_password, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_cred_user, preference_cred_password, + preference_nuki_id_lock, preference_nuki_id_opener, }; std::vector _boolPrefs = { From ef7fe751eda6275a0e981d756710311d5cb7b5f0 Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 17 Mar 2024 22:50:46 +0100 Subject: [PATCH 4/7] Update WebCfgServer.cpp --- WebCfgServer.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/WebCfgServer.cpp b/WebCfgServer.cpp index 4754561..2dec953 100644 --- a/WebCfgServer.cpp +++ b/WebCfgServer.cpp @@ -568,6 +568,21 @@ bool WebCfgServer::processArgs(String& message) _preferences->putBool(preference_opener_enabled, (value == "1")); configChanged = true; } + else if(key == "LCKFORCEDRSNSR") + { + _preferences->putBool(preference_lock_force_doorsensor, (value == "1")); + configChanged = true; + } + else if(key == "LCKFORCEKPAD") + { + _preferences->putBool(preference_lock_force_keypad, (value == "1")); + configChanged = true; + } + else if(key == "OPFORCEKPAD") + { + _preferences->putBool(preference_opener_force_keypad, (value == "1")); + configChanged = true; + } else if(key == "CREDUSER") { if(value == "#") From a3658bfd3c008263caa4d23bad218de34a664e14 Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 17 Mar 2024 22:57:24 +0100 Subject: [PATCH 5/7] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f8d13db..3eaa625 100644 --- a/README.md +++ b/README.md @@ -130,8 +130,11 @@ In a browser navigate to the IP address assigned to the ESP32. - Nuki Smartlock enabled: Enable if you want Nuki Hub to connect to a Nuki Lock (1.0-4.0) - MQTT Nuki Smartlock Path (Lock only): Set to the preferred MQTT root topic for the Nuki Lock, defaults to "nuki". Make sure this topic is not the same as the setting for the opener and is unique when using multiple Nuki Hub devices (when using multiple Nuki Locks) +- Force Lock Keypad availability (Lock only): Enable to force availability of a Nuki Keypad connected to the lock. When disabled keypad can be considered not available (for a short period) and lead to removal from Home Assistant discovery in certain cases. +- Force Lock Door Sensor availability (Lock only): Enable to force availability of a Nuki Door Sensor connected to the lock. When disabled door sensor can be considered not available (for a short period) and lead to removal from Home Assistant discovery in certain cases. - Nuki Opener enabled: Enable if you want Nuki Hub to connect to a Nuki Opener - MQTT Nuki Opener Path (Opener only): Set to the preferred MQTT root topic for the Nuki Opener, defaults to "nukiopener". Make sure this topic is not the same as the setting for the lock and is unique when using multiple Nuki Hub devices (when using multiple Nuki Openers) +- Force Opener Keypad availability (Opener only): Enable to force availability of a Nuki Keypad connected to the opener. When disabled keypad can be considered not available (for a short period) and lead to removal from Home Assistant discovery in certain cases. #### Advanced Nuki Configuration From 56d718bc000338bd13bd9c5d0dcd2486165d4ba5 Mon Sep 17 00:00:00 2001 From: iranl Date: Sat, 23 Mar 2024 19:52:50 +0100 Subject: [PATCH 6/7] Remove Force options --- NukiOpenerWrapper.cpp | 2 +- NukiWrapper.cpp | 11 +++-------- PreferencesKeys.h | 9 +++------ README.md | 3 --- WebCfgServer.cpp | 22 ++-------------------- 5 files changed, 9 insertions(+), 38 deletions(-) diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index 974297b..ef78887 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -396,7 +396,6 @@ void NukiOpenerWrapper::updateConfig() readConfig(); readAdvancedConfig(); _configRead = true; - _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; if(_nukiConfigValid) { @@ -407,6 +406,7 @@ void NukiOpenerWrapper::updateConfig() if(_preferences->getUInt(preference_nuki_id_opener, 0) == _nukiConfig.nukiId) { + _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; _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]); _network->publishConfig(_nukiConfig); diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index d484f5e..412c345 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -362,7 +362,7 @@ void NukiWrapper::updateConfig() readConfig(); readAdvancedConfig(); _configRead = true; - _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; + if(_nukiConfigValid) { if(_preferences->getUInt(preference_nuki_id_lock, 0) == 0) @@ -372,6 +372,7 @@ void NukiWrapper::updateConfig() if(_preferences->getUInt(preference_nuki_id_lock, 0) == _nukiConfig.nukiId) { + _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; _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]); _network->publishConfig(_nukiConfig); @@ -739,12 +740,8 @@ void NukiWrapper::setupHASS() String baseTopic = _preferences->getString(preference_mqtt_lock_path); char uidString[20]; itoa(_nukiConfig.nukiId, uidString, 16); - - bool HASSkeypad = _hasKeypad; - - if(_preferences->getBool(preference_lock_force_keypad)) HASSkeypad = true; - _network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), HASSkeypad, _publishAuthData, "lock", "unlock", "unlatch"); + _network->publishHASSConfig("SmartLock", baseTopic.c_str(),(char*)_nukiConfig.name, uidString, hasDoorSensor(), _hasKeypad, _publishAuthData, "lock", "unlock", "unlatch"); _hassSetupCompleted = true; Log->println("HASS setup for lock completed."); @@ -752,8 +749,6 @@ void NukiWrapper::setupHASS() bool NukiWrapper::hasDoorSensor() const { - if(_preferences->getBool(preference_lock_force_doorsensor)) return true; - return _keyTurnerState.doorSensorState == Nuki::DoorSensorState::DoorClosed || _keyTurnerState.doorSensorState == Nuki::DoorSensorState::DoorOpened || _keyTurnerState.doorSensorState == Nuki::DoorSensorState::Calibrating; diff --git a/PreferencesKeys.h b/PreferencesKeys.h index aabfa9a..18edb41 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -21,9 +21,6 @@ #define preference_check_updates "checkupdates" #define preference_lock_max_keypad_code_count "maxkpad" #define preference_opener_max_keypad_code_count "opmaxkpad" -#define preference_lock_force_keypad "forcekpad" -#define preference_opener_force_keypad "opforcekpad" -#define preference_lock_force_doorsensor "forcedrsnsr" #define preference_mqtt_ca "mqttca" #define preference_mqtt_crt "mqttcrt" #define preference_mqtt_key "mqttkey" @@ -75,8 +72,8 @@ private: { preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_continuous_mode, preference_mqtt_opener_path, - preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_lock_force_keypad, preference_opener_force_keypad, - preference_lock_force_doorsensor, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, + 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_mqtt_hass_cu_url, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_network_wifi_fallback_disabled, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, @@ -99,7 +96,7 @@ private: { preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_opener_continuous_mode, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_admin_enabled, preference_keypad_info_enabled, - preference_lock_force_keypad, preference_opener_force_keypad, preference_lock_force_doorsensor, preference_register_as_app, preference_ip_dhcp_enabled, + preference_register_as_app, preference_ip_dhcp_enabled, preference_publish_authdata, preference_has_mac_saved, preference_publish_debug_info, preference_network_wifi_fallback_disabled }; diff --git a/README.md b/README.md index 3eaa625..f8d13db 100644 --- a/README.md +++ b/README.md @@ -130,11 +130,8 @@ In a browser navigate to the IP address assigned to the ESP32. - Nuki Smartlock enabled: Enable if you want Nuki Hub to connect to a Nuki Lock (1.0-4.0) - MQTT Nuki Smartlock Path (Lock only): Set to the preferred MQTT root topic for the Nuki Lock, defaults to "nuki". Make sure this topic is not the same as the setting for the opener and is unique when using multiple Nuki Hub devices (when using multiple Nuki Locks) -- Force Lock Keypad availability (Lock only): Enable to force availability of a Nuki Keypad connected to the lock. When disabled keypad can be considered not available (for a short period) and lead to removal from Home Assistant discovery in certain cases. -- Force Lock Door Sensor availability (Lock only): Enable to force availability of a Nuki Door Sensor connected to the lock. When disabled door sensor can be considered not available (for a short period) and lead to removal from Home Assistant discovery in certain cases. - Nuki Opener enabled: Enable if you want Nuki Hub to connect to a Nuki Opener - MQTT Nuki Opener Path (Opener only): Set to the preferred MQTT root topic for the Nuki Opener, defaults to "nukiopener". Make sure this topic is not the same as the setting for the lock and is unique when using multiple Nuki Hub devices (when using multiple Nuki Openers) -- Force Opener Keypad availability (Opener only): Enable to force availability of a Nuki Keypad connected to the opener. When disabled keypad can be considered not available (for a short period) and lead to removal from Home Assistant discovery in certain cases. #### Advanced Nuki Configuration diff --git a/WebCfgServer.cpp b/WebCfgServer.cpp index 2dec953..c7c5389 100644 --- a/WebCfgServer.cpp +++ b/WebCfgServer.cpp @@ -568,21 +568,6 @@ bool WebCfgServer::processArgs(String& message) _preferences->putBool(preference_opener_enabled, (value == "1")); configChanged = true; } - else if(key == "LCKFORCEDRSNSR") - { - _preferences->putBool(preference_lock_force_doorsensor, (value == "1")); - configChanged = true; - } - else if(key == "LCKFORCEKPAD") - { - _preferences->putBool(preference_lock_force_keypad, (value == "1")); - configChanged = true; - } - else if(key == "OPFORCEKPAD") - { - _preferences->putBool(preference_opener_force_keypad, (value == "1")); - configChanged = true; - } else if(key == "CREDUSER") { if(value == "#") @@ -954,7 +939,7 @@ void WebCfgServer::buildAccLvlHtml(String &response) response.concat("

Nuki General Access Control

"); response.concat(""); printCheckBox(response, "ACLCNF", "Change Nuki configuration", _preferences->getBool(preference_admin_enabled)); - if((_nuki != nullptr && (_preferences->getString(preference_lock_force_keypad) || _nuki->hasKeypad())) || (_nukiOpener != nullptr && (_preferences->getString(preference_opener_force_keypad) || _nukiOpener->hasKeypad()))) + if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) { printCheckBox(response, "KPPUB", "Publish keypad codes information", _preferences->getBool(preference_keypad_info_enabled)); printCheckBox(response, "KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled)); @@ -1008,14 +993,11 @@ void WebCfgServer::buildNukiConfigHtml(String &response) if(_preferences->getBool(preference_lock_enabled)) { printInputField(response, "MQTTPATH", "MQTT Nuki Smartlock Path", _preferences->getString(preference_mqtt_lock_path).c_str(), 180); - printCheckBox(response, "LCKFORCEKPAD", "Force Lock Keypad availability", _preferences->getBool(preference_lock_force_keypad)); - printCheckBox(response, "LCKFORCEDRSNSR", "Force Lock Door Sensor availability", _preferences->getBool(preference_lock_force_doorsensor)); } printCheckBox(response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled)); if(_preferences->getBool(preference_opener_enabled)) { printInputField(response, "MQTTOPPATH", "MQTT Nuki Opener Path", _preferences->getString(preference_mqtt_opener_path).c_str(), 180); - printCheckBox(response, "OPFORCEKPAD", "Force Opener Keypad availability", _preferences->getBool(preference_opener_force_keypad)); } response.concat("
SettingEnabled

"); @@ -1025,7 +1007,7 @@ void WebCfgServer::buildNukiConfigHtml(String &response) printInputField(response, "LSTINT", "Query interval lock state (seconds)", _preferences->getInt(preference_query_interval_lockstate), 10); printInputField(response, "CFGINT", "Query interval configuration (seconds)", _preferences->getInt(preference_query_interval_configuration), 10); printInputField(response, "BATINT", "Query interval battery (seconds)", _preferences->getInt(preference_query_interval_battery), 10); - if((_nuki != nullptr && (_preferences->getString(preference_lock_force_keypad) || _nuki->hasKeypad())) || (_nukiOpener != nullptr && (_preferences->getString(preference_opener_force_keypad) || _nukiOpener->hasKeypad()))) + if((_nuki != nullptr && _nuki->hasKeypad()) || (_nukiOpener != nullptr && _nukiOpener->hasKeypad())) { printInputField(response, "KPINT", "Query interval keypad (seconds)", _preferences->getInt(preference_query_interval_keypad), 10); } From 1dcef135fcb0e138f421ab2b389dbcb34469262f Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 24 Mar 2024 22:28:44 +0100 Subject: [PATCH 7/7] Add extra checks and auto retries --- NukiOpenerWrapper.cpp | 25 ++++++++++++++++++++++++- NukiOpenerWrapper.h | 1 + NukiWrapper.cpp | 25 ++++++++++++++++++++++++- NukiWrapper.h | 1 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index ef78887..b1f5aa5 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -396,10 +396,11 @@ void NukiOpenerWrapper::updateConfig() readConfig(); readAdvancedConfig(); _configRead = true; + bool expectedConfig = true; if(_nukiConfigValid) { - if(_preferences->getUInt(preference_nuki_id_opener, 0) == 0) + if(_preferences->getUInt(preference_nuki_id_opener, 0) == 0 || _retryConfigCount == 10) { _preferences->putUInt(preference_nuki_id_opener, _nukiConfig.nukiId); } @@ -410,11 +411,33 @@ 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]); _network->publishConfig(_nukiConfig); + _retryConfigCount = 0; } + else + { + expectedConfig = false; + ++_retryConfigCount; + } + } + else + { + expectedConfig = false; + ++_retryConfigCount; } if(_nukiAdvancedConfigValid && _preferences->getUInt(preference_nuki_id_opener, 0) == _nukiConfig.nukiId) { _network->publishAdvancedConfig(_nukiAdvancedConfig); + _retryConfigCount = 0; + } + else + { + expectedConfig = false; + ++_retryConfigCount; + } + if(!expectedConfig && _retryConfigCount < 11) + { + unsigned long ts = millis(); + _nextConfigUpdateTs = ts + 60000; } } diff --git a/NukiOpenerWrapper.h b/NukiOpenerWrapper.h index cc4a758..657fe2b 100644 --- a/NukiOpenerWrapper.h +++ b/NukiOpenerWrapper.h @@ -88,6 +88,7 @@ private: int _nrOfRetries = 0; int _retryDelay = 0; int _retryCount = 0; + int _retryConfigCount = 0; int _retryLockstateCount = 0; unsigned long _nextRetryTs = 0; std::vector _keypadCodeIds; diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 412c345..35a4c3f 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -362,10 +362,11 @@ void NukiWrapper::updateConfig() readConfig(); readAdvancedConfig(); _configRead = true; + bool expectedConfig = true; if(_nukiConfigValid) { - if(_preferences->getUInt(preference_nuki_id_lock, 0) == 0) + if(_preferences->getUInt(preference_nuki_id_lock, 0) == 0 || _retryConfigCount == 10) { _preferences->putUInt(preference_nuki_id_lock, _nukiConfig.nukiId); } @@ -376,11 +377,33 @@ void NukiWrapper::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]); _network->publishConfig(_nukiConfig); + _retryConfigCount = 0; } + else + { + expectedConfig = false; + ++_retryConfigCount; + } + } + else + { + expectedConfig = false; + ++_retryConfigCount; } if(_nukiAdvancedConfigValid && _preferences->getUInt(preference_nuki_id_lock, 0) == _nukiConfig.nukiId) { _network->publishAdvancedConfig(_nukiAdvancedConfig); + _retryConfigCount = 0; + } + else + { + expectedConfig = false; + ++_retryConfigCount; + } + if(!expectedConfig && _retryConfigCount < 11) + { + unsigned long ts = millis(); + _nextConfigUpdateTs = ts + 60000; } } diff --git a/NukiWrapper.h b/NukiWrapper.h index 7ff26f4..2f68988 100644 --- a/NukiWrapper.h +++ b/NukiWrapper.h @@ -108,6 +108,7 @@ private: int _nrOfRetries = 0; int _retryDelay = 0; int _retryCount = 0; + int _retryConfigCount = 0; int _retryLockstateCount = 0; long _rssiPublishInterval = 0; unsigned long _nextRetryTs = 0;