Check keypad codes

This commit is contained in:
iranl
2024-10-15 21:08:45 +02:00
parent 041ea1a154
commit 0b9b3da380
7 changed files with 728 additions and 594 deletions

View File

@@ -4,7 +4,7 @@
#define NUKI_HUB_VERSION "9.02" #define NUKI_HUB_VERSION "9.02"
#define NUKI_HUB_BUILD "unknownbuildnr" #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_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" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json"

View File

@@ -89,6 +89,7 @@ void NukiOpenerWrapper::readSettings()
_retryDelay = _preferences->getInt(preference_command_retry_delay); _retryDelay = _preferences->getInt(preference_command_retry_delay);
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
_disableNonJSON = _preferences->getBool(preference_disable_non_json, false); _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_basic_acl, &_basicOpenerConfigAclPrefs, sizeof(_basicOpenerConfigAclPrefs));
_preferences->getBytes(preference_conf_opener_advanced_acl, &_advancedOpenerConfigAclPrefs, sizeof(_advancedOpenerConfigAclPrefs)); _preferences->getBytes(preference_conf_opener_advanced_acl, &_advancedOpenerConfigAclPrefs, sizeof(_advancedOpenerConfigAclPrefs));
@@ -253,6 +254,11 @@ void NukiOpenerWrapper::update()
_nextKeypadUpdateTs = ts + _intervalKeypad * 1000; _nextKeypadUpdateTs = ts + _intervalKeypad * 1000;
updateKeypad(false); updateKeypad(false);
} }
if(_checkKeypadCodes && _invalidCount > 0 && ts - 120000 < _lastCodeCheck)
{
_invalidCount--;
}
if(_nextLockAction != (NukiOpener::LockAction)0xff) if(_nextLockAction != (NukiOpener::LockAction)0xff)
{ {
@@ -714,10 +720,13 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved)
_network->publishKeypad(entries, _maxKeypadCodeCount); _network->publishKeypad(entries, _maxKeypadCodeCount);
_keypadCodeIds.clear(); _keypadCodeIds.clear();
_keypadCodes.clear();
_keypadCodeIds.reserve(entries.size()); _keypadCodeIds.reserve(entries.size());
_keypadCodes.reserve(entries.size());
for(const auto& entry : entries) for(const auto& entry : entries)
{ {
_keypadCodeIds.push_back(entry.codeId); _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(); idExists = std::find(_keypadCodeIds.begin(), _keypadCodeIds.end(), codeId) != _keypadCodeIds.end();
} }
Nuki::CmdResult result = (Nuki::CmdResult)-1; if(strcmp(action, "check") == 0) {
int retryCount = 0; if(!_preferences->getBool(preference_keypad_check_code_enabled, false))
{
_network->publishKeypadJsonCommandResult("checkingKeypadCodesDisabled");
return;
}
while(retryCount < _nrOfRetries + 1) if(pow(_invalidCount, 5) + _lastCodeCheck > (esp_timer_get_time() / 1000))
{ {
if(strcmp(action, "delete") == 0) { _network->publishKeypadJsonCommandResult("checkingCodesBlockedTooManyInvalid");
if(idExists) _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); _invalidCount = 0;
Log->print(F("Delete keypad code: ")); _network->publishKeypadJsonCommandResult("codeValid");
Log->println((int)result); Log->println("Valid");
return;
} }
else else
{ {
_network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); _invalidCount++;
_network->publishKeypadJsonCommandResult("codeInvalid");
Log->println("Invalid");
return; return;
} }
} }
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) else
{ {
if(name.length() < 1) _invalidCount++;
{ _network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
if (strcmp(action, "update") != 0) 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; return;
} }
} }
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
if(code != 12)
{ {
String codeStr = json["code"].as<String>(); if(name.length() < 1)
bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1);
if (!codeValid)
{ {
_network->publishKeypadJsonCommandResult("noValidCodeSet"); if (strcmp(action, "update") != 0)
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;
if(timeLimited == 1)
{
if(allowedFrom.length() > 0)
{
if(allowedFrom.length() == 19)
{ {
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); _network->publishKeypadJsonCommandResult("noNameSet");
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); return;
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(code != 12)
{
String codeStr = json["code"].as<String>();
bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1);
if (!codeValid)
{
_network->publishKeypadJsonCommandResult("noValidCodeSet");
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;
if(timeLimited == 1)
{
if(allowedFrom.length() > 0)
{
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"); _network->publishKeypadJsonCommandResult("invalidAllowedFrom");
return; return;
} }
} }
else
{
_network->publishKeypadJsonCommandResult("invalidAllowedFrom");
return;
}
}
if(allowedUntil.length() > 0) if(allowedUntil.length() > 0)
{
if(allowedUntil.length() > 0 == 19)
{ {
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); if(allowedUntil.length() > 0 == 19)
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); {
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).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(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"); _network->publishKeypadJsonCommandResult("invalidAllowedUntil");
return; return;
} }
} }
else
{
_network->publishKeypadJsonCommandResult("invalidAllowedUntil");
return;
}
}
if(allowedFromTime.length() > 0) if(allowedFromTime.length() > 0)
{
if(allowedFromTime.length() == 5)
{ {
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); if(allowedFromTime.length() == 5)
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); {
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(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
{
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
return;
}
}
else
{ {
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); _network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
return; return;
} }
} }
else
{
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
return;
}
}
if(allowedUntilTime.length() > 0) if(allowedUntilTime.length() > 0)
{
if(allowedUntilTime.length() == 5)
{ {
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); if(allowedUntilTime.length() == 5)
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); {
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(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
{
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
return;
}
}
else
{ {
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
return; return;
} }
} }
else
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)
{ {
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); 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.addKeypadEntry(entry);
Log->print(F("Add keypad code: "));
Log->println((int)result);
}
else if (strcmp(action, "update") == 0)
{
if(!codeId)
{
_network->publishKeypadJsonCommandResult("noCodeIdSet");
return; return;
} }
}
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; if(!idExists)
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(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.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(!idExists)
{
_network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
return;
}
Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
bool foundExisting = false;
if(resultKp == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiOpener::KeypadEntry> entries;
_nukiOpener.getKeypadEntries(&entries);
for(const auto& entry : entries)
{ {
if (codeId != entry.codeId) continue; _network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
else foundExisting = true; return;
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(!foundExisting) Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
bool foundExisting = false;
if(resultKp == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiOpener::KeypadEntry> entries;
_nukiOpener.getKeypadEntries(&entries);
for(const auto& entry : entries)
{
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(!foundExisting)
{
_network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry");
return;
}
}
else
{ {
_network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry");
return; return;
} }
}
else
{
_network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry");
return;
}
NukiOpener::UpdatedKeypadEntry entry; NukiOpener::UpdatedKeypadEntry entry;
memset(&entry, 0, sizeof(entry)); memset(&entry, 0, sizeof(entry));
entry.codeId = codeId; entry.codeId = codeId;
entry.code = code; entry.code = code;
if(name.length() < 1) 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;
if(enabled == 1)
{
if(timeLimited == 1)
{ {
if(allowedFrom.length() > 0) size_t nameLen = strlen(oldName);
{ memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
entry.allowedFromYear = allowedFromAr[0]; }
entry.allowedFromMonth = allowedFromAr[1]; else
entry.allowedFromDay = allowedFromAr[2]; {
entry.allowedFromHour = allowedFromAr[3]; size_t nameLen = name.length();
entry.allowedFromMin = allowedFromAr[4]; memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
entry.allowedFromSec = allowedFromAr[5]; }
} entry.enabled = enabled;
entry.timeLimited = timeLimited;
if(allowedUntil.length() > 0) if(enabled == 1)
{
if(timeLimited == 1)
{ {
entry.allowedUntilYear = allowedUntilAr[0]; if(allowedFrom.length() > 0)
entry.allowedUntilMonth = allowedUntilAr[1]; {
entry.allowedUntilDay = allowedUntilAr[2]; entry.allowedFromYear = allowedFromAr[0];
entry.allowedUntilHour = allowedUntilAr[3]; entry.allowedFromMonth = allowedFromAr[1];
entry.allowedUntilMin = allowedUntilAr[4]; entry.allowedFromDay = allowedFromAr[2];
entry.allowedUntilSec = allowedUntilAr[5]; entry.allowedFromHour = allowedFromAr[3];
} entry.allowedFromMin = allowedFromAr[4];
entry.allowedFromSec = allowedFromAr[5];
}
entry.allowedWeekdays = allowedWeekdaysInt; 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(allowedFromTime.length() > 0) entry.allowedWeekdays = allowedWeekdaysInt;
{
entry.allowedFromTimeHour = allowedFromTimeAr[0];
entry.allowedFromTimeMin = allowedFromTimeAr[1];
}
if(allowedUntilTime.length() > 0) if(allowedFromTime.length() > 0)
{ {
entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; entry.allowedFromTimeHour = allowedFromTimeAr[0];
entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; 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;
}
if(result != Nuki::CmdResult::Success) {
++retryCount;
}
else break;
} }
else
updateKeypad(false);
if((int)result != -1)
{ {
_network->publishKeypadJsonCommandResult("invalidAction"); char resultStr[15];
return; memset(&resultStr, 0, sizeof(resultStr));
NukiOpener::cmdResultToString(result, resultStr);
_network->publishKeypadJsonCommandResult(resultStr);
} }
if(result != Nuki::CmdResult::Success) {
++retryCount;
}
else break;
}
updateKeypad(false);
if((int)result != -1)
{
char resultStr[15];
memset(&resultStr, 0, sizeof(resultStr));
NukiOpener::cmdResultToString(result, resultStr);
_network->publishKeypadJsonCommandResult(resultStr);
} }
} }
else else

View File

@@ -106,12 +106,16 @@ private:
bool _publishAuthData = false; bool _publishAuthData = false;
bool _clearAuthData = false; bool _clearAuthData = false;
bool _disableNonJSON = false; bool _disableNonJSON = false;
bool _checkKeypadCodes = false;
int _nrOfRetries = 0; int _nrOfRetries = 0;
int _retryDelay = 0; int _retryDelay = 0;
int _retryConfigCount = 0; int _retryConfigCount = 0;
int _retryLockstateCount = 0; int _retryLockstateCount = 0;
int64_t _nextRetryTs = 0; int64_t _nextRetryTs = 0;
int64_t _invalidCount = 0;
int64_t _lastCodeCheck = 0;
std::vector<uint16_t> _keypadCodeIds; std::vector<uint16_t> _keypadCodeIds;
std::vector<uint32_t> _keypadCodes;
std::vector<uint8_t> _timeControlIds; std::vector<uint8_t> _timeControlIds;
std::vector<uint32_t> _authIds; std::vector<uint32_t> _authIds;

View File

@@ -137,7 +137,7 @@ void NukiWrapper::readSettings()
_retryDelay = _preferences->getInt(preference_command_retry_delay); _retryDelay = _preferences->getInt(preference_command_retry_delay);
_rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000;
_disableNonJSON = _preferences->getBool(preference_disable_non_json, false); _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_basic_acl, &_basicLockConfigaclPrefs, sizeof(_basicLockConfigaclPrefs));
_preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs)); _preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs));
@@ -369,6 +369,10 @@ void NukiWrapper::update()
_nextKeypadUpdateTs = ts + _intervalKeypad * 1000; _nextKeypadUpdateTs = ts + _intervalKeypad * 1000;
updateKeypad(false); updateKeypad(false);
} }
if(_checkKeypadCodes && _invalidCount > 0 && ts - 120000 < _lastCodeCheck)
{
_invalidCount--;
}
} }
if(_clearAuthData) if(_clearAuthData)
{ {
@@ -773,10 +777,13 @@ void NukiWrapper::updateKeypad(bool retrieved)
_network->publishKeypad(entries, _maxKeypadCodeCount); _network->publishKeypad(entries, _maxKeypadCodeCount);
_keypadCodeIds.clear(); _keypadCodeIds.clear();
_keypadCodes.clear();
_keypadCodeIds.reserve(entries.size()); _keypadCodeIds.reserve(entries.size());
_keypadCodes.reserve(entries.size());
for(const auto& entry : entries) for(const auto& entry : entries)
{ {
_keypadCodeIds.push_back(entry.codeId); _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(); 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; if(pow(_invalidCount, 5) + _lastCodeCheck > (esp_timer_get_time() / 1000))
int retryCount = 0; {
_network->publishKeypadJsonCommandResult("checkingCodesBlockedTooManyInvalid");
_lastCodeCheck = (esp_timer_get_time() / 1000);
return;
}
while(retryCount < _nrOfRetries + 1) _lastCodeCheck = (esp_timer_get_time() / 1000);
{
if(strcmp(action, "delete") == 0) { if(idExists)
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); _invalidCount = 0;
Log->print(F("Delete keypad code: ")); _network->publishKeypadJsonCommandResult("codeValid");
Log->println((int)result); Log->println("Valid");
return;
} }
else else
{ {
_network->publishKeypadJsonCommandResult("noExistingCodeIdSet"); _invalidCount++;
_network->publishKeypadJsonCommandResult("codeInvalid");
Log->println("Invalid");
return; return;
} }
} }
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0) else
{ {
if(name.length() < 1) _invalidCount++;
{ _network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
if (strcmp(action, "update") != 0) 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; return;
} }
} }
else if(strcmp(action, "add") == 0 || strcmp(action, "update") == 0)
if(code != 12)
{ {
String codeStr = json["code"].as<String>(); if(name.length() < 1)
bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1);
if (!codeValid)
{ {
_network->publishKeypadJsonCommandResult("noValidCodeSet"); if (strcmp(action, "update") != 0)
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;
if(timeLimited == 1)
{
if(allowedFrom.length() > 0)
{
if(allowedFrom.length() == 19)
{ {
allowedFromAr[0] = (uint16_t)allowedFrom.substring(0, 4).toInt(); _network->publishKeypadJsonCommandResult("noNameSet");
allowedFromAr[1] = (uint8_t)allowedFrom.substring(5, 7).toInt(); return;
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(code != 12)
{
String codeStr = json["code"].as<String>();
bool codeValid = code > 100000 && code < 1000000 && (codeStr.indexOf('0') == -1);
if (!codeValid)
{
_network->publishKeypadJsonCommandResult("noValidCodeSet");
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;
if(timeLimited == 1)
{
if(allowedFrom.length() > 0)
{
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"); _network->publishKeypadJsonCommandResult("invalidAllowedFrom");
return; return;
} }
} }
else
{
_network->publishKeypadJsonCommandResult("invalidAllowedFrom");
return;
}
}
if(allowedUntil.length() > 0) if(allowedUntil.length() > 0)
{
if(allowedUntil.length() > 0 == 19)
{ {
allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt(); if(allowedUntil.length() > 0 == 19)
allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt(); {
allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt(); allowedUntilAr[0] = (uint16_t)allowedUntil.substring(0, 4).toInt();
allowedUntilAr[3] = (uint8_t)allowedUntil.substring(11, 13).toInt(); allowedUntilAr[1] = (uint8_t)allowedUntil.substring(5, 7).toInt();
allowedUntilAr[4] = (uint8_t)allowedUntil.substring(14, 16).toInt(); allowedUntilAr[2] = (uint8_t)allowedUntil.substring(8, 10).toInt();
allowedUntilAr[5] = (uint8_t)allowedUntil.substring(17, 19).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(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"); _network->publishKeypadJsonCommandResult("invalidAllowedUntil");
return; return;
} }
} }
else
{
_network->publishKeypadJsonCommandResult("invalidAllowedUntil");
return;
}
}
if(allowedFromTime.length() > 0) if(allowedFromTime.length() > 0)
{
if(allowedFromTime.length() == 5)
{ {
allowedFromTimeAr[0] = (uint8_t)allowedFromTime.substring(0, 2).toInt(); if(allowedFromTime.length() == 5)
allowedFromTimeAr[1] = (uint8_t)allowedFromTime.substring(3, 5).toInt(); {
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(allowedFromTimeAr[0] < 0 || allowedFromTimeAr[0] > 23 || allowedFromTimeAr[1] < 0 || allowedFromTimeAr[1] > 59)
{
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
return;
}
}
else
{ {
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime"); _network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
return; return;
} }
} }
else
{
_network->publishKeypadJsonCommandResult("invalidAllowedFromTime");
return;
}
}
if(allowedUntilTime.length() > 0) if(allowedUntilTime.length() > 0)
{
if(allowedUntilTime.length() == 5)
{ {
allowedUntilTimeAr[0] = (uint8_t)allowedUntilTime.substring(0, 2).toInt(); if(allowedUntilTime.length() == 5)
allowedUntilTimeAr[1] = (uint8_t)allowedUntilTime.substring(3, 5).toInt(); {
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(allowedUntilTimeAr[0] < 0 || allowedUntilTimeAr[0] > 23 || allowedUntilTimeAr[1] < 0 || allowedUntilTimeAr[1] > 59)
{
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
return;
}
}
else
{ {
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); _network->publishKeypadJsonCommandResult("invalidAllowedUntilTime");
return; return;
} }
} }
else
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)
{ {
_network->publishKeypadJsonCommandResult("invalidAllowedUntilTime"); 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.addKeypadEntry(entry);
Log->print(F("Add keypad code: "));
Log->println((int)result);
}
else if (strcmp(action, "update") == 0)
{
if(!codeId)
{
_network->publishKeypadJsonCommandResult("noCodeIdSet");
return; return;
} }
}
if(allowedWeekdays.indexOf("mon") >= 0) allowedWeekdaysInt += 64; if(!idExists)
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(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.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(!idExists)
{
_network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
return;
}
Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
bool foundExisting = false;
if(resultKp == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiLock::KeypadEntry> entries;
_nukiLock.getKeypadEntries(&entries);
for(const auto& entry : entries)
{ {
if (codeId != entry.codeId) continue; _network->publishKeypadJsonCommandResult("noExistingCodeIdSet");
else foundExisting = true; return;
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(!foundExisting) Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
bool foundExisting = false;
if(resultKp == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiLock::KeypadEntry> entries;
_nukiLock.getKeypadEntries(&entries);
for(const auto& entry : entries)
{
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(!foundExisting)
{
_network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry");
return;
}
}
else
{ {
_network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry"); _network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry");
return; return;
} }
}
else
{
_network->publishKeypadJsonCommandResult("failedToRetrieveExistingKeypadEntry");
return;
}
NukiLock::UpdatedKeypadEntry entry; NukiLock::UpdatedKeypadEntry entry;
memset(&entry, 0, sizeof(entry)); memset(&entry, 0, sizeof(entry));
entry.codeId = codeId; entry.codeId = codeId;
entry.code = code; entry.code = code;
if(name.length() < 1) 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;
if(enabled == 1)
{
if(timeLimited == 1)
{ {
if(allowedFrom.length() > 0) size_t nameLen = strlen(oldName);
{ memcpy(&entry.name, oldName, nameLen > 20 ? 20 : nameLen);
entry.allowedFromYear = allowedFromAr[0]; }
entry.allowedFromMonth = allowedFromAr[1]; else
entry.allowedFromDay = allowedFromAr[2]; {
entry.allowedFromHour = allowedFromAr[3]; size_t nameLen = name.length();
entry.allowedFromMin = allowedFromAr[4]; memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
entry.allowedFromSec = allowedFromAr[5]; }
} entry.enabled = enabled;
entry.timeLimited = timeLimited;
if(allowedUntil.length() > 0) if(enabled == 1)
{
if(timeLimited == 1)
{ {
entry.allowedUntilYear = allowedUntilAr[0]; if(allowedFrom.length() > 0)
entry.allowedUntilMonth = allowedUntilAr[1]; {
entry.allowedUntilDay = allowedUntilAr[2]; entry.allowedFromYear = allowedFromAr[0];
entry.allowedUntilHour = allowedUntilAr[3]; entry.allowedFromMonth = allowedFromAr[1];
entry.allowedUntilMin = allowedUntilAr[4]; entry.allowedFromDay = allowedFromAr[2];
entry.allowedUntilSec = allowedUntilAr[5]; entry.allowedFromHour = allowedFromAr[3];
} entry.allowedFromMin = allowedFromAr[4];
entry.allowedFromSec = allowedFromAr[5];
}
entry.allowedWeekdays = allowedWeekdaysInt; 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(allowedFromTime.length() > 0) entry.allowedWeekdays = allowedWeekdaysInt;
{
entry.allowedFromTimeHour = allowedFromTimeAr[0];
entry.allowedFromTimeMin = allowedFromTimeAr[1];
}
if(allowedUntilTime.length() > 0) if(allowedFromTime.length() > 0)
{ {
entry.allowedUntilTimeHour = allowedUntilTimeAr[0]; entry.allowedFromTimeHour = allowedFromTimeAr[0];
entry.allowedUntilTimeMin = allowedUntilTimeAr[1]; 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;
}
if(result != Nuki::CmdResult::Success) {
++retryCount;
}
else break;
} }
else
updateKeypad(false);
if((int)result != -1)
{ {
_network->publishKeypadJsonCommandResult("invalidAction"); char resultStr[15];
return; memset(&resultStr, 0, sizeof(resultStr));
NukiLock::cmdResultToString(result, resultStr);
_network->publishKeypadJsonCommandResult(resultStr);
} }
if(result != Nuki::CmdResult::Success) {
++retryCount;
}
else break;
}
updateKeypad(false);
if((int)result != -1)
{
char resultStr[15];
memset(&resultStr, 0, sizeof(resultStr));
NukiLock::cmdResultToString(result, resultStr);
_network->publishKeypadJsonCommandResult(resultStr);
} }
} }
else else

View File

@@ -107,7 +107,11 @@ private:
int _restartBeaconTimeout = 0; // seconds int _restartBeaconTimeout = 0; // seconds
bool _publishAuthData = false; bool _publishAuthData = false;
bool _clearAuthData = false; bool _clearAuthData = false;
bool _checkKeypadCodes = false;
int64_t _invalidCount = 0;
int64_t _lastCodeCheck = 0;
std::vector<uint16_t> _keypadCodeIds; std::vector<uint16_t> _keypadCodeIds;
std::vector<uint32_t> _keypadCodes;
std::vector<uint8_t> _timeControlIds; std::vector<uint8_t> _timeControlIds;
std::vector<uint32_t> _authIds; std::vector<uint32_t> _authIds;

View File

@@ -98,6 +98,7 @@
#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis" #define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis"
#define preference_official_hybrid_actions (char*)"hybridAct" #define preference_official_hybrid_actions (char*)"hybridAct"
#define preference_official_hybrid_retry (char*)"hybridRtry" #define preference_official_hybrid_retry (char*)"hybridRtry"
#define preference_keypad_check_code_enabled (char*)"kpChkEna"
//NOT USER CHANGABLE //NOT USER CHANGABLE
#define preference_updater_version (char*)"updVer" #define preference_updater_version (char*)"updVer"
@@ -284,7 +285,7 @@ private:
preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, 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_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_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<char*> _redact = std::vector<char*> _redact =
{ {
@@ -300,7 +301,7 @@ private:
preference_publish_authdata, preference_publish_debug_info, preference_network_wifi_fallback_disabled, preference_official_hybrid_enabled, 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_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_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<char*> _bytePrefs = std::vector<char*> _bytePrefs =
{ {

View File

@@ -1561,6 +1561,16 @@ bool WebCfgServer::processArgs(AsyncWebServerRequest *request, String& message)
//configChanged = true; //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") else if(key == "KPENA")
{ {
if(_preferences->getBool(preference_keypad_control_enabled, false) != (value == "1")) if(_preferences->getBool(preference_keypad_control_enabled, false) != (value == "1"))
@@ -2639,15 +2649,15 @@ void WebCfgServer::buildCredHtml(AsyncWebServerRequest *request)
{ {
_response = ""; _response = "";
buildHtmlHeader(); buildHtmlHeader();
_response.concat("<form class=\"adapt\" method=\"post\" action=\"savecfg\">"); _response.concat("<form id=\"credfrm\" class=\"adapt\" onsubmit=\"return testcreds();\" method=\"post\" action=\"savecfg\">");
_response.concat("<h3>Credentials</h3>"); _response.concat("<h3>Credentials</h3>");
_response.concat("<table>"); _response.concat("<table>");
printInputField("CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "", false, true); printInputField("CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 30, "id=\"inputuser\"", false, true);
printInputField("CREDPASS", "Password", "*", 30, "", true, true); printInputField("CREDPASS", "Password", "*", 30, "id=\"inputpass\"", true, true);
printInputField("CREDPASSRE", "Retype password", "*", 30, "", true); printInputField("CREDPASSRE", "Retype password", "*", 30, "", true);
_response.concat("</table>"); _response.concat("</table>");
_response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">"); _response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
_response.concat("</form>"); _response.concat("</form><script>function testcreds() { var input_user = document.getElementById(\"inputuser\").value; var input_pass = document.getElementById(\"inputpass\").value; var pattern = /^[ -~]*$/; if(!pattern.test(input_user) || !pattern.test(input_pass)) { alert('Only non unicode characters are allowed in username and password'); return false;} else { return true; } }</script>");
if(_nuki != nullptr) if(_nuki != nullptr)
{ {
_response.concat("<br><br><form class=\"adapt\" method=\"post\" action=\"savecfg\">"); _response.concat("<br><br><form class=\"adapt\" method=\"post\" action=\"savecfg\">");
@@ -2919,6 +2929,7 @@ void WebCfgServer::buildAccLvlHtml(AsyncWebServerRequest *request)
printCheckBox("KPPUB", "Publish keypad entries information", _preferences->getBool(preference_keypad_info_enabled), ""); printCheckBox("KPPUB", "Publish keypad entries information", _preferences->getBool(preference_keypad_info_enabled), "");
printCheckBox("KPPER", "Publish a topic per keypad entry and create HA sensor", _preferences->getBool(preference_keypad_topic_per_entry), ""); printCheckBox("KPPER", "Publish a topic per keypad entry and create HA sensor", _preferences->getBool(preference_keypad_topic_per_entry), "");
printCheckBox("KPCODE", "Also publish keypad codes (<span class=\"warning\">Disadvised for security reasons</span>)", _preferences->getBool(preference_keypad_publish_code, false), ""); printCheckBox("KPCODE", "Also publish keypad codes (<span class=\"warning\">Disadvised for security reasons</span>)", _preferences->getBool(preference_keypad_publish_code, false), "");
printCheckBox("KPCHECK", "Allow checking if keypad codes are valid (<span class=\"warning\">Disadvised for security reasons</span>)", _preferences->getBool(preference_keypad_check_code_enabled, false), "");
printCheckBox("KPENA", "Add, modify and delete keypad codes", _preferences->getBool(preference_keypad_control_enabled), ""); 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), ""); 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(_preferences->getBool(preference_keypad_topic_per_entry, false) ? "Yes" : "No");
_response.concat("\nPublish Keypad codes: "); _response.concat("\nPublish Keypad codes: ");
_response.concat(_preferences->getBool(preference_keypad_publish_code, false) ? "Yes" : "No"); _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("\nMax keypad entries to retrieve: ");
_response.concat(_preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD)); _response.concat(_preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
_response.concat("\nPublish timecontrol info: "); _response.concat("\nPublish timecontrol info: ");