From e16ba6263123f718838d2087d3a61c1921038b88 Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 29 Mar 2024 10:48:20 +0100 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ea34708fd173fb2648f34f78389df05662431281 Merge: e14877b 1dcef13 Author: Jan-Ole Schümann Date: Tue Mar 26 20:16:00 2024 +0700 Merge pull request #328 from iranl/ha-fixes Multiple fixes (Nuki ID + README + ACL + DoorSensor/Keypad) commit 1dcef135fcb0e138f421ab2b389dbcb34469262f Author: iranl Date: Sun Mar 24 22:28:44 2024 +0100 Add extra checks and auto retries commit 56d718bc000338bd13bd9c5d0dcd2486165d4ba5 Author: iranl Date: Sat Mar 23 19:52:50 2024 +0100 Remove Force options commit a3658bfd3c008263caa4d23bad218de34a664e14 Author: iranl Date: Sun Mar 17 22:57:24 2024 +0100 Update README.md commit ef7fe751eda6275a0e981d756710311d5cb7b5f0 Author: iranl Date: Sun Mar 17 22:50:46 2024 +0100 Update WebCfgServer.cpp commit ac375df39f87390f882bad585f316a3d548e2846 Author: iranl Date: Sun Mar 17 22:47:42 2024 +0100 Redact Nuki ID commit 516af39fe6690cd09e31b1d89288f144db2ee36e Author: iranl Date: Sun Mar 17 22:40:22 2024 +0100 ACL Info WebCfg + Force Keypad/DoorSensor commit 3db4c0699e1a2237c4669b2108650a5649dce022 Author: iranl Date: Sun Mar 17 21:59:57 2024 +0100 Nuki ID + README + ACL fix --- NukiOpenerWrapper.cpp | 53 +++++++++++++++++++++++++++++++++++-------- NukiOpenerWrapper.h | 1 + NukiWrapper.cpp | 51 +++++++++++++++++++++++++++++++---------- NukiWrapper.h | 1 + PreferencesKeys.h | 12 ++++++---- README.md | 4 +++- WebCfgServer.cpp | 40 +++++++++++++++++++++++++++++++- main.cpp | 4 ++++ 8 files changed, 137 insertions(+), 29 deletions(-) diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index 3d3b3b8..b1f5aa5 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(); @@ -395,17 +396,48 @@ void NukiOpenerWrapper::updateConfig() readConfig(); readAdvancedConfig(); _configRead = true; - _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; + bool expectedConfig = true; 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 || _retryConfigCount == 10) + { + _preferences->putUInt(preference_nuki_id_opener, _nukiConfig.nukiId); + } + + 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); + _retryConfigCount = 0; + } + else + { + expectedConfig = false; + ++_retryConfigCount; + } } - if(_nukiAdvancedConfigValid) + 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; } } @@ -442,7 +474,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 +530,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 +784,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/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 d5e9766..35a4c3f 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; } @@ -361,16 +362,48 @@ void NukiWrapper::updateConfig() readConfig(); readAdvancedConfig(); _configRead = true; - _hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2; + bool expectedConfig = true; + 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 || _retryConfigCount == 10) + { + _preferences->putUInt(preference_nuki_id_lock, _nukiConfig.nukiId); + } + + 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); + _retryConfigCount = 0; + } + else + { + expectedConfig = false; + ++_retryConfigCount; + } } - if(_nukiAdvancedConfigValid) + 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; } } @@ -560,13 +593,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 +758,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/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; diff --git a/PreferencesKeys.h b/PreferencesKeys.h index 06ec03a..18edb41 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,11 +70,10 @@ 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_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_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, preference_network_hardware, preference_network_wifi_fallback_disabled, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, @@ -89,6 +90,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 = { 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/WebCfgServer.cpp b/WebCfgServer.cpp index ea43d99..c7c5389 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)); @@ -1089,6 +1089,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 +1106,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 +1137,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: "); 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 {