From 3c94b047709b91fdb878c6fd3a23daf7bc7b2d32 Mon Sep 17 00:00:00 2001 From: iranl Date: Tue, 15 Oct 2024 21:08:45 +0200 Subject: [PATCH 1/3] Check keypad codes --- src/NukiOpenerWrapper.cpp | 615 +++++++++++++++++++++----------------- src/NukiOpenerWrapper.h | 4 + src/NukiWrapper.cpp | 615 +++++++++++++++++++++----------------- src/NukiWrapper.h | 4 + src/PreferencesKeys.h | 5 +- src/WebCfgServer.cpp | 19 +- 6 files changed, 698 insertions(+), 564 deletions(-) diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index 3ae17a7d..14c5a34f 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -89,6 +89,7 @@ void NukiOpenerWrapper::readSettings() _retryDelay = _preferences->getInt(preference_command_retry_delay); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); + _checkKeypadCodes = _preferences->getBool(preference_keypad_check_code_enabled, false); _preferences->getBytes(preference_conf_opener_basic_acl, &_basicOpenerConfigAclPrefs, sizeof(_basicOpenerConfigAclPrefs)); _preferences->getBytes(preference_conf_opener_advanced_acl, &_advancedOpenerConfigAclPrefs, sizeof(_advancedOpenerConfigAclPrefs)); @@ -253,6 +254,11 @@ void NukiOpenerWrapper::update() _nextKeypadUpdateTs = ts + _intervalKeypad * 1000; updateKeypad(false); } + + if(_checkKeypadCodes && _invalidCount > 0 && ts - 120000 < _lastCodeCheck) + { + _invalidCount--; + } if(_nextLockAction != (NukiOpener::LockAction)0xff) { @@ -714,10 +720,13 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved) _network->publishKeypad(entries, _maxKeypadCodeCount); _keypadCodeIds.clear(); + _keypadCodes.clear(); _keypadCodeIds.reserve(entries.size()); + _keypadCodes.reserve(entries.size()); for(const auto& entry : entries) { _keypadCodeIds.push_back(entry.codeId); + _keypadCodes.push_back(entry.code); } } @@ -1797,372 +1806,420 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value) idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end(); } - Nuki::CmdResult result = (Nuki::CmdResult)-1; - int retryCount = 0; + if(strcmp(action, "check") == 0) { + if(!_preferences->getBool(preference_keypad_check_code_enabled, false)) + { + _network->publishKeypadJsonCommandResult("checkingKeypadCodesDisabled"); + return; + } - while(retryCount < _nrOfRetries + 1) - { - if(strcmp(action, "delete") == 0) { - if(idExists) + if(pow(_invalidCount, 5) + _lastCodeCheck > (esp_timer_get_time() / 1000)) + { + _network->publishKeypadJsonCommandResult("checkingCodesBlockedTooManyInvalid"); + _lastCodeCheck = (esp_timer_get_time() / 1000); + return; + } + + _lastCodeCheck = (esp_timer_get_time() / 1000); + + if(idExists) + { + auto it1 = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId); + int index = it1 - _keypadCodeIds.begin(); + Log->print(F("Check keypad code: ")); + + if(code == _keypadCodes[index]) { - result = _nukiOpener.deleteKeypadEntry(codeId); - Log->print(F("Delete keypad code: ")); - Log->println((int)result); + _invalidCount = 0; + _network->publishKeypadJsonCommandResult("codeValid"); + Log->println("Valid"); + return; } else { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + _invalidCount++; + _network->publishKeypadJsonCommandResult("codeInvalid"); + Log->println("Invalid"); return; } } - else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) + else { - if(name.length() < 1) - { - if (strcmp(action, "update") != 0) + _invalidCount++; + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } + } + else + { + + Nuki::CmdResult result = (Nuki::CmdResult)-1; + int retryCount = 0; + + while(retryCount < _nrOfRetries + 1) + { + if(strcmp(action, "delete") == 0) { + if(idExists) { - _network->publishKeypadJsonCommandResult("noNameSet"); + result = _nukiOpener.deleteKeypadEntry(codeId); + Log->print(F("Delete keypad code: ")); + Log->println((int)result); + } + else + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); return; } } - - if(code != 12) + else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) { - String codeStr = json["code"].as(); - bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + if(name.length() < 1) + { + if (strcmp(action, "update") != 0) + { + _network->publishKeypadJsonCommandResult("noNameSet"); + return; + } + } - if (!codeValid) + if(code != 12) + { + String codeStr = json["code"].as(); + bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + + if (!codeValid) + { + _network->publishKeypadJsonCommandResult("noValidCodeSet"); + return; + } + } + else if (strcmp(action, "update") != 0) { - _network->publishKeypadJsonCommandResult("noValidCodeSet"); + _network->publishKeypadJsonCommandResult("noCodeSet"); return; } - } - else if (strcmp(action, "update") != 0) - { - _network->publishKeypadJsonCommandResult("noCodeSet"); - return; - } - unsigned int allowedFromAr[6]; - unsigned int allowedUntilAr[6]; - unsigned int allowedFromTimeAr[2]; - unsigned int allowedUntilTimeAr[2]; - uint8_t allowedWeekdaysInt = 0; + unsigned int allowedFromAr[6]; + unsigned int allowedUntilAr[6]; + unsigned int allowedFromTimeAr[2]; + unsigned int allowedUntilTimeAr[2]; + uint8_t allowedWeekdaysInt = 0; - if(timeLimited == 1) - { - if(allowedFrom.length() > 0) + if(timeLimited == 1) { - if(allowedFrom.length() == 19) + if(allowedFrom.length() > 0) { - allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); - allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); - allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); - allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); - allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); - allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); - - if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + if(allowedFrom.length() == 19) + { + allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); + allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); + allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); + allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); + allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); + allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); + + if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); - return; - } - } - if(allowedUntil.length() > 0) - { - if(allowedUntil.length() > 0 == 19) + if(allowedUntil.length() > 0) { - allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); - allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); - allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); - allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); - allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); - allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); - - if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + if(allowedUntil.length() > 0 == 19) + { + allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); + allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); + allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); + allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); + allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); + allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); + + if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); - return; - } - } - if(allowedFromTime.length() > 0) - { - if(allowedFromTime.length() == 5) + if(allowedFromTime.length() > 0) { - allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); - allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); - - if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + if(allowedFromTime.length() == 5) + { + allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); + allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); + + if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); - return; - } - } - if(allowedUntilTime.length() > 0) - { - if(allowedUntilTime.length() == 5) + if(allowedUntilTime.length() > 0) { - allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); - allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); - - if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + if(allowedUntilTime.length() == 5) + { + allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); + allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); + + if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); - return; - } - } - - if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; - if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; - if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; - if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; - if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; - if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; - if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; - } - if(strcmp(action, "add") == 0) - { - NukiOpener::NewKeypadEntry entry; - memset(&entry, 0, sizeof(entry)); - size_t nameLen = name.length(); - memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); - entry.code = code; - entry.timeLimited = timeLimited == 1 ? 1 : 0; - - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; + if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; + if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; + if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; + if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; + if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; + if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; + if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; } - if(allowedUntil.length() > 0) + if(strcmp(action, "add") == 0) { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } + NukiOpener::NewKeypadEntry entry; + memset(&entry, 0, sizeof(entry)); + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + entry.code = code; + entry.timeLimited = timeLimited == 1 ? 1 : 0; - entry.allowedWeekdays = allowedWeekdaysInt; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } - if(allowedUntilTime.length() > 0) - { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; - } + entry.allowedWeekdays = allowedWeekdaysInt; - result = _nukiOpener.addKeypadEntry(entry); - Log->print(F("Add keypad code: ")); - Log->println((int)result); - } - else if (strcmp(action, "update") == 0) - { - if(!codeId) - { - _network->publishKeypadJsonCommandResult("noCodeIdSet"); - return; - } + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } - if(!idExists) - { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); - return; + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } + + result = _nukiOpener.addKeypadEntry(entry); + Log->print(F("Add keypad code: ")); + Log->println((int)result); } + else if (strcmp(action, "update") == 0) + { + if(!codeId) + { + _network->publishKeypadJsonCommandResult("noCodeIdSet"); + return; + } - Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); - bool foundExisting = false; + if(!idExists) + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } - if(resultKp == Nuki::CmdResult::Success) - { - delay(250); - std::list entries; - _nukiOpener.getKeypadEntries(&entries); + Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); + bool foundExisting = false; - for(const auto& entry : entries) + if(resultKp == Nuki::CmdResult::Success) { - if (codeId != entry.codeId) continue; - else foundExisting = true; + delay(250); + std::list entries; + _nukiOpener.getKeypadEntries(&entries); - if(name.length() < 1) - { - memset(oldName, 0, sizeof(oldName)); - memcpy(oldName, entry.name, sizeof(entry.name)); - } - if(code == 12) code = entry.code; - if(enabled == 2) enabled = entry.enabled; - if(timeLimited == 2) timeLimited = entry.timeLimited; - if(allowedFrom.length() < 1) + for(const auto& entry : entries) { - allowedFrom = "old"; - allowedFromAr[0] = entry.allowedFromYear; - allowedFromAr[1] = entry.allowedFromMonth; - allowedFromAr[2] = entry.allowedFromDay; - allowedFromAr[3] = entry.allowedFromHour; - allowedFromAr[4] = entry.allowedFromMin; - allowedFromAr[5] = entry.allowedFromSec; - } - if(allowedUntil.length() < 1) - { - allowedUntil = "old"; - allowedUntilAr[0] = entry.allowedUntilYear; - allowedUntilAr[1] = entry.allowedUntilMonth; - allowedUntilAr[2] = entry.allowedUntilDay; - allowedUntilAr[3] = entry.allowedUntilHour; - allowedUntilAr[4] = entry.allowedUntilMin; - allowedUntilAr[5] = entry.allowedUntilSec; - } - if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; - if(allowedFromTime.length() < 1) - { - allowedFromTime = "old"; - allowedFromTimeAr[0] = entry.allowedFromTimeHour; - allowedFromTimeAr[1] = entry.allowedFromTimeMin; + if (codeId != entry.codeId) continue; + else foundExisting = true; + + if(name.length() < 1) + { + memset(oldName, 0, sizeof(oldName)); + memcpy(oldName, entry.name, sizeof(entry.name)); + } + if(code == 12) code = entry.code; + if(enabled == 2) enabled = entry.enabled; + if(timeLimited == 2) timeLimited = entry.timeLimited; + if(allowedFrom.length() < 1) + { + allowedFrom = "old"; + allowedFromAr[0] = entry.allowedFromYear; + allowedFromAr[1] = entry.allowedFromMonth; + allowedFromAr[2] = entry.allowedFromDay; + allowedFromAr[3] = entry.allowedFromHour; + allowedFromAr[4] = entry.allowedFromMin; + allowedFromAr[5] = entry.allowedFromSec; + } + if(allowedUntil.length() < 1) + { + allowedUntil = "old"; + allowedUntilAr[0] = entry.allowedUntilYear; + allowedUntilAr[1] = entry.allowedUntilMonth; + allowedUntilAr[2] = entry.allowedUntilDay; + allowedUntilAr[3] = entry.allowedUntilHour; + allowedUntilAr[4] = entry.allowedUntilMin; + allowedUntilAr[5] = entry.allowedUntilSec; + } + if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; + if(allowedFromTime.length() < 1) + { + allowedFromTime = "old"; + allowedFromTimeAr[0] = entry.allowedFromTimeHour; + allowedFromTimeAr[1] = entry.allowedFromTimeMin; + } + + if(allowedUntilTime.length() < 1) + { + allowedUntilTime = "old"; + allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; + allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + } } - if(allowedUntilTime.length() < 1) + if(!foundExisting) { - allowedUntilTime = "old"; - allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; - allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); + return; } } - - if(!foundExisting) + else { _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); return; } - } - else - { - _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); - return; - } - NukiOpener::UpdatedKeypadEntry entry; - - memset(&entry, 0, sizeof(entry)); - entry.codeId = codeId; - entry.code = code; + NukiOpener::UpdatedKeypadEntry entry; - if(name.length() < 1) - { - size_t nameLen = strlen(oldName); - memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); - } - else - { - size_t nameLen = name.length(); - memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); - } - entry.enabled = enabled; - entry.timeLimited = timeLimited; + memset(&entry, 0, sizeof(entry)); + entry.codeId = codeId; + entry.code = code; - if(enabled == 1) - { - if(timeLimited == 1) + if(name.length() < 1) { - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; - } - - if(allowedUntil.length() > 0) - { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } - - entry.allowedWeekdays = allowedWeekdaysInt; - - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + size_t nameLen = strlen(oldName); + memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); + } + else + { + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + } + entry.enabled = enabled; + entry.timeLimited = timeLimited; - if(allowedUntilTime.length() > 0) + if(enabled == 1) + { + if(timeLimited == 1) { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } + + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } + + entry.allowedWeekdays = allowedWeekdaysInt; + + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } + + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } } } - } - result = _nukiOpener.updateKeypadEntry(entry); - Log->print(F("Update keypad code: ")); - Log->println((int)result); + result = _nukiOpener.updateKeypadEntry(entry); + Log->print(F("Update keypad code: ")); + Log->println((int)result); + } + } + else + { + _network->publishKeypadJsonCommandResult("invalidAction"); + return; } - } - else - { - _network->publishKeypadJsonCommandResult("invalidAction"); - return; - } - if(result != Nuki::CmdResult::Success) { - ++retryCount; + if(result != Nuki::CmdResult::Success) { + ++retryCount; + } + else break; } - else break; - } - updateKeypad(false); + updateKeypad(false); - if((int)result != -1) - { - char resultStr[15]; - memset(&resultStr, 0, sizeof(resultStr)); - NukiOpener::cmdResultToString(result, resultStr); - _network->publishKeypadJsonCommandResult(resultStr); + if((int)result != -1) + { + char resultStr[15]; + memset(&resultStr, 0, sizeof(resultStr)); + NukiOpener::cmdResultToString(result, resultStr); + _network->publishKeypadJsonCommandResult(resultStr); + } } } else diff --git a/src/NukiOpenerWrapper.h b/src/NukiOpenerWrapper.h index 1d5eb287..6853711d 100644 --- a/src/NukiOpenerWrapper.h +++ b/src/NukiOpenerWrapper.h @@ -106,12 +106,16 @@ class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler bool _publishAuthData = false; bool _clearAuthData = false; bool _disableNonJSON = false; + bool _checkKeypadCodes = false; int _nrOfRetries = 0; int _retryDelay = 0; int _retryConfigCount = 0; int _retryLockstateCount = 0; int64_t _nextRetryTs = 0; + int64_t _invalidCount = 0; + int64_t _lastCodeCheck = 0; std::vector _keypadCodeIds; + std::vector _keypadCodes; std::vector _timeControlIds; std::vector _authIds; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 7cc1092f..da04bfba 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -137,7 +137,7 @@ void NukiWrapper::readSettings() _retryDelay = _preferences->getInt(preference_command_retry_delay); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); - + _checkKeypadCodes = _preferences->getBool(preference_keypad_check_code_enabled, false); _preferences->getBytes(preference_conf_lock_basic_acl, &_basicLockConfigaclPrefs, sizeof(_basicLockConfigaclPrefs)); _preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs)); @@ -369,6 +369,10 @@ void NukiWrapper::update() _nextKeypadUpdateTs = ts + _intervalKeypad * 1000; updateKeypad(false); } + if(_checkKeypadCodes && _invalidCount > 0 && ts - 120000 < _lastCodeCheck) + { + _invalidCount--; + } } if(_clearAuthData) { @@ -773,10 +777,13 @@ void NukiWrapper::updateKeypad(bool retrieved) _network->publishKeypad(entries, _maxKeypadCodeCount); _keypadCodeIds.clear(); + _keypadCodes.clear(); _keypadCodeIds.reserve(entries.size()); + _keypadCodes.reserve(entries.size()); for(const auto& entry : entries) { _keypadCodeIds.push_back(entry.codeId); + _keypadCodes.push_back(entry.code); } } @@ -1918,373 +1925,421 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value) { idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end(); } + + if(strcmp(action, "check") == 0) { + if(!_preferences->getBool(preference_keypad_check_code_enabled, false)) + { + _network->publishKeypadJsonCommandResult("checkingKeypadCodesDisabled"); + return; + } - Nuki::CmdResult result = (Nuki::CmdResult)-1; - int retryCount = 0; + if(pow(_invalidCount, 5) + _lastCodeCheck > (esp_timer_get_time() / 1000)) + { + _network->publishKeypadJsonCommandResult("checkingCodesBlockedTooManyInvalid"); + _lastCodeCheck = (esp_timer_get_time() / 1000); + return; + } - while(retryCount < _nrOfRetries + 1) - { - if(strcmp(action, "delete") == 0) { - if(idExists) + _lastCodeCheck = (esp_timer_get_time() / 1000); + + if(idExists) + { + auto it1 = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId); + int index = it1 - _keypadCodeIds.begin(); + Log->print(F("Check keypad code: ")); + + if(code == _keypadCodes[index]) { - result = _nukiLock.deleteKeypadEntry(codeId); - Log->print(F("Delete keypad code: ")); - Log->println((int)result); + _invalidCount = 0; + _network->publishKeypadJsonCommandResult("codeValid"); + Log->println("Valid"); + return; } else { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + _invalidCount++; + _network->publishKeypadJsonCommandResult("codeInvalid"); + Log->println("Invalid"); return; } } - else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) + else { - if(name.length() < 1) - { - if (strcmp(action, "update") != 0) + _invalidCount++; + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } + } + else + { + + Nuki::CmdResult result = (Nuki::CmdResult)-1; + int retryCount = 0; + + while(retryCount < _nrOfRetries + 1) + { + if(strcmp(action, "delete") == 0) { + if(idExists) { - _network->publishKeypadJsonCommandResult("noNameSet"); + result = _nukiLock.deleteKeypadEntry(codeId); + Log->print(F("Delete keypad code: ")); + Log->println((int)result); + } + else + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); return; } } - - if(code != 12) + else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) { - String codeStr = json["code"].as(); - bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + if(name.length() < 1) + { + if (strcmp(action, "update") != 0) + { + _network->publishKeypadJsonCommandResult("noNameSet"); + return; + } + } - if (!codeValid) + if(code != 12) + { + String codeStr = json["code"].as(); + bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + + if (!codeValid) + { + _network->publishKeypadJsonCommandResult("noValidCodeSet"); + return; + } + } + else if (strcmp(action, "update") != 0) { - _network->publishKeypadJsonCommandResult("noValidCodeSet"); + _network->publishKeypadJsonCommandResult("noCodeSet"); return; } - } - else if (strcmp(action, "update") != 0) - { - _network->publishKeypadJsonCommandResult("noCodeSet"); - return; - } - unsigned int allowedFromAr[6]; - unsigned int allowedUntilAr[6]; - unsigned int allowedFromTimeAr[2]; - unsigned int allowedUntilTimeAr[2]; - uint8_t allowedWeekdaysInt = 0; + unsigned int allowedFromAr[6]; + unsigned int allowedUntilAr[6]; + unsigned int allowedFromTimeAr[2]; + unsigned int allowedUntilTimeAr[2]; + uint8_t allowedWeekdaysInt = 0; - if(timeLimited == 1) - { - if(allowedFrom.length() > 0) + if(timeLimited == 1) { - if(allowedFrom.length() == 19) + if(allowedFrom.length() > 0) { - allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); - allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); - allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); - allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); - allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); - allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); - - if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + if(allowedFrom.length() == 19) + { + allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); + allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); + allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); + allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); + allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); + allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); + + if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); - return; - } - } - if(allowedUntil.length() > 0) - { - if(allowedUntil.length() > 0 == 19) + if(allowedUntil.length() > 0) { - allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); - allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); - allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); - allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); - allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); - allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); - - if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + if(allowedUntil.length() > 0 == 19) + { + allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); + allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); + allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); + allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); + allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); + allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); + + if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); - return; - } - } - if(allowedFromTime.length() > 0) - { - if(allowedFromTime.length() == 5) + if(allowedFromTime.length() > 0) { - allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); - allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); - - if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + if(allowedFromTime.length() == 5) + { + allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); + allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); + + if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); - return; - } - } - if(allowedUntilTime.length() > 0) - { - if(allowedUntilTime.length() == 5) + if(allowedUntilTime.length() > 0) { - allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); - allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); - - if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + if(allowedUntilTime.length() == 5) + { + allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); + allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); + + if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); - return; - } - } - - if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; - if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; - if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; - if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; - if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; - if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; - if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; - } - - if(strcmp(action, "add") == 0) - { - 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 = code; - entry.timeLimited = timeLimited == 1 ? 1 : 0; - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; + if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; + if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; + if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; + if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; + if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; + if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; + if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; } - if(allowedUntil.length() > 0) + if(strcmp(action, "add") == 0) { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } + 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 = code; + entry.timeLimited = timeLimited == 1 ? 1 : 0; - entry.allowedWeekdays = allowedWeekdaysInt; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } - if(allowedUntilTime.length() > 0) - { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; - } + entry.allowedWeekdays = allowedWeekdaysInt; - result = _nukiLock.addKeypadEntry(entry); - Log->print(F("Add keypad code: ")); - Log->println((int)result); - } - else if (strcmp(action, "update") == 0) - { - if(!codeId) - { - _network->publishKeypadJsonCommandResult("noCodeIdSet"); - return; - } + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } - if(!idExists) - { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); - return; + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } + + result = _nukiLock.addKeypadEntry(entry); + Log->print(F("Add keypad code: ")); + Log->println((int)result); } + else if (strcmp(action, "update") == 0) + { + if(!codeId) + { + _network->publishKeypadJsonCommandResult("noCodeIdSet"); + return; + } - Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); - bool foundExisting = false; + if(!idExists) + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } - if(resultKp == Nuki::CmdResult::Success) - { - delay(250); - std::list entries; - _nukiLock.getKeypadEntries(&entries); + Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); + bool foundExisting = false; - for(const auto& entry : entries) + if(resultKp == Nuki::CmdResult::Success) { - if (codeId != entry.codeId) continue; - else foundExisting = true; + delay(250); + std::list entries; + _nukiLock.getKeypadEntries(&entries); - if(name.length() < 1) - { - memset(oldName, 0, sizeof(oldName)); - memcpy(oldName, entry.name, sizeof(entry.name)); - } - if(code == 12) code = entry.code; - if(enabled == 2) enabled = entry.enabled; - if(timeLimited == 2) timeLimited = entry.timeLimited; - if(allowedFrom.length() < 1) + for(const auto& entry : entries) { - allowedFrom = "old"; - allowedFromAr[0] = entry.allowedFromYear; - allowedFromAr[1] = entry.allowedFromMonth; - allowedFromAr[2] = entry.allowedFromDay; - allowedFromAr[3] = entry.allowedFromHour; - allowedFromAr[4] = entry.allowedFromMin; - allowedFromAr[5] = entry.allowedFromSec; - } - if(allowedUntil.length() < 1) - { - allowedUntil = "old"; - allowedUntilAr[0] = entry.allowedUntilYear; - allowedUntilAr[1] = entry.allowedUntilMonth; - allowedUntilAr[2] = entry.allowedUntilDay; - allowedUntilAr[3] = entry.allowedUntilHour; - allowedUntilAr[4] = entry.allowedUntilMin; - allowedUntilAr[5] = entry.allowedUntilSec; - } - if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; - if(allowedFromTime.length() < 1) - { - allowedFromTime = "old"; - allowedFromTimeAr[0] = entry.allowedFromTimeHour; - allowedFromTimeAr[1] = entry.allowedFromTimeMin; + if (codeId != entry.codeId) continue; + else foundExisting = true; + + if(name.length() < 1) + { + memset(oldName, 0, sizeof(oldName)); + memcpy(oldName, entry.name, sizeof(entry.name)); + } + if(code == 12) code = entry.code; + if(enabled == 2) enabled = entry.enabled; + if(timeLimited == 2) timeLimited = entry.timeLimited; + if(allowedFrom.length() < 1) + { + allowedFrom = "old"; + allowedFromAr[0] = entry.allowedFromYear; + allowedFromAr[1] = entry.allowedFromMonth; + allowedFromAr[2] = entry.allowedFromDay; + allowedFromAr[3] = entry.allowedFromHour; + allowedFromAr[4] = entry.allowedFromMin; + allowedFromAr[5] = entry.allowedFromSec; + } + if(allowedUntil.length() < 1) + { + allowedUntil = "old"; + allowedUntilAr[0] = entry.allowedUntilYear; + allowedUntilAr[1] = entry.allowedUntilMonth; + allowedUntilAr[2] = entry.allowedUntilDay; + allowedUntilAr[3] = entry.allowedUntilHour; + allowedUntilAr[4] = entry.allowedUntilMin; + allowedUntilAr[5] = entry.allowedUntilSec; + } + if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; + if(allowedFromTime.length() < 1) + { + allowedFromTime = "old"; + allowedFromTimeAr[0] = entry.allowedFromTimeHour; + allowedFromTimeAr[1] = entry.allowedFromTimeMin; + } + + if(allowedUntilTime.length() < 1) + { + allowedUntilTime = "old"; + allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; + allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + } } - if(allowedUntilTime.length() < 1) + if(!foundExisting) { - allowedUntilTime = "old"; - allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; - allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); + return; } } - - if(!foundExisting) + else { _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); return; } - } - else - { - _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); - return; - } - - NukiLock::UpdatedKeypadEntry entry; - memset(&entry, 0, sizeof(entry)); - entry.codeId = codeId; - entry.code = code; + NukiLock::UpdatedKeypadEntry entry; - if(name.length() < 1) - { - size_t nameLen = strlen(oldName); - memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); - } - else - { - size_t nameLen = name.length(); - memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); - } - entry.enabled = enabled; - entry.timeLimited = timeLimited; + memset(&entry, 0, sizeof(entry)); + entry.codeId = codeId; + entry.code = code; - if(enabled == 1) - { - if(timeLimited == 1) + if(name.length() < 1) { - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; - } - - if(allowedUntil.length() > 0) - { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } - - entry.allowedWeekdays = allowedWeekdaysInt; - - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + size_t nameLen = strlen(oldName); + memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); + } + else + { + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + } + entry.enabled = enabled; + entry.timeLimited = timeLimited; - if(allowedUntilTime.length() > 0) + if(enabled == 1) + { + if(timeLimited == 1) { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } + + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } + + entry.allowedWeekdays = allowedWeekdaysInt; + + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } + + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } } } - } - result = _nukiLock.updateKeypadEntry(entry); - Log->print(F("Update keypad code: ")); - Log->println((int)result); + result = _nukiLock.updateKeypadEntry(entry); + Log->print(F("Update keypad code: ")); + Log->println((int)result); + } + } + else + { + _network->publishKeypadJsonCommandResult("invalidAction"); + return; } - } - else - { - _network->publishKeypadJsonCommandResult("invalidAction"); - return; - } - if(result != Nuki::CmdResult::Success) { - ++retryCount; + if(result != Nuki::CmdResult::Success) { + ++retryCount; + } + else break; } - else break; - } - updateKeypad(false); + updateKeypad(false); - if((int)result != -1) - { - char resultStr[15]; - memset(&resultStr, 0, sizeof(resultStr)); - NukiLock::cmdResultToString(result, resultStr); - _network->publishKeypadJsonCommandResult(resultStr); + if((int)result != -1) + { + char resultStr[15]; + memset(&resultStr, 0, sizeof(resultStr)); + NukiLock::cmdResultToString(result, resultStr); + _network->publishKeypadJsonCommandResult(resultStr); + } } } else diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index ee695f98..0cf886bf 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -107,7 +107,11 @@ class NukiWrapper : public Nuki::SmartlockEventHandler int _restartBeaconTimeout = 0; // seconds bool _publishAuthData = false; bool _clearAuthData = false; + bool _checkKeypadCodes = false; + int64_t _invalidCount = 0; + int64_t _lastCodeCheck = 0; std::vector _keypadCodeIds; + std::vector _keypadCodes; std::vector _timeControlIds; std::vector _authIds; diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index fc0cb169..7edc9970 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -98,6 +98,7 @@ #define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis" #define preference_official_hybrid_actions (char*)"hybridAct" #define preference_official_hybrid_retry (char*)"hybridRtry" +#define preference_keypad_check_code_enabled (char*)"kpChkEna" //NOT USER CHANGABLE #define preference_updater_version (char*)"updVer" @@ -284,7 +285,7 @@ class DebugPreferences preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count, - preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, + preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, preference_keypad_check_code_enabled }; std::vector _redact = { @@ -300,7 +301,7 @@ class DebugPreferences preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid_enabled, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, - preference_ntw_reconfigure + preference_ntw_reconfigure, preference_keypad_check_code_enabled }; std::vector _bytePrefs = { diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 208124e8..4d3e975e 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -1561,6 +1561,16 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) //configChanged = true; } } + else if(key == "KPCHECK") + { + if(_preferences->getBool(preference_keypad_check_code_enabled, false) != (value == "1")) + { + _preferences->putBool(preference_keypad_check_code_enabled, (value == "1")); + Log->print(F("Setting changed: ")); + Log->println(key); + //configChanged = true; + } + } else if(key == "KPENA") { if(_preferences->getBool(preference_keypad_control_enabled, false) != (value == "1")) @@ -2639,15 +2649,15 @@ void WebCfgServer::buildCredHtml(AsyncWebServerRequest *request) { _response = ""; buildHtmlHeader(); - _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("CREDPASS", "Password", "*", 30, "id=\"inputpass\"", true, true); printInputField("CREDPASSRE", "Retype password", "*", 30, "", true); _response.concat("
"); _response.concat("
"); - _response.concat("
"); + _response.concat(""); if(_nuki != nullptr) { _response.concat("

