BLE fixes (#436)

* BLE fixes

* Lock/Opener action fixes

* Prevent Keypad/TC request when no valid PIN is set

* Set BLE Power and C6 improvements

* Update nuki_ble

* BLE High performance on state change

* Update nuki_ble
This commit is contained in:
iranl
2024-08-01 15:56:39 +02:00
committed by GitHub
parent 41483b1b19
commit 9d09c4354d
15 changed files with 383 additions and 280 deletions

View File

@@ -113,7 +113,7 @@ void NukiNetworkLock::initialize()
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
}
if(!_preferences->getBool(preference_conf_info_enabled, false))
if(!_preferences->getBool(preference_conf_info_enabled, true))
{
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
_network->removeTopic(_mqttPath, mqtt_topic_config_advanced_json);

View File

@@ -69,7 +69,7 @@ void NukiNetworkOpener::initialize()
//_network->removeTopic(_mqttPath, mqtt_topic_presence);
}
if(!_preferences->getBool(preference_conf_info_enabled, false))
if(!_preferences->getBool(preference_conf_info_enabled, true))
{
_network->removeTopic(_mqttPath, mqtt_topic_config_basic_json);
_network->removeTopic(_mqttPath, mqtt_topic_config_advanced_json);
@@ -156,7 +156,7 @@ void NukiNetworkOpener::onMqttDataReceived(const char* topic, byte* payload, con
strcmp(value, "denied") == 0 ||
strcmp(value, "error") == 0) return;
Log->print(F("Lock action received: "));
Log->print(F("Opener action received: "));
Log->println(value);
LockActionResult lockActionResult = LockActionResult::Failed;
if(_lockActionReceivedCallback != NULL)

View File

@@ -24,10 +24,10 @@ NukiOpenerWrapper::NukiOpenerWrapper(const std::string& deviceName, NukiDeviceId
nukiOpenerInst = this;
memset(&_lastKeyTurnerState, sizeof(NukiLock::KeyTurnerState), 0);
memset(&_lastBatteryReport, sizeof(NukiLock::BatteryReport), 0);
memset(&_batteryReport, sizeof(NukiLock::BatteryReport), 0);
memset(&_keyTurnerState, sizeof(NukiLock::KeyTurnerState), 0);
memset(&_lastKeyTurnerState, sizeof(NukiOpener::OpenerState), 0);
memset(&_lastBatteryReport, sizeof(NukiOpener::BatteryReport), 0);
memset(&_batteryReport, sizeof(NukiOpener::BatteryReport), 0);
memset(&_keyTurnerState, sizeof(NukiOpener::OpenerState), 0);
_keyTurnerState.lockState = NukiOpener::LockState::Undefined;
network->setLockActionReceivedCallback(nukiOpenerInst->onLockActionReceivedCallback);
@@ -48,6 +48,19 @@ NukiOpenerWrapper::~NukiOpenerWrapper()
void NukiOpenerWrapper::initialize()
{
_nukiOpener.initialize();
esp_power_level_t powerLevel;
if(_preferences->getInt(preference_ble_tx_power, 9) >= 9) powerLevel = ESP_PWR_LVL_P9;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= 6) powerLevel = ESP_PWR_LVL_P6;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= 3) powerLevel = ESP_PWR_LVL_P6;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= 0) powerLevel = ESP_PWR_LVL_P3;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -3) powerLevel = ESP_PWR_LVL_N3;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -6) powerLevel = ESP_PWR_LVL_N6;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -9) powerLevel = ESP_PWR_LVL_N9;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -12) powerLevel = ESP_PWR_LVL_N12;
_nukiOpener.setPower(powerLevel);
_nukiOpener.registerBleScanner(_bleScanner);
_intervalLockstate = _preferences->getInt(preference_query_interval_lockstate);
@@ -103,9 +116,10 @@ void NukiOpenerWrapper::initialize()
}
_nukiOpener.setEventHandler(this);
_nukiOpener.setConnectTimeout(3);
_nukiOpener.setDisconnectTimeout(5000);
Log->print(F("Lock state interval: "));
Log->print(F("Opener state interval: "));
Log->print(_intervalLockstate);
Log->print(F(" | Battery interval: "));
Log->print(_intervalBattery);
@@ -219,33 +233,23 @@ void NukiOpenerWrapper::update()
updateKeypad(false);
}
if(_nextLockAction != (NukiOpener::LockAction)0xff && ts > _nextRetryTs)
if(_nextLockAction != (NukiOpener::LockAction)0xff)
{
Nuki::CmdResult cmdResult = _nukiOpener.lockAction(_nextLockAction, 0, 0);
delay(250);
_retryCount = 0;
Nuki::CmdResult cmdResult;
char resultStr[15] = {0};
NukiOpener::cmdResultToString(cmdResult, resultStr);
_network->publishCommandResult(resultStr);
Log->print(F("Lock action result: "));
Log->println(resultStr);
if(cmdResult == Nuki::CmdResult::Success)
while(_retryCount < _nrOfRetries + 1 && cmdResult != Nuki::CmdResult::Success)
{
_retryCount = 0;
_nextLockAction = (NukiOpener::LockAction) 0xff;
_network->publishRetry("--");
cmdResult = _nukiOpener.lockAction(_nextLockAction, 0, 0);
char resultStr[15] = {0};
NukiOpener::cmdResultToString(cmdResult, resultStr);
if(_intervalLockstate > 10)
{
_nextLockStateUpdateTs = ts + 10 * 1000;
}
}
else
{
if(_retryCount < _nrOfRetries)
_network->publishCommandResult(resultStr);
Log->print(F("Opener action result: "));
Log->println(resultStr);
if(cmdResult != Nuki::CmdResult::Success)
{
Log->print(F("Opener: Last command failed, retrying after "));
Log->print(_retryDelay);
@@ -256,20 +260,27 @@ void NukiOpenerWrapper::update()
_network->publishRetry(std::to_string(_retryCount + 1));
_nextRetryTs = (esp_timer_get_time() / 1000) + _retryDelay;
delay(_retryDelay);
++_retryCount;
}
else
{
Log->println(F("Opener: Maximum number of retries exceeded, aborting."));
_network->publishRetry("failed");
_retryCount = 0;
_nextRetryTs = 0;
_nextLockAction = (NukiOpener::LockAction) 0xff;
}
postponeBleWatchdog();
}
if(cmdResult == Nuki::CmdResult::Success)
{
_nextLockAction = (NukiOpener::LockAction) 0xff;
_network->publishRetry("--");
_retryCount = 0;
if(_intervalLockstate > 10) _nextLockStateUpdateTs = ts + 10 * 1000;
}
else
{
Log->println(F("Opener: Maximum number of retries exceeded, aborting."));
_network->publishRetry("failed");
_retryCount = 0;
_nextLockAction = (NukiOpener::LockAction) 0xff;
}
postponeBleWatchdog();
}
if(_clearAuthData)
@@ -349,7 +360,7 @@ void NukiOpenerWrapper::updateKeyTurnerState()
{
Log->print(F("Querying opener state: "));
result =_nukiOpener.requestOpenerState(&_keyTurnerState);
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -459,7 +470,7 @@ void NukiOpenerWrapper::updateConfig()
_hasKeypad = _nukiConfig.hasKeypad > 0 || _nukiConfig.hasKeypadV2 > 0;
_firmwareVersion = std::to_string(_nukiConfig.firmwareVersion[0]) + "." + std::to_string(_nukiConfig.firmwareVersion[1]) + "." + std::to_string(_nukiConfig.firmwareVersion[2]);
_hardwareVersion = std::to_string(_nukiConfig.hardwareRevision[0]) + "." + std::to_string(_nukiConfig.hardwareRevision[1]);
if(_preferences->getBool(preference_conf_info_enabled, false)) _network->publishConfig(_nukiConfig);
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishConfig(_nukiConfig);
_retryConfigCount = 0;
if(_preferences->getBool(preference_timecontrol_info_enabled)) updateTimeControl(false);
@@ -473,7 +484,7 @@ void NukiOpenerWrapper::updateConfig()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiOpener.verifySecurityPin();
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -513,7 +524,7 @@ void NukiOpenerWrapper::updateConfig()
}
if(_nukiAdvancedConfigValid && _preferences->getUInt(preference_nuki_id_opener, 0) == _nukiConfig.nukiId)
{
if(_preferences->getBool(preference_conf_info_enabled, false)) _network->publishAdvancedConfig(_nukiAdvancedConfig);
if(_preferences->getBool(preference_conf_info_enabled, true)) _network->publishAdvancedConfig(_nukiAdvancedConfig);
_retryConfigCount = 0;
}
else
@@ -538,7 +549,6 @@ void NukiOpenerWrapper::updateAuthData(bool retrieved)
if(!retrieved)
{
delay(250);
Nuki::CmdResult result = (Nuki::CmdResult)-1;
_retryCount = 0;
@@ -546,7 +556,7 @@ void NukiOpenerWrapper::updateAuthData(bool retrieved)
{
Log->print(F("Retrieve log entries: "));
result = _nukiOpener.retrieveLogEntries(0, _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 1, false);
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -604,6 +614,12 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved)
{
if(!_preferences->getBool(preference_keypad_info_enabled)) return;
if(!isPinValid())
{
Log->println(F("No valid Nuki Opener PIN set"));
return;
}
if(!retrieved)
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
@@ -613,7 +629,7 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved)
{
Log->print(F("Querying opener keypad: "));
result = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -631,7 +647,7 @@ void NukiOpenerWrapper::updateKeypad(bool retrieved)
std::list<NukiOpener::KeypadEntry> entries;
_nukiOpener.getKeypadEntries(&entries);
Log->print(F("Lock keypad codes: "));
Log->print(F("Opener keypad codes: "));
Log->println(entries.size());
entries.sort([](const NukiOpener::KeypadEntry& a, const NukiOpener::KeypadEntry& b) { return a.codeId < b.codeId; });
@@ -665,6 +681,12 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
{
if(!_preferences->getBool(preference_timecontrol_info_enabled)) return;
if(!isPinValid())
{
Log->println(F("No valid Nuki Opener PIN set"));
return;
}
if(!retrieved)
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
@@ -674,7 +696,7 @@ void NukiOpenerWrapper::updateTimeControl(bool retrieved)
{
Log->print(F("Querying opener time control: "));
result = _nukiOpener.retrieveTimeControlEntries();
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -1509,7 +1531,6 @@ void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
entry.code = codeInt;
result = _nukiOpener.addKeypadEntry(entry);
delay(250);
Log->print("Add keypad code: ");
Log->println((int)result);
updateKeypad(false);
@@ -1523,7 +1544,6 @@ void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint
}
result = _nukiOpener.deleteKeypadEntry(id);
delay(250);
Log->print("Delete keypad code: ");
Log->println((int)result);
updateKeypad(false);
@@ -1559,8 +1579,7 @@ void NukiOpenerWrapper::onKeypadCommandReceived(const char *command, const uint
entry.code = codeInt;
entry.enabled = enabled == 0 ? 0 : 1;
result = _nukiOpener.updateKeypadEntry(entry);
delay(250);
Log->print("Update keypad code: ");
Log->print("Update keypad code: ");
Log->println((int)result);
updateKeypad(false);
}
@@ -1677,7 +1696,6 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value)
if(idExists)
{
result = _nukiOpener.deleteKeypadEntry(codeId);
delay(250);
Log->print(F("Delete keypad code: "));
Log->println((int)result);
}
@@ -1864,7 +1882,6 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value)
}
result = _nukiOpener.addKeypadEntry(entry);
delay(250);
Log->print(F("Add keypad code: "));
Log->println((int)result);
}
@@ -1883,11 +1900,11 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value)
}
Nuki::CmdResult resultKp = _nukiOpener.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
delay(250);
bool foundExisting = false;
if(resultKp == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiOpener::KeypadEntry> entries;
_nukiOpener.getKeypadEntries(&entries);
@@ -2012,7 +2029,6 @@ void NukiOpenerWrapper::onKeypadJsonCommandReceived(const char *value)
}
result = _nukiOpener.updateKeypadEntry(entry);
delay(250);
Log->print(F("Update keypad code: "));
Log->println((int)result);
}
@@ -2119,7 +2135,6 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
if(idExists)
{
result = _nukiOpener.removeTimeControlEntry(entryId);
delay(250);
Log->print(F("Delete time control: "));
Log->println((int)result);
}
@@ -2178,7 +2193,6 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
entry.lockAction = timeControlLockAction;
result = _nukiOpener.addTimeControlEntry(entry);
delay(250);
Log->print(F("Add time control: "));
Log->println((int)result);
}
@@ -2191,11 +2205,11 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
}
Nuki::CmdResult resultTc = _nukiOpener.retrieveTimeControlEntries();
delay(250);
bool foundExisting = false;
if(resultTc == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiOpener::TimeControlEntry> timeControlEntries;
_nukiOpener.getTimeControlEntries(&timeControlEntries);
@@ -2241,7 +2255,6 @@ void NukiOpenerWrapper::onTimeControlCommandReceived(const char *value)
entry.lockAction = timeControlLockAction;
result = _nukiOpener.updateTimeControlEntry(entry);
delay(250);
Log->print(F("Update time control: "));
Log->println((int)result);
}
@@ -2318,7 +2331,6 @@ void NukiOpenerWrapper::readConfig()
while(_retryCount < _nrOfRetries + 1)
{
Nuki::CmdResult result = _nukiOpener.requestConfig(&_nukiConfig);
delay(250);
_nukiConfigValid = result == Nuki::CmdResult::Success;
if(!_nukiConfigValid) {
@@ -2329,6 +2341,7 @@ void NukiOpenerWrapper::readConfig()
char resultStr[20];
NukiOpener::cmdResultToString(result, resultStr);
Log->print(F("Opener config result: "));
Log->println(resultStr);
postponeBleWatchdog();
}
@@ -2341,7 +2354,6 @@ void NukiOpenerWrapper::readAdvancedConfig()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiOpener.requestAdvancedConfig(&_nukiAdvancedConfig);
delay(250);
_nukiAdvancedConfigValid = result == Nuki::CmdResult::Success;
if(!_nukiAdvancedConfigValid) {
@@ -2352,6 +2364,7 @@ void NukiOpenerWrapper::readAdvancedConfig()
char resultStr[20];
NukiOpener::cmdResultToString(result, resultStr);
Log->print(F("Opener advanced config result: "));
Log->println(resultStr);
postponeBleWatchdog();
}
@@ -2383,7 +2396,6 @@ void NukiOpenerWrapper::disableHASS()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiOpener.requestConfig(&_nukiConfig);
delay(250);
_nukiConfigValid = result == Nuki::CmdResult::Success;
if(!_nukiConfigValid) {

View File

@@ -51,8 +51,20 @@ NukiWrapper::~NukiWrapper()
void NukiWrapper::initialize(const bool& firstStart)
{
_nukiLock.initialize();
esp_power_level_t powerLevel;
if(_preferences->getInt(preference_ble_tx_power, 9) >= 9) powerLevel = ESP_PWR_LVL_P9;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= 6) powerLevel = ESP_PWR_LVL_P6;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= 3) powerLevel = ESP_PWR_LVL_P6;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= 0) powerLevel = ESP_PWR_LVL_P3;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -3) powerLevel = ESP_PWR_LVL_N3;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -6) powerLevel = ESP_PWR_LVL_N6;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -9) powerLevel = ESP_PWR_LVL_N9;
else if(_preferences->getInt(preference_ble_tx_power, 9) >= -12) powerLevel = ESP_PWR_LVL_N12;
_nukiLock.setPower(powerLevel);
_nukiLock.registerBleScanner(_bleScanner);
_intervalLockstate = _preferences->getInt(preference_query_interval_lockstate);
@@ -75,7 +87,7 @@ void NukiWrapper::initialize(const bool& firstStart)
Log->println("First start, setting preference defaults");
_preferences->putBool(preference_network_wifi_fallback_disabled, false);
_preferences->putBool(preference_find_best_rssi, false);
_preferences->putBool(preference_check_updates, true);
_preferences->putBool(preference_check_updates, true);
_preferences->putBool(preference_opener_continuous_mode, false);
_preferences->putBool(preference_network_wifi_fallback_disabled, false);
_preferences->putBool(preference_official_hybrid, false);
@@ -83,7 +95,7 @@ void NukiWrapper::initialize(const bool& firstStart)
_preferences->putBool(preference_official_hybrid_retry, false);
_preferences->putBool(preference_disable_non_json, false);
_preferences->putBool(preference_update_from_mqtt, false);
_preferences->putBool(preference_ip_dhcp_enabled, true);
_preferences->putBool(preference_ip_dhcp_enabled, true);
_preferences->putBool(preference_enable_bootloop_reset, false);
_preferences->putBool(preference_show_secrets, false);
@@ -105,7 +117,7 @@ void NukiWrapper::initialize(const bool& firstStart)
_preferences->putInt(preference_task_size_nuki, NUKI_TASK_SIZE);
_preferences->putInt(preference_authlog_max_entries, MAX_AUTHLOG);
_preferences->putInt(preference_keypad_max_entries, MAX_KEYPAD);
_preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL);
_preferences->putInt(preference_timecontrol_max_entries, MAX_TIMECONTROL);
_preferences->putInt(preference_query_interval_hybrid_lockstate, 600);
_preferences->putInt(preference_rssi_publish_interval, 60);
_preferences->putInt(preference_network_timeout, 60);
@@ -171,8 +183,9 @@ void NukiWrapper::initialize(const bool& firstStart)
_restartBeaconTimeout = -1;
_preferences->putInt(preference_restart_ble_beacon_lost, _restartBeaconTimeout);
}
_nukiLock.setEventHandler(this);
_nukiLock.setConnectTimeout(3);
_nukiLock.setDisconnectTimeout(5000);
Log->print(F("Lock state interval: "));
@@ -232,99 +245,28 @@ void NukiWrapper::update()
_nukiLock.updateConnectionState();
if(_statusUpdated || _nextLockStateUpdateTs == 0 || ts >= _nextLockStateUpdateTs || (queryCommands & QUERY_COMMAND_LOCKSTATE) > 0)
{
Log->println("Updating Lock state based on timer or query");
_statusUpdated = false;
_nextLockStateUpdateTs = ts + _intervalLockstate * 1000;
updateKeyTurnerState();
_network->publishStatusUpdated(_statusUpdated);
}
if(networkInst->_offCommandExecutedTs>0 && ts >= networkInst->_offCommandExecutedTs)
{
nukiInst->_nextLockAction = networkInst->_offCommand;
networkInst->_offCommandExecutedTs = 0;
}
if(_nextBatteryReportTs == 0 || ts > _nextBatteryReportTs || (queryCommands & QUERY_COMMAND_BATTERY) > 0)
if(_nextLockAction != (NukiLock::LockAction)0xff)
{
Log->println("Updating Lock battery state based on timer or query");
_nextBatteryReportTs = ts + _intervalBattery * 1000;
updateBatteryState();
}
if(_nextConfigUpdateTs == 0 || ts > _nextConfigUpdateTs || (queryCommands & QUERY_COMMAND_CONFIG) > 0)
{
Log->println("Updating Lock config based on timer or query");
_nextConfigUpdateTs = ts + _intervalConfig * 1000;
updateConfig();
if(_hassEnabled && !_hassSetupCompleted)
_retryCount = 0;
Nuki::CmdResult cmdResult;
while(_retryCount < _nrOfRetries + 1 && cmdResult != Nuki::CmdResult::Success)
{
setupHASS();
}
}
if(_waitAuthLogUpdateTs != 0 && ts > _waitAuthLogUpdateTs)
{
_waitAuthLogUpdateTs = 0;
updateAuthData(true);
}
if(_waitKeypadUpdateTs != 0 && ts > _waitKeypadUpdateTs)
{
_waitKeypadUpdateTs = 0;
updateKeypad(true);
}
if(_waitTimeControlUpdateTs != 0 && ts > _waitTimeControlUpdateTs)
{
_waitTimeControlUpdateTs = 0;
updateTimeControl(true);
}
if(_hassEnabled && _configRead && _network->reconnected())
{
setupHASS();
}
if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs))
{
_nextRssiTs = ts + _rssiPublishInterval;
cmdResult = _nukiLock.lockAction(_nextLockAction, 0, 0);
char resultStr[15] = {0};
NukiLock::cmdResultToString(cmdResult, resultStr);
int rssi = _nukiLock.getRssi();
if(rssi != _lastRssi)
{
_network->publishRssi(rssi);
_lastRssi = rssi;
}
}
_network->publishCommandResult(resultStr);
if(_hasKeypad && _keypadEnabled && (_nextKeypadUpdateTs == 0 || ts > _nextKeypadUpdateTs || (queryCommands & QUERY_COMMAND_KEYPAD) > 0))
{
Log->println("Updating Lock keypad based on timer or query");
_nextKeypadUpdateTs = ts + _intervalKeypad * 1000;
updateKeypad(false);
}
Log->print(F("Lock action result: "));
Log->println(resultStr);
if(_nextLockAction != (NukiLock::LockAction)0xff && ts > _nextRetryTs)
{
Nuki::CmdResult cmdResult = _nukiLock.lockAction(_nextLockAction, 0, 0);
char resultStr[15] = {0};
NukiLock::cmdResultToString(cmdResult, resultStr);
_network->publishCommandResult(resultStr);
Log->print(F("Lock action result: "));
Log->println(resultStr);
if(cmdResult == Nuki::CmdResult::Success)
{
_retryCount = 0;
_nextLockAction = (NukiLock::LockAction) 0xff;
_network->publishRetry("--");
if(_intervalLockstate > 10)
{
_nextLockStateUpdateTs = ts + 10 * 1000;
}
}
else
{
if(_retryCount < _nrOfRetries)
if(cmdResult != Nuki::CmdResult::Success)
{
Log->print(F("Lock: Last command failed, retrying after "));
Log->print(_retryDelay);
@@ -335,22 +277,93 @@ void NukiWrapper::update()
_network->publishRetry(std::to_string(_retryCount + 1));
_nextRetryTs = (esp_timer_get_time() / 1000) + _retryDelay;
delay(_retryDelay);
++_retryCount;
}
else
postponeBleWatchdog();
}
if(cmdResult == Nuki::CmdResult::Success)
{
_nextLockAction = (NukiLock::LockAction) 0xff;
_network->publishRetry("--");
_retryCount = 0;
if(!_network->_offConnected) _statusUpdated = true; Log->println(F("Lock: updating status after action"));
_statusUpdatedTs = ts;
if(_intervalLockstate > 10) _nextLockStateUpdateTs = ts + 10 * 1000;
}
else
{
Log->println(F("Lock: Maximum number of retries exceeded, aborting."));
_network->publishRetry("failed");
_retryCount = 0;
_nextLockAction = (NukiLock::LockAction) 0xff;
}
}
if(_statusUpdated || _nextLockStateUpdateTs == 0 || ts >= _nextLockStateUpdateTs || (queryCommands & QUERY_COMMAND_LOCKSTATE) > 0)
{
Log->println("Updating Lock state based on status, timer or query");
_statusUpdated = false;
_nextLockStateUpdateTs = ts + _intervalLockstate * 1000;
updateKeyTurnerState();
_network->publishStatusUpdated(_statusUpdated);
}
if(!_statusUpdated)
{
if(_nextBatteryReportTs == 0 || ts > _nextBatteryReportTs || (queryCommands & QUERY_COMMAND_BATTERY) > 0)
{
Log->println("Updating Lock battery state based on timer or query");
_nextBatteryReportTs = ts + _intervalBattery * 1000;
updateBatteryState();
}
if(_nextConfigUpdateTs == 0 || ts > _nextConfigUpdateTs || (queryCommands & QUERY_COMMAND_CONFIG) > 0)
{
Log->println("Updating Lock config based on timer or query");
_nextConfigUpdateTs = ts + _intervalConfig * 1000;
updateConfig();
if(_hassEnabled && !_hassSetupCompleted)
{
Log->println(F("Lock: Maximum number of retries exceeded, aborting."));
_network->publishRetry("failed");
_retryCount = 0;
_nextRetryTs = 0;
_nextLockAction = (NukiLock::LockAction) 0xff;
setupHASS();
}
}
postponeBleWatchdog();
}
if(_waitAuthLogUpdateTs != 0 && ts > _waitAuthLogUpdateTs)
{
_waitAuthLogUpdateTs = 0;
updateAuthData(true);
}
if(_waitKeypadUpdateTs != 0 && ts > _waitKeypadUpdateTs)
{
_waitKeypadUpdateTs = 0;
updateKeypad(true);
}
if(_waitTimeControlUpdateTs != 0 && ts > _waitTimeControlUpdateTs)
{
_waitTimeControlUpdateTs = 0;
updateTimeControl(true);
}
if(_hassEnabled && _configRead && _network->reconnected())
{
setupHASS();
}
if(_rssiPublishInterval > 0 && (_nextRssiTs == 0 || ts > _nextRssiTs))
{
_nextRssiTs = ts + _rssiPublishInterval;
int rssi = _nukiLock.getRssi();
if(rssi != _lastRssi)
{
_network->publishRssi(rssi);
_lastRssi = rssi;
}
}
if(_hasKeypad && _keypadEnabled && (_nextKeypadUpdateTs == 0 || ts > _nextKeypadUpdateTs || (queryCommands & QUERY_COMMAND_KEYPAD) > 0))
{
Log->println("Updating Lock keypad based on timer or query");
_nextKeypadUpdateTs = ts + _intervalKeypad * 1000;
updateKeypad(false);
}
}
if(_clearAuthData)
{
Log->println("Clearing Lock auth data");
@@ -417,7 +430,7 @@ void NukiWrapper::updateKeyTurnerState()
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
_retryCount = 0;
Log->println("Querying lock state");
while(_retryCount < _nrOfRetries + 1)
@@ -426,7 +439,6 @@ void NukiWrapper::updateKeyTurnerState()
Log->print(_retryCount + 1);
Log->print("): ");
result =_nukiLock.requestKeyTurnerState(&_keyTurnerState);
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
@@ -455,19 +467,36 @@ void NukiWrapper::updateKeyTurnerState()
}
_retryLockstateCount = 0;
const NukiLock::LockState& lockState = _keyTurnerState.lockState;
if(_publishAuthData)
if(lockState != _lastKeyTurnerState.lockState) _statusUpdatedTs = esp_timer_get_time() / 1000;
if(lockState == NukiLock::LockState::Locked ||
lockState == NukiLock::LockState::Unlocked ||
lockState == NukiLock::LockState::Calibration ||
lockState == NukiLock::LockState::BootRun ||
lockState == NukiLock::LockState::MotorBlocked)
{
Log->println(F("Publishing auth data"));
updateAuthData(false);
Log->println(F("Done publishing auth data"));
}
if(_publishAuthData && (lockState == NukiLock::LockState::Locked || lockState == NukiLock::LockState::Unlocked))
{
Log->println(F("Publishing auth data"));
updateAuthData(false);
Log->println(F("Done publishing auth data"));
}
updateGpioOutputs();
}
else if(!_network->_offConnected && (esp_timer_get_time() / 1000) < _statusUpdatedTs + 10000)
{
_statusUpdated = true;
Log->println(F("Lock: Keep updating status on intermediate lock state"));
}
_network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState);
updateGpioOutputs();
char lockStateStr[20];
lockstateToString(_keyTurnerState.lockState, lockStateStr);
lockstateToString(lockState, lockStateStr);
Log->println(lockStateStr);
postponeBleWatchdog();
@@ -478,7 +507,7 @@ void NukiWrapper::updateBatteryState()
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
_retryCount = 0;
Log->println("Querying lock battery state");
while(_retryCount < _nrOfRetries + 1)
@@ -487,7 +516,7 @@ void NukiWrapper::updateBatteryState()
Log->print(_retryCount + 1);
Log->print("): ");
result = _nukiLock.requestBatteryReport(&_batteryReport);
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -519,7 +548,7 @@ void NukiWrapper::updateConfig()
Log->print(F("Saving Nuki ID to preferences ("));
Log->print(_nukiConfig.nukiId);
Log->print(" / ");
Log->print(uidString);
Log->print(uidString);
Log->println(")");
_preferences->putUInt(preference_nuki_id_lock, _nukiConfig.nukiId);
}
@@ -544,7 +573,7 @@ void NukiWrapper::updateConfig()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiLock.verifySecurityPin();
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -554,7 +583,7 @@ void NukiWrapper::updateConfig()
if(result != Nuki::CmdResult::Success)
{
Log->println(F("Nuki Lock PIN is invalid"));
if(pinStatus != 2) {
if(pinStatus != 2) {
_preferences->putInt(preference_lock_pin_status, 2);
}
}
@@ -615,7 +644,6 @@ void NukiWrapper::updateAuthData(bool retrieved)
if(!retrieved)
{
delay(250);
Nuki::CmdResult result = (Nuki::CmdResult)-1;
_retryCount = 0;
@@ -623,7 +651,7 @@ void NukiWrapper::updateAuthData(bool retrieved)
{
Log->print(F("Retrieve log entries: "));
result = _nukiLock.retrieveLogEntries(0, _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 1, false);
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -680,6 +708,12 @@ void NukiWrapper::updateKeypad(bool retrieved)
{
if(!_preferences->getBool(preference_keypad_info_enabled)) return;
if(!isPinValid())
{
Log->println(F("No valid Nuki Lock PIN set"));
return;
}
if(!retrieved)
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
@@ -689,7 +723,7 @@ void NukiWrapper::updateKeypad(bool retrieved)
{
Log->print(F("Querying lock keypad: "));
result = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -741,6 +775,12 @@ void NukiWrapper::updateTimeControl(bool retrieved)
{
if(!_preferences->getBool(preference_timecontrol_info_enabled)) return;
if(!isPinValid())
{
Log->println(F("No valid Nuki Lock PIN set"));
return;
}
if(!retrieved)
{
Nuki::CmdResult result = (Nuki::CmdResult)-1;
@@ -750,7 +790,7 @@ void NukiWrapper::updateTimeControl(bool retrieved)
{
Log->print(F("Querying lock time control: "));
result = _nukiLock.retrieveTimeControlEntries();
delay(250);
if(result != Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -986,6 +1026,7 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value)
{
_network->_offState = atoi(value);
_statusUpdated = true;
Log->println(F("Lock: Updating status on Hybrid state change"));
_network->publishStatusUpdated(_statusUpdated);
NukiLock::lockstateToString((NukiLock::LockState)_network->_offState, str);
_network->publishString(mqtt_topic_lock_state, str, true);
@@ -1002,6 +1043,7 @@ void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value)
{
_network->_offDoorsensorState = atoi(value);
_statusUpdated = true;
Log->println(F("Lock: Updating status on Hybrid door sensor state change"));
_network->publishStatusUpdated(_statusUpdated);
NukiLock::doorSensorStateToString((NukiLock::DoorSensorState)_network->_offDoorsensorState, str);
@@ -1350,7 +1392,6 @@ void NukiWrapper::onConfigUpdateReceived(const char *value)
else jsonResult[basicKeys[i]] = "invalidValue";
}
delay(250);
if(!cmdResult == Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -1635,7 +1676,6 @@ void NukiWrapper::onConfigUpdateReceived(const char *value)
else jsonResult[advancedKeys[j]] = "invalidValue";
}
delay(250);
if(!cmdResult == Nuki::CmdResult::Success) {
++_retryCount;
}
@@ -1794,8 +1834,8 @@ void NukiWrapper::onKeypadCommandReceived(const char *command, const uint &id, c
memcpy(&entry.name, name.c_str(), nameLen > 20 ? 20 : nameLen);
entry.code = codeInt;
result = _nukiLock.addKeypadEntry(entry);
delay(250);
Log->print("Add keypad code: "); Log->println((int)result);
Log->print("Add keypad code: ");
Log->println((int)result);
updateKeypad(false);
}
else if(strcmp(command, "delete") == 0)
@@ -1807,8 +1847,8 @@ void NukiWrapper::onKeypadCommandReceived(const char *command, const uint &id, c
}
result = _nukiLock.deleteKeypadEntry(id);
delay(250);
Log->print("Delete keypad code: "); Log->println((int)result);
Log->print("Delete keypad code: ");
Log->println((int)result);
updateKeypad(false);
}
else if(strcmp(command, "update") == 0)
@@ -1842,8 +1882,8 @@ void NukiWrapper::onKeypadCommandReceived(const char *command, const uint &id, c
entry.code = codeInt;
entry.enabled = enabled == 0 ? 0 : 1;
result = _nukiLock.updateKeypadEntry(entry);
delay(250);
Log->print("Update keypad code: "); Log->println((int)result);
Log->print("Update keypad code: ");
Log->println((int)result);
updateKeypad(false);
}
else if(strcmp(command, "--") == 0)
@@ -1959,7 +1999,6 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value)
if(idExists)
{
result = _nukiLock.deleteKeypadEntry(codeId);
delay(250);
Log->print(F("Delete keypad code: "));
Log->println((int)result);
}
@@ -2146,7 +2185,6 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value)
}
result = _nukiLock.addKeypadEntry(entry);
delay(250);
Log->print(F("Add keypad code: "));
Log->println((int)result);
}
@@ -2165,11 +2203,11 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value)
}
Nuki::CmdResult resultKp = _nukiLock.retrieveKeypadEntries(0, _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD));
delay(250);
bool foundExisting = false;
if(resultKp == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiLock::KeypadEntry> entries;
_nukiLock.getKeypadEntries(&entries);
@@ -2294,7 +2332,6 @@ void NukiWrapper::onKeypadJsonCommandReceived(const char *value)
}
result = _nukiLock.updateKeypadEntry(entry);
delay(250);
Log->print(F("Update keypad code: "));
Log->println((int)result);
}
@@ -2401,7 +2438,6 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
if(idExists)
{
result = _nukiLock.removeTimeControlEntry(entryId);
delay(250);
Log->print(F("Delete time control: "));
Log->println((int)result);
}
@@ -2461,7 +2497,6 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
entry.lockAction = timeControlLockAction;
result = _nukiLock.addTimeControlEntry(entry);
delay(250);
Log->print(F("Add time control: "));
Log->println((int)result);
}
@@ -2474,11 +2509,11 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
}
Nuki::CmdResult resultTc = _nukiLock.retrieveTimeControlEntries();
delay(250);
bool foundExisting = false;
if(resultTc == Nuki::CmdResult::Success)
{
delay(250);
std::list<NukiLock::TimeControlEntry> timeControlEntries;
_nukiLock.getTimeControlEntries(&timeControlEntries);
@@ -2525,7 +2560,6 @@ void NukiWrapper::onTimeControlCommandReceived(const char *value)
entry.lockAction = timeControlLockAction;
result = _nukiLock.updateTimeControlEntry(entry);
delay(250);
Log->print(F("Update time control: "));
Log->println((int)result);
}
@@ -2590,6 +2624,7 @@ void NukiWrapper::notify(Nuki::EventType eventType)
{
Log->println("KeyTurnerStatusUpdated");
_statusUpdated = true;
_statusUpdatedTs = esp_timer_get_time() / 1000;
_network->publishStatusUpdated(_statusUpdated);
}
}
@@ -2604,11 +2639,8 @@ void NukiWrapper::readConfig()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiLock.requestConfig(&_nukiConfig);
delay(250);
_nukiConfigValid = result == Nuki::CmdResult::Success;
Log->print(F("Config valid: "));
Log->println(_nukiConfigValid);
if(!_nukiConfigValid) {
++_retryCount;
Log->println("Retrying in 1s");
@@ -2618,6 +2650,7 @@ void NukiWrapper::readConfig()
char resultStr[20];
NukiLock::cmdResultToString(result, resultStr);
Log->print(F("Lock config result: "));
Log->println(resultStr);
}
@@ -2629,7 +2662,6 @@ void NukiWrapper::readAdvancedConfig()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiLock.requestAdvancedConfig(&_nukiAdvancedConfig);
delay(250);
_nukiAdvancedConfigValid = result == Nuki::CmdResult::Success;
if(!_nukiAdvancedConfigValid) {
@@ -2640,6 +2672,7 @@ void NukiWrapper::readAdvancedConfig()
char resultStr[20];
NukiLock::cmdResultToString(result, resultStr);
Log->print(F("Lock advanced config result: "));
Log->println(resultStr);
}
@@ -2675,7 +2708,6 @@ void NukiWrapper::disableHASS()
while(_retryCount < _nrOfRetries + 1)
{
result = _nukiLock.requestConfig(&_nukiConfig);
delay(250);
_nukiConfigValid = result == Nuki::CmdResult::Success;
if(!_nukiConfigValid) {

View File

@@ -127,6 +127,7 @@ private:
int _retryConfigCount = 0;
int _retryLockstateCount = 0;
int _rssiPublishInterval = 0;
int64_t _statusUpdatedTs = 0;
int64_t _nextRetryTs = 0;
int64_t _nextLockStateUpdateTs = 0;
int64_t _nextHybridLockStateUpdateTs = 0;

View File

@@ -97,6 +97,7 @@
#define preference_ota_updater_url (char*)"otaUpdUrl"
#define preference_update_from_mqtt (char*)"updMqtt"
#define preference_show_secrets (char*)"showSecr"
#define preference_ble_tx_power (char*)"bleTxPwr"
#define preference_recon_netw_on_mqtt_discon (char*)"recNtwMqttDis"
inline bool initPreferences(Preferences* preferences)
@@ -250,7 +251,7 @@ private:
preference_official_hybrid, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, preference_has_mac_saved,
preference_has_mac_byte_0, preference_has_mac_byte_1, preference_has_mac_byte_2, preference_latest_version, preference_task_size_network, preference_task_size_nuki,
preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries, preference_update_from_mqtt, preference_show_secrets,
preference_recon_netw_on_mqtt_discon
preference_ble_tx_power, preference_recon_netw_on_mqtt_discon
};
std::vector<char*> _redact =
{
@@ -280,7 +281,8 @@ private:
preference_rssi_publish_interval, preference_network_timeout, preference_restart_ble_beacon_lost, preference_query_interval_lockstate,
preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_command_nr_of_retries,
preference_command_retry_delay, preference_presence_detection_timeout, preference_query_interval_hybrid_lockstate, preference_latest_version,
preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries
preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries,
preference_ble_tx_power
};
std::vector<char*> _charPrefs =
{

View File

@@ -766,7 +766,7 @@ void WebCfgServer::sendSettings()
}
}
if(pairing && _preferences->getBool(preference_show_secrets))
if(pairing)
{
if(_nuki != nullptr)
{
@@ -1119,6 +1119,14 @@ bool WebCfgServer::processArgs(String& message)
_preferences->putInt(preference_command_retry_delay, value.toInt());
configChanged = true;
}
else if(key == "TXPWR")
{
if(value.toInt() >= -12 && value.toInt() <= 9)
{
_preferences->putInt(preference_ble_tx_power, value.toInt());
configChanged = true;
}
}
#if PRESENCE_DETECTION_ENABLED
else if(key == "PRDTMO")
{
@@ -2530,6 +2538,8 @@ void WebCfgServer::buildNukiConfigHtml(String &response)
printInputField(response, "PRDTMO", "Presence detection timeout (seconds; -1 to disable)", _preferences->getInt(preference_presence_detection_timeout), 10, "");
#endif
printInputField(response, "RSBC", "Restart if bluetooth beacons not received (seconds; -1 to disable)", _preferences->getInt(preference_restart_ble_beacon_lost), 10, "");
printInputField(response, "TXPWR", "BLE transmit power in dB (minimum -12, maximum 9)", _preferences->getInt(preference_ble_tx_power, 9), 10, "");
response.concat("</table>");
response.concat("<br><input type=\"submit\" name=\"submit\" value=\"Save\">");
response.concat("</form>");

View File

@@ -74,14 +74,19 @@ TaskHandle_t networkTaskHandle = nullptr;
void networkTask(void *pvParameters)
{
int64_t networkLoopTs = 0;
bool secrets = preferences->getBool(preference_show_secrets);
while(true)
{
int64_t ts = (esp_timer_get_time() / 1000);
if(ts > 120000 && ts < 125000 && bootloopCounter > 0)
if(ts > 120000 && ts < 125000)
{
bootloopCounter = (int8_t)0;
Log->println(F("Bootloop counter reset"));
if(secrets) preferences->putBool(preference_show_secrets, false);
if(bootloopCounter > 0)
{
bootloopCounter = (int8_t)0;
Log->println(F("Bootloop counter reset"));
}
}
bool connected = network->update();
@@ -99,13 +104,13 @@ void networkTask(void *pvParameters)
#else
webCfgServer->update();
#endif
if((esp_timer_get_time() / 1000) - networkLoopTs > 120000)
{
Log->println("networkTask is running");
networkLoopTs = esp_timer_get_time() / 1000;
}
esp_task_wdt_reset();
delay(100);
@@ -116,7 +121,8 @@ void networkTask(void *pvParameters)
void nukiTask(void *pvParameters)
{
int64_t nukiLoopTs = 0;
bool whiteListed = false;
while(true)
{
bleScanner->update();
@@ -128,6 +134,20 @@ void nukiTask(void *pvParameters)
{
delay(5000);
}
#ifndef PRESENCE_DETECTION_ENABLED
else if (!whiteListed)
{
whiteListed = true;
if(lockEnabled)
{
bleScanner->whitelist(nuki->getBleAddress());
}
if(openerEnabled)
{
bleScanner->whitelist(nukiOpener->getBleAddress());
}
}
#endif
if(lockEnabled)
{
@@ -137,13 +157,13 @@ void nukiTask(void *pvParameters)
{
nukiOpener->update();
}
if((esp_timer_get_time() / 1000) - nukiLoopTs > 120000)
{
Log->println("nukiTask is running");
nukiLoopTs = esp_timer_get_time() / 1000;
}
esp_task_wdt_reset();
}
}
@@ -274,7 +294,7 @@ void otaTask(void *pvParameter)
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
esp_task_wdt_reset();
}
#endif
@@ -282,7 +302,7 @@ void otaTask(void *pvParameter)
void setupTasks(bool ota)
{
// configMAX_PRIORITIES is 25
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0))
esp_task_wdt_init(300, true);
#else
@@ -313,7 +333,7 @@ void setupTasks(bool ota)
xTaskCreatePinnedToCore(networkTask, "ntw", preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), NULL, 3, &networkTaskHandle, 1);
esp_task_wdt_add(networkTaskHandle);
#ifndef NUKI_HUB_UPDATER
xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 1);
xTaskCreatePinnedToCore(nukiTask, "nuki", preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), NULL, 2, &nukiTaskHandle, 0);
esp_task_wdt_add(nukiTaskHandle);
#endif
}
@@ -349,7 +369,7 @@ void setup()
uint8_t partitionType = checkPartition();
initializeRestartReason();
#ifndef NUKI_HUB_UPDATER
if(preferences->getBool(preference_enable_bootloop_reset, false))
{
@@ -393,7 +413,7 @@ void setup()
// Scan interval and window according to Nuki recommendations:
// https://developer.nuki.io/t/bluetooth-specification-questions/1109/27
bleScanner->initialize("NukiHub", true, 40, 40);
bleScanner->setScanDuration(10);
bleScanner->setScanDuration(0);
#if PRESENCE_DETECTION_ENABLED
if(preferences->getInt(preference_presence_detection_timeout) >= 0)