"); @@ -2919,6 +2929,7 @@ void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request) 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("KPCHECK", "Allow checking if keypad codes are valid (Disadvised for security reasons)", _preferences->getBool(preference_keypad_check_code_enabled, false), ""); printCheckBox("KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), ""); } printCheckBox("TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), ""); @@ -3386,6 +3397,8 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request) _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("\nAllow checking Keypad codes: "); + _response.concat(_preferences->getBool(preference_keypad_check_code_enabled, 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: "); From 0b9b3da38036e990f32d54d9e62969b014329683 Mon Sep 17 00:00:00 2001 From: iranl Date: Tue, 15 Oct 2024 21:08:45 +0200 Subject: [PATCH 2/3] Check keypad codes --- src/Config.h | 2 +- src/NukiOpenerWrapper.cpp | 615 +++++++++++++++++++++----------------- src/NukiOpenerWrapper.h | 4 + src/NukiWrapper.cpp | 615 +++++++++++++++++++++----------------- src/NukiWrapper.h | 4 + src/PreferencesKeys.h | 5 +- src/WebCfgServer.cpp | 21 +- 7 files changed, 700 insertions(+), 566 deletions(-) diff --git a/src/Config.h b/src/Config.h index 630b1a1a..c90f7726 100644 --- a/src/Config.h +++ b/src/Config.h @@ -4,7 +4,7 @@ #define NUKI_HUB_VERSION "9.02" #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2024-10-13" +#define NUKI_HUB_DATE "2024-10-15" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index 3ae17a7d..14c5a34f 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -89,6 +89,7 @@ void NukiOpenerWrapper::readSettings() _retryDelay = _preferences->getInt(preference_command_retry_delay); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); + _checkKeypadCodes = _preferences->getBool(preference_keypad_check_code_enabled, false); _preferences->getBytes(preference_conf_opener_basic_acl, &_basicOpenerConfigAclPrefs, sizeof(_basicOpenerConfigAclPrefs)); _preferences->getBytes(preference_conf_opener_advanced_acl, &_advancedOpenerConfigAclPrefs, sizeof(_advancedOpenerConfigAclPrefs)); @@ -253,6 +254,11 @@ void NukiOpenerWrapper::update() _nextKeypadUpdateTs = ts + _intervalKeypad * 1000; updateKeypad(false); } + + if(_checkKeypadCodes && _invalidCount > 0 && ts - 120000 < _lastCodeCheck) + { + _invalidCount--; + } if(_nextLockAction != (NukiOpener::LockAction)0xff) { @@ -714,10 +720,13 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved) _network->publishKeypad(entries, _maxKeypadCodeCount); _keypadCodeIds.clear(); + _keypadCodes.clear(); _keypadCodeIds.reserve(entries.size()); + _keypadCodes.reserve(entries.size()); for(const auto& entry : entries) { _keypadCodeIds.push_back(entry.codeId); + _keypadCodes.push_back(entry.code); } } @@ -1797,372 +1806,420 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value) idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end(); } - Nuki::CmdResult result = (Nuki::CmdResult)-1; - int retryCount = 0; + if(strcmp(action, "check") == 0) { + if(!_preferences->getBool(preference_keypad_check_code_enabled, false)) + { + _network->publishKeypadJsonCommandResult("checkingKeypadCodesDisabled"); + return; + } - while(retryCount < _nrOfRetries + 1) - { - if(strcmp(action, "delete") == 0) { - if(idExists) + if(pow(_invalidCount, 5) + _lastCodeCheck > (esp_timer_get_time() / 1000)) + { + _network->publishKeypadJsonCommandResult("checkingCodesBlockedTooManyInvalid"); + _lastCodeCheck = (esp_timer_get_time() / 1000); + return; + } + + _lastCodeCheck = (esp_timer_get_time() / 1000); + + if(idExists) + { + auto it1 = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId); + int index = it1 - _keypadCodeIds.begin(); + Log->print(F("Check keypad code: ")); + + if(code == _keypadCodes[index]) { - result = _nukiOpener.deleteKeypadEntry(codeId); - Log->print(F("Delete keypad code: ")); - Log->println((int)result); + _invalidCount = 0; + _network->publishKeypadJsonCommandResult("codeValid"); + Log->println("Valid"); + return; } else { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + _invalidCount++; + _network->publishKeypadJsonCommandResult("codeInvalid"); + Log->println("Invalid"); return; } } - else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) + else { - if(name.length() < 1) - { - if (strcmp(action, "update") != 0) + _invalidCount++; + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } + } + else + { + + Nuki::CmdResult result = (Nuki::CmdResult)-1; + int retryCount = 0; + + while(retryCount < _nrOfRetries + 1) + { + if(strcmp(action, "delete") == 0) { + if(idExists) { - _network->publishKeypadJsonCommandResult("noNameSet"); + result = _nukiOpener.deleteKeypadEntry(codeId); + Log->print(F("Delete keypad code: ")); + Log->println((int)result); + } + else + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); return; } } - - if(code != 12) + else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) { - String codeStr = json["code"].as(); - bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + if(name.length() < 1) + { + if (strcmp(action, "update") != 0) + { + _network->publishKeypadJsonCommandResult("noNameSet"); + return; + } + } - if (!codeValid) + if(code != 12) + { + String codeStr = json["code"].as(); + bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + + if (!codeValid) + { + _network->publishKeypadJsonCommandResult("noValidCodeSet"); + return; + } + } + else if (strcmp(action, "update") != 0) { - _network->publishKeypadJsonCommandResult("noValidCodeSet"); + _network->publishKeypadJsonCommandResult("noCodeSet"); return; } - } - else if (strcmp(action, "update") != 0) - { - _network->publishKeypadJsonCommandResult("noCodeSet"); - return; - } - unsigned int allowedFromAr[6]; - unsigned int allowedUntilAr[6]; - unsigned int allowedFromTimeAr[2]; - unsigned int allowedUntilTimeAr[2]; - uint8_t allowedWeekdaysInt = 0; + unsigned int allowedFromAr[6]; + unsigned int allowedUntilAr[6]; + unsigned int allowedFromTimeAr[2]; + unsigned int allowedUntilTimeAr[2]; + uint8_t allowedWeekdaysInt = 0; - if(timeLimited == 1) - { - if(allowedFrom.length() > 0) + if(timeLimited == 1) { - if(allowedFrom.length() == 19) + if(allowedFrom.length() > 0) { - allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); - allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); - allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); - allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); - allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); - allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); - - if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + if(allowedFrom.length() == 19) + { + allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); + allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); + allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); + allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); + allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); + allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); + + if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); - return; - } - } - if(allowedUntil.length() > 0) - { - if(allowedUntil.length() > 0 == 19) + if(allowedUntil.length() > 0) { - allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); - allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); - allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); - allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); - allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); - allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); - - if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + if(allowedUntil.length() > 0 == 19) + { + allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); + allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); + allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); + allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); + allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); + allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); + + if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); - return; - } - } - if(allowedFromTime.length() > 0) - { - if(allowedFromTime.length() == 5) + if(allowedFromTime.length() > 0) { - allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); - allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); - - if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + if(allowedFromTime.length() == 5) + { + allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); + allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); + + if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); - return; - } - } - if(allowedUntilTime.length() > 0) - { - if(allowedUntilTime.length() == 5) + if(allowedUntilTime.length() > 0) { - allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); - allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); - - if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + if(allowedUntilTime.length() == 5) + { + allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); + allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); + + if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); - return; - } - } - - if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; - if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; - if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; - if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; - if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; - if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; - if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; - } - if(strcmp(action, "add") == 0) - { - NukiOpener::NewKeypadEntry entry; - memset(&entry, 0, sizeof(entry)); - size_t nameLen = name.length(); - memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); - entry.code = code; - entry.timeLimited = timeLimited == 1 ? 1 : 0; - - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; + if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; + if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; + if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; + if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; + if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; + if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; + if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; } - if(allowedUntil.length() > 0) + if(strcmp(action, "add") == 0) { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } + NukiOpener::NewKeypadEntry entry; + memset(&entry, 0, sizeof(entry)); + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + entry.code = code; + entry.timeLimited = timeLimited == 1 ? 1 : 0; - entry.allowedWeekdays = allowedWeekdaysInt; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } - if(allowedUntilTime.length() > 0) - { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; - } + entry.allowedWeekdays = allowedWeekdaysInt; - result = _nukiOpener.addKeypadEntry(entry); - Log->print(F("Add keypad code: ")); - Log->println((int)result); - } - else if (strcmp(action, "update") == 0) - { - if(!codeId) - { - _network->publishKeypadJsonCommandResult("noCodeIdSet"); - return; - } + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } - if(!idExists) - { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); - return; + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } + + result = _nukiOpener.addKeypadEntry(entry); + Log->print(F("Add keypad code: ")); + Log->println((int)result); } + else if (strcmp(action, "update") == 0) + { + if(!codeId) + { + _network->publishKeypadJsonCommandResult("noCodeIdSet"); + return; + } - Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); - bool foundExisting = false; + if(!idExists) + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } - if(resultKp == Nuki::CmdResult::Success) - { - delay(250); - std::list entries; - _nukiOpener.getKeypadEntries(&entries); + Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); + bool foundExisting = false; - for(const auto& entry : entries) + if(resultKp == Nuki::CmdResult::Success) { - if (codeId != entry.codeId) continue; - else foundExisting = true; + delay(250); + std::list entries; + _nukiOpener.getKeypadEntries(&entries); - if(name.length() < 1) - { - memset(oldName, 0, sizeof(oldName)); - memcpy(oldName, entry.name, sizeof(entry.name)); - } - if(code == 12) code = entry.code; - if(enabled == 2) enabled = entry.enabled; - if(timeLimited == 2) timeLimited = entry.timeLimited; - if(allowedFrom.length() < 1) + for(const auto& entry : entries) { - allowedFrom = "old"; - allowedFromAr[0] = entry.allowedFromYear; - allowedFromAr[1] = entry.allowedFromMonth; - allowedFromAr[2] = entry.allowedFromDay; - allowedFromAr[3] = entry.allowedFromHour; - allowedFromAr[4] = entry.allowedFromMin; - allowedFromAr[5] = entry.allowedFromSec; - } - if(allowedUntil.length() < 1) - { - allowedUntil = "old"; - allowedUntilAr[0] = entry.allowedUntilYear; - allowedUntilAr[1] = entry.allowedUntilMonth; - allowedUntilAr[2] = entry.allowedUntilDay; - allowedUntilAr[3] = entry.allowedUntilHour; - allowedUntilAr[4] = entry.allowedUntilMin; - allowedUntilAr[5] = entry.allowedUntilSec; - } - if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; - if(allowedFromTime.length() < 1) - { - allowedFromTime = "old"; - allowedFromTimeAr[0] = entry.allowedFromTimeHour; - allowedFromTimeAr[1] = entry.allowedFromTimeMin; + if (codeId != entry.codeId) continue; + else foundExisting = true; + + if(name.length() < 1) + { + memset(oldName, 0, sizeof(oldName)); + memcpy(oldName, entry.name, sizeof(entry.name)); + } + if(code == 12) code = entry.code; + if(enabled == 2) enabled = entry.enabled; + if(timeLimited == 2) timeLimited = entry.timeLimited; + if(allowedFrom.length() < 1) + { + allowedFrom = "old"; + allowedFromAr[0] = entry.allowedFromYear; + allowedFromAr[1] = entry.allowedFromMonth; + allowedFromAr[2] = entry.allowedFromDay; + allowedFromAr[3] = entry.allowedFromHour; + allowedFromAr[4] = entry.allowedFromMin; + allowedFromAr[5] = entry.allowedFromSec; + } + if(allowedUntil.length() < 1) + { + allowedUntil = "old"; + allowedUntilAr[0] = entry.allowedUntilYear; + allowedUntilAr[1] = entry.allowedUntilMonth; + allowedUntilAr[2] = entry.allowedUntilDay; + allowedUntilAr[3] = entry.allowedUntilHour; + allowedUntilAr[4] = entry.allowedUntilMin; + allowedUntilAr[5] = entry.allowedUntilSec; + } + if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; + if(allowedFromTime.length() < 1) + { + allowedFromTime = "old"; + allowedFromTimeAr[0] = entry.allowedFromTimeHour; + allowedFromTimeAr[1] = entry.allowedFromTimeMin; + } + + if(allowedUntilTime.length() < 1) + { + allowedUntilTime = "old"; + allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; + allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + } } - if(allowedUntilTime.length() < 1) + if(!foundExisting) { - allowedUntilTime = "old"; - allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; - allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); + return; } } - - if(!foundExisting) + else { _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); return; } - } - else - { - _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); - return; - } - NukiOpener::UpdatedKeypadEntry entry; - - memset(&entry, 0, sizeof(entry)); - entry.codeId = codeId; - entry.code = code; + NukiOpener::UpdatedKeypadEntry entry; - if(name.length() < 1) - { - size_t nameLen = strlen(oldName); - memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); - } - else - { - size_t nameLen = name.length(); - memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); - } - entry.enabled = enabled; - entry.timeLimited = timeLimited; + memset(&entry, 0, sizeof(entry)); + entry.codeId = codeId; + entry.code = code; - if(enabled == 1) - { - if(timeLimited == 1) + if(name.length() < 1) { - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; - } - - if(allowedUntil.length() > 0) - { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } - - entry.allowedWeekdays = allowedWeekdaysInt; - - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + size_t nameLen = strlen(oldName); + memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); + } + else + { + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + } + entry.enabled = enabled; + entry.timeLimited = timeLimited; - if(allowedUntilTime.length() > 0) + if(enabled == 1) + { + if(timeLimited == 1) { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } + + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } + + entry.allowedWeekdays = allowedWeekdaysInt; + + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } + + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } } } - } - result = _nukiOpener.updateKeypadEntry(entry); - Log->print(F("Update keypad code: ")); - Log->println((int)result); + result = _nukiOpener.updateKeypadEntry(entry); + Log->print(F("Update keypad code: ")); + Log->println((int)result); + } + } + else + { + _network->publishKeypadJsonCommandResult("invalidAction"); + return; } - } - else - { - _network->publishKeypadJsonCommandResult("invalidAction"); - return; - } - if(result != Nuki::CmdResult::Success) { - ++retryCount; + if(result != Nuki::CmdResult::Success) { + ++retryCount; + } + else break; } - else break; - } - updateKeypad(false); + updateKeypad(false); - if((int)result != -1) - { - char resultStr[15]; - memset(&resultStr, 0, sizeof(resultStr)); - NukiOpener::cmdResultToString(result, resultStr); - _network->publishKeypadJsonCommandResult(resultStr); + if((int)result != -1) + { + char resultStr[15]; + memset(&resultStr, 0, sizeof(resultStr)); + NukiOpener::cmdResultToString(result, resultStr); + _network->publishKeypadJsonCommandResult(resultStr); + } } } else diff --git a/src/NukiOpenerWrapper.h b/src/NukiOpenerWrapper.h index 1d5eb287..6853711d 100644 --- a/src/NukiOpenerWrapper.h +++ b/src/NukiOpenerWrapper.h @@ -106,12 +106,16 @@ class NukiOpenerWrapper : public NukiOpener::SmartlockEventHandler bool _publishAuthData = false; bool _clearAuthData = false; bool _disableNonJSON = false; + bool _checkKeypadCodes = false; int _nrOfRetries = 0; int _retryDelay = 0; int _retryConfigCount = 0; int _retryLockstateCount = 0; int64_t _nextRetryTs = 0; + int64_t _invalidCount = 0; + int64_t _lastCodeCheck = 0; std::vector _keypadCodeIds; + std::vector _keypadCodes; std::vector _timeControlIds; std::vector _authIds; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 7cc1092f..da04bfba 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -137,7 +137,7 @@ void NukiWrapper::readSettings() _retryDelay = _preferences->getInt(preference_command_retry_delay); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); - + _checkKeypadCodes = _preferences->getBool(preference_keypad_check_code_enabled, false); _preferences->getBytes(preference_conf_lock_basic_acl, &_basicLockConfigaclPrefs, sizeof(_basicLockConfigaclPrefs)); _preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs)); @@ -369,6 +369,10 @@ void NukiWrapper::update() _nextKeypadUpdateTs = ts + _intervalKeypad * 1000; updateKeypad(false); } + if(_checkKeypadCodes && _invalidCount > 0 && ts - 120000 < _lastCodeCheck) + { + _invalidCount--; + } } if(_clearAuthData) { @@ -773,10 +777,13 @@ void NukiWrapper::updateKeypad(bool retrieved) _network->publishKeypad(entries, _maxKeypadCodeCount); _keypadCodeIds.clear(); + _keypadCodes.clear(); _keypadCodeIds.reserve(entries.size()); + _keypadCodes.reserve(entries.size()); for(const auto& entry : entries) { _keypadCodeIds.push_back(entry.codeId); + _keypadCodes.push_back(entry.code); } } @@ -1918,373 +1925,421 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value) { idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end(); } + + if(strcmp(action, "check") == 0) { + if(!_preferences->getBool(preference_keypad_check_code_enabled, false)) + { + _network->publishKeypadJsonCommandResult("checkingKeypadCodesDisabled"); + return; + } - Nuki::CmdResult result = (Nuki::CmdResult)-1; - int retryCount = 0; + if(pow(_invalidCount, 5) + _lastCodeCheck > (esp_timer_get_time() / 1000)) + { + _network->publishKeypadJsonCommandResult("checkingCodesBlockedTooManyInvalid"); + _lastCodeCheck = (esp_timer_get_time() / 1000); + return; + } - while(retryCount < _nrOfRetries + 1) - { - if(strcmp(action, "delete") == 0) { - if(idExists) + _lastCodeCheck = (esp_timer_get_time() / 1000); + + if(idExists) + { + auto it1 = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId); + int index = it1 - _keypadCodeIds.begin(); + Log->print(F("Check keypad code: ")); + + if(code == _keypadCodes[index]) { - result = _nukiLock.deleteKeypadEntry(codeId); - Log->print(F("Delete keypad code: ")); - Log->println((int)result); + _invalidCount = 0; + _network->publishKeypadJsonCommandResult("codeValid"); + Log->println("Valid"); + return; } else { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + _invalidCount++; + _network->publishKeypadJsonCommandResult("codeInvalid"); + Log->println("Invalid"); return; } } - else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) + else { - if(name.length() < 1) - { - if (strcmp(action, "update") != 0) + _invalidCount++; + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } + } + else + { + + Nuki::CmdResult result = (Nuki::CmdResult)-1; + int retryCount = 0; + + while(retryCount < _nrOfRetries + 1) + { + if(strcmp(action, "delete") == 0) { + if(idExists) { - _network->publishKeypadJsonCommandResult("noNameSet"); + result = _nukiLock.deleteKeypadEntry(codeId); + Log->print(F("Delete keypad code: ")); + Log->println((int)result); + } + else + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); return; } } - - if(code != 12) + else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) { - String codeStr = json["code"].as(); - bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + if(name.length() < 1) + { + if (strcmp(action, "update") != 0) + { + _network->publishKeypadJsonCommandResult("noNameSet"); + return; + } + } - if (!codeValid) + if(code != 12) + { + String codeStr = json["code"].as(); + bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1); + + if (!codeValid) + { + _network->publishKeypadJsonCommandResult("noValidCodeSet"); + return; + } + } + else if (strcmp(action, "update") != 0) { - _network->publishKeypadJsonCommandResult("noValidCodeSet"); + _network->publishKeypadJsonCommandResult("noCodeSet"); return; } - } - else if (strcmp(action, "update") != 0) - { - _network->publishKeypadJsonCommandResult("noCodeSet"); - return; - } - unsigned int allowedFromAr[6]; - unsigned int allowedUntilAr[6]; - unsigned int allowedFromTimeAr[2]; - unsigned int allowedUntilTimeAr[2]; - uint8_t allowedWeekdaysInt = 0; + unsigned int allowedFromAr[6]; + unsigned int allowedUntilAr[6]; + unsigned int allowedFromTimeAr[2]; + unsigned int allowedUntilTimeAr[2]; + uint8_t allowedWeekdaysInt = 0; - if(timeLimited == 1) - { - if(allowedFrom.length() > 0) + if(timeLimited == 1) { - if(allowedFrom.length() == 19) + if(allowedFrom.length() > 0) { - allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); - allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); - allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); - allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); - allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); - allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); - - if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + if(allowedFrom.length() == 19) + { + allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); + allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); + allowedFromAr[2] = (uint8_t)allowedFrom.substring(8, 10).toInt(); + allowedFromAr[3] = (uint8_t)allowedFrom.substring(11, 13).toInt(); + allowedFromAr[4] = (uint8_t)allowedFrom.substring(14, 16).toInt(); + allowedFromAr[5] = (uint8_t)allowedFrom.substring(17, 19).toInt(); + + if(allowedFromAr[0] < 2000 || allowedFromAr[0] > 3000 || allowedFromAr[1] < 1 || allowedFromAr[1] > 12 || allowedFromAr[2] < 1 || allowedFromAr[2] > 31 || allowedFromAr[3] < 0 || allowedFromAr[3] > 23 || allowedFromAr[4] < 0 || allowedFromAr[4] > 59 || allowedFromAr[5] < 0 || allowedFromAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFrom"); - return; - } - } - if(allowedUntil.length() > 0) - { - if(allowedUntil.length() > 0 == 19) + if(allowedUntil.length() > 0) { - allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); - allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); - allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); - allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); - allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); - allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); - - if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + if(allowedUntil.length() > 0 == 19) + { + allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); + allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); + allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); + allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); + allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); + allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).toInt(); + + if(allowedUntilAr[0] < 2000 || allowedUntilAr[0] > 3000 || allowedUntilAr[1] < 1 || allowedUntilAr[1] > 12 || allowedUntilAr[2] < 1 || allowedUntilAr[2] > 31 || allowedUntilAr[3] < 0 || allowedUntilAr[3] > 23 || allowedUntilAr[4] < 0 || allowedUntilAr[4] > 59 || allowedUntilAr[5] < 0 || allowedUntilAr[5] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntil"); - return; - } - } - if(allowedFromTime.length() > 0) - { - if(allowedFromTime.length() == 5) + if(allowedFromTime.length() > 0) { - allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); - allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); - - if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + if(allowedFromTime.length() == 5) + { + allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); + allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); + + if(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); - return; - } - } - if(allowedUntilTime.length() > 0) - { - if(allowedUntilTime.length() == 5) + if(allowedUntilTime.length() > 0) { - allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); - allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); - - if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + if(allowedUntilTime.length() == 5) + { + allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); + allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); + + if(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59) + { + _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); + return; + } + } + else { _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); return; } } - else - { - _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); - return; - } - } - - if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; - if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; - if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; - if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; - if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; - if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; - if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; - } - - if(strcmp(action, "add") == 0) - { - 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 = code; - entry.timeLimited = timeLimited == 1 ? 1 : 0; - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; + if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; + if(allowedWeekdays.indexOf("tue") >= 0) allowedWeekdaysInt += 32; + if(allowedWeekdays.indexOf("wed") >= 0) allowedWeekdaysInt += 16; + if(allowedWeekdays.indexOf("thu") >= 0) allowedWeekdaysInt += 8; + if(allowedWeekdays.indexOf("fri") >= 0) allowedWeekdaysInt += 4; + if(allowedWeekdays.indexOf("sat") >= 0) allowedWeekdaysInt += 2; + if(allowedWeekdays.indexOf("sun") >= 0) allowedWeekdaysInt += 1; } - if(allowedUntil.length() > 0) + if(strcmp(action, "add") == 0) { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } + 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 = code; + entry.timeLimited = timeLimited == 1 ? 1 : 0; - entry.allowedWeekdays = allowedWeekdaysInt; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } - if(allowedUntilTime.length() > 0) - { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; - } + entry.allowedWeekdays = allowedWeekdaysInt; - result = _nukiLock.addKeypadEntry(entry); - Log->print(F("Add keypad code: ")); - Log->println((int)result); - } - else if (strcmp(action, "update") == 0) - { - if(!codeId) - { - _network->publishKeypadJsonCommandResult("noCodeIdSet"); - return; - } + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } - if(!idExists) - { - _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); - return; + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } + + result = _nukiLock.addKeypadEntry(entry); + Log->print(F("Add keypad code: ")); + Log->println((int)result); } + else if (strcmp(action, "update") == 0) + { + if(!codeId) + { + _network->publishKeypadJsonCommandResult("noCodeIdSet"); + return; + } - Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); - bool foundExisting = false; + if(!idExists) + { + _network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); + return; + } - if(resultKp == Nuki::CmdResult::Success) - { - delay(250); - std::list entries; - _nukiLock.getKeypadEntries(&entries); + Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); + bool foundExisting = false; - for(const auto& entry : entries) + if(resultKp == Nuki::CmdResult::Success) { - if (codeId != entry.codeId) continue; - else foundExisting = true; + delay(250); + std::list entries; + _nukiLock.getKeypadEntries(&entries); - if(name.length() < 1) - { - memset(oldName, 0, sizeof(oldName)); - memcpy(oldName, entry.name, sizeof(entry.name)); - } - if(code == 12) code = entry.code; - if(enabled == 2) enabled = entry.enabled; - if(timeLimited == 2) timeLimited = entry.timeLimited; - if(allowedFrom.length() < 1) + for(const auto& entry : entries) { - allowedFrom = "old"; - allowedFromAr[0] = entry.allowedFromYear; - allowedFromAr[1] = entry.allowedFromMonth; - allowedFromAr[2] = entry.allowedFromDay; - allowedFromAr[3] = entry.allowedFromHour; - allowedFromAr[4] = entry.allowedFromMin; - allowedFromAr[5] = entry.allowedFromSec; - } - if(allowedUntil.length() < 1) - { - allowedUntil = "old"; - allowedUntilAr[0] = entry.allowedUntilYear; - allowedUntilAr[1] = entry.allowedUntilMonth; - allowedUntilAr[2] = entry.allowedUntilDay; - allowedUntilAr[3] = entry.allowedUntilHour; - allowedUntilAr[4] = entry.allowedUntilMin; - allowedUntilAr[5] = entry.allowedUntilSec; - } - if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; - if(allowedFromTime.length() < 1) - { - allowedFromTime = "old"; - allowedFromTimeAr[0] = entry.allowedFromTimeHour; - allowedFromTimeAr[1] = entry.allowedFromTimeMin; + if (codeId != entry.codeId) continue; + else foundExisting = true; + + if(name.length() < 1) + { + memset(oldName, 0, sizeof(oldName)); + memcpy(oldName, entry.name, sizeof(entry.name)); + } + if(code == 12) code = entry.code; + if(enabled == 2) enabled = entry.enabled; + if(timeLimited == 2) timeLimited = entry.timeLimited; + if(allowedFrom.length() < 1) + { + allowedFrom = "old"; + allowedFromAr[0] = entry.allowedFromYear; + allowedFromAr[1] = entry.allowedFromMonth; + allowedFromAr[2] = entry.allowedFromDay; + allowedFromAr[3] = entry.allowedFromHour; + allowedFromAr[4] = entry.allowedFromMin; + allowedFromAr[5] = entry.allowedFromSec; + } + if(allowedUntil.length() < 1) + { + allowedUntil = "old"; + allowedUntilAr[0] = entry.allowedUntilYear; + allowedUntilAr[1] = entry.allowedUntilMonth; + allowedUntilAr[2] = entry.allowedUntilDay; + allowedUntilAr[3] = entry.allowedUntilHour; + allowedUntilAr[4] = entry.allowedUntilMin; + allowedUntilAr[5] = entry.allowedUntilSec; + } + if(allowedWeekdays.length() < 1) allowedWeekdaysInt = entry.allowedWeekdays; + if(allowedFromTime.length() < 1) + { + allowedFromTime = "old"; + allowedFromTimeAr[0] = entry.allowedFromTimeHour; + allowedFromTimeAr[1] = entry.allowedFromTimeMin; + } + + if(allowedUntilTime.length() < 1) + { + allowedUntilTime = "old"; + allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; + allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + } } - if(allowedUntilTime.length() < 1) + if(!foundExisting) { - allowedUntilTime = "old"; - allowedUntilTimeAr[0] = entry.allowedUntilTimeHour; - allowedUntilTimeAr[1] = entry.allowedUntilTimeMin; + _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); + return; } } - - if(!foundExisting) + else { _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); return; } - } - else - { - _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); - return; - } - - NukiLock::UpdatedKeypadEntry entry; - memset(&entry, 0, sizeof(entry)); - entry.codeId = codeId; - entry.code = code; + NukiLock::UpdatedKeypadEntry entry; - if(name.length() < 1) - { - size_t nameLen = strlen(oldName); - memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); - } - else - { - size_t nameLen = name.length(); - memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); - } - entry.enabled = enabled; - entry.timeLimited = timeLimited; + memset(&entry, 0, sizeof(entry)); + entry.codeId = codeId; + entry.code = code; - if(enabled == 1) - { - if(timeLimited == 1) + if(name.length() < 1) { - if(allowedFrom.length() > 0) - { - entry.allowedFromYear = allowedFromAr[0]; - entry.allowedFromMonth = allowedFromAr[1]; - entry.allowedFromDay = allowedFromAr[2]; - entry.allowedFromHour = allowedFromAr[3]; - entry.allowedFromMin = allowedFromAr[4]; - entry.allowedFromSec = allowedFromAr[5]; - } - - if(allowedUntil.length() > 0) - { - entry.allowedUntilYear = allowedUntilAr[0]; - entry.allowedUntilMonth = allowedUntilAr[1]; - entry.allowedUntilDay = allowedUntilAr[2]; - entry.allowedUntilHour = allowedUntilAr[3]; - entry.allowedUntilMin = allowedUntilAr[4]; - entry.allowedUntilSec = allowedUntilAr[5]; - } - - entry.allowedWeekdays = allowedWeekdaysInt; - - if(allowedFromTime.length() > 0) - { - entry.allowedFromTimeHour = allowedFromTimeAr[0]; - entry.allowedFromTimeMin = allowedFromTimeAr[1]; - } + size_t nameLen = strlen(oldName); + memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen); + } + else + { + size_t nameLen = name.length(); + memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen); + } + entry.enabled = enabled; + entry.timeLimited = timeLimited; - if(allowedUntilTime.length() > 0) + if(enabled == 1) + { + if(timeLimited == 1) { - entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; - entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + if(allowedFrom.length() > 0) + { + entry.allowedFromYear = allowedFromAr[0]; + entry.allowedFromMonth = allowedFromAr[1]; + entry.allowedFromDay = allowedFromAr[2]; + entry.allowedFromHour = allowedFromAr[3]; + entry.allowedFromMin = allowedFromAr[4]; + entry.allowedFromSec = allowedFromAr[5]; + } + + if(allowedUntil.length() > 0) + { + entry.allowedUntilYear = allowedUntilAr[0]; + entry.allowedUntilMonth = allowedUntilAr[1]; + entry.allowedUntilDay = allowedUntilAr[2]; + entry.allowedUntilHour = allowedUntilAr[3]; + entry.allowedUntilMin = allowedUntilAr[4]; + entry.allowedUntilSec = allowedUntilAr[5]; + } + + entry.allowedWeekdays = allowedWeekdaysInt; + + if(allowedFromTime.length() > 0) + { + entry.allowedFromTimeHour = allowedFromTimeAr[0]; + entry.allowedFromTimeMin = allowedFromTimeAr[1]; + } + + if(allowedUntilTime.length() > 0) + { + entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; + entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; + } } } - } - result = _nukiLock.updateKeypadEntry(entry); - Log->print(F("Update keypad code: ")); - Log->println((int)result); + result = _nukiLock.updateKeypadEntry(entry); + Log->print(F("Update keypad code: ")); + Log->println((int)result); + } + } + else + { + _network->publishKeypadJsonCommandResult("invalidAction"); + return; } - } - else - { - _network->publishKeypadJsonCommandResult("invalidAction"); - return; - } - if(result != Nuki::CmdResult::Success) { - ++retryCount; + if(result != Nuki::CmdResult::Success) { + ++retryCount; + } + else break; } - else break; - } - updateKeypad(false); + updateKeypad(false); - if((int)result != -1) - { - char resultStr[15]; - memset(&resultStr, 0, sizeof(resultStr)); - NukiLock::cmdResultToString(result, resultStr); - _network->publishKeypadJsonCommandResult(resultStr); + if((int)result != -1) + { + char resultStr[15]; + memset(&resultStr, 0, sizeof(resultStr)); + NukiLock::cmdResultToString(result, resultStr); + _network->publishKeypadJsonCommandResult(resultStr); + } } } else diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index ee695f98..0cf886bf 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -107,7 +107,11 @@ class NukiWrapper : public Nuki::SmartlockEventHandler int _restartBeaconTimeout = 0; // seconds bool _publishAuthData = false; bool _clearAuthData = false; + bool _checkKeypadCodes = false; + int64_t _invalidCount = 0; + int64_t _lastCodeCheck = 0; std::vector _keypadCodeIds; + std::vector _keypadCodes; std::vector _timeControlIds; std::vector _authIds; diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index fc0cb169..7edc9970 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -98,6 +98,7 @@ #define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis" #define preference_official_hybrid_actions (char*)"hybridAct" #define preference_official_hybrid_retry (char*)"hybridRtry" +#define preference_keypad_check_code_enabled (char*)"kpChkEna" //NOT USER CHANGABLE #define preference_updater_version (char*)"updVer" @@ -284,7 +285,7 @@ class DebugPreferences preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count, - preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, + preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, preference_keypad_check_code_enabled }; std::vector _redact = { @@ -300,7 +301,7 @@ class DebugPreferences preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid_enabled, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt, preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_recon_netw_on_mqtt_discon, preference_webserial_enabled, - preference_ntw_reconfigure + preference_ntw_reconfigure, preference_keypad_check_code_enabled }; std::vector _bytePrefs = { diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 208124e8..ea5d492c 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -1561,6 +1561,16 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message) //configChanged = true; } } + else if(key == "KPCHECK") + { + if(_preferences->getBool(preference_keypad_check_code_enabled, false) != (value == "1")) + { + _preferences->putBool(preference_keypad_check_code_enabled, (value == "1")); + Log->print(F("Setting changed: ")); + Log->println(key); + //configChanged = true; + } + } else if(key == "KPENA") { if(_preferences->getBool(preference_keypad_control_enabled, false) != (value == "1")) @@ -2639,15 +2649,15 @@ void WebCfgServer::buildCredHtml(AsyncWebServerRequest *request) { _response = ""; buildHtmlHeader(); - _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("CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "id=\"inputuser\"", false, true); + printInputField("CREDPASS", "Password", "*", 30, "id=\"inputpass\"", true, true); printInputField("CREDPASSRE", "Retype password", "*", 30, "", true); _response.concat("
"); _response.concat("
"); - _response.concat(""); + _response.concat(""); if(_nuki != nullptr) { _response.concat("

"); @@ -2919,6 +2929,7 @@ void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request) 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("KPCHECK", "Allow checking if keypad codes are valid (Disadvised for security reasons)", _preferences->getBool(preference_keypad_check_code_enabled, false), ""); printCheckBox("KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), ""); } printCheckBox("TCPUB", "Publish time control entries information", _preferences->getBool(preference_timecontrol_info_enabled), ""); @@ -3386,6 +3397,8 @@ void WebCfgServer::buildInfoHtml(AsyncWebServerRequest *request) _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("\nAllow checking Keypad codes: "); + _response.concat(_preferences->getBool(preference_keypad_check_code_enabled, 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: "); From 7d34e43c335cdf364c4b98facbe74f6d4cffe05e Mon Sep 17 00:00:00 2001 From: iranl Date: Tue, 15 Oct 2024 22:33:43 +0200 Subject: [PATCH 3/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23449a8c..e95a3011 100644 --- a/README.md +++ b/README.md @@ -565,7 +565,7 @@ Examples: ### Result of attempted keypad code changes -The result of the last configuration change action will be published to the `configuration/commandResultJson` MQTT topic.
+The result of the last keypad change action will be published to the `keypad/commandResultJson` MQTT topic.
Possible values are "noValidPinSet", "keypadControlDisabled", "keypadNotAvailable", "keypadDisabled", "invalidConfig", "invalidJson", "noActionSet", "invalidAction", "noExistingCodeIdSet", "noNameSet", "noValidCodeSet", "noCodeSet", "invalidAllowedFrom", "invalidAllowedUntil", "invalidAllowedFromTime", "invalidAllowedUntilTime", "success", "failed", "timeOut", "working", "notPaired", "error" and "undefined".
## Keypad control (alternative, optional)