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

@@ -22,8 +22,8 @@ Feel free to join us on Discord: https://discord.gg/9nPq85bP4p
<b>Supported ESP32 devices:</b>
- Nuki Hub is compiled against all ESP32 models with Wi-Fi and Bluetooh Low Energy (BLE) which are supported by ESP-IDF 5.1.4 and Arduino Core 3.0.1.
- Tested stable builds are provided for the ESP32, ESP32-S3 and ESP32-C3.
- Support for the ESP32-C6 is ***HIGHLY*** experimental. Expect frequent crashes, especially when running Nuki Hub paired as an app (when not using in Hybrid mode). Pairing is also not supported yet and needs to be done manually on the /advanced page of the web configurator.
- The ESP32-Solo1 is not supported by ESP-IDF 5.1 and as such can't be build using Arduino Core 3 and ESP-IDF 5.1. Untested build against Arduino Core 2.0.14 and ESP-IDF 4.4 are provided.
- Support for the ESP32-C6 is experimental. There could be more frequent crashes than on other ESP32 devices and connections with the Nuki device could be slower than on other ESP32 devices.
- The ESP32-Solo1 is not supported by ESP-IDF 5.1 and as such can't be build using Arduino Core 3 and ESP-IDF 5.1. Untested builds against Arduino Core 2.0.14 and ESP-IDF 4.4 are provided.
<b>Not supported ESP32 devices:</b>
- The ESP32-S2 has no BLE and as such can't run Nuki Hub.
@@ -165,7 +165,7 @@ In a browser navigate to the IP address assigned to the ESP32.
- Lock: Nuki Bridge is running alongside Nuki Hub: Enable to allow Nuki Hub to co-exist with a Nuki Bridge by registering Nuki Hub as an (smartphone) app instead of a bridge. Changing this setting will require re-pairing. Enabling this setting is strongly discouraged as described in the "[Pairing with a Nuki Lock or Opener](#pairing-with-a-nuki-lock-or-opener)" section of this README, ***unless when used in [Hybrid mode](/HYBRID.md) (Official MQTT / Nuki Hub co-existance)***
- Opener: Nuki Bridge is running alongside Nuki Hub: Enable to allow Nuki Hub to co-exist with a Nuki Bridge by registering Nuki Hub as an (smartphone) app instead of a bridge. Changing this setting will require re-pairing. Enabling this setting is strongly discouraged as described in the "[Pairing with a Nuki Lock or Opener](#pairing-with-a-nuki-lock-or-opener)" section of this README
- Restart if bluetooth beacons not received: Set to a positive integer to restart the Nuki Hub after the set amount of seconds has passed without receiving a bluetooth beacon from the Nuki device, set to -1 to disable, default 60. Because the bluetooth stack of the ESP32 can silently fail it is not recommended to disable this setting.
- BLE transmit power in dB: Set to a integer between -12 and 9 to set the Bluetooth transmit power, default 9.
### Access Level Configuration
#### Nuki General Access Control

View File

@@ -30,6 +30,7 @@ void Scanner::initialize(const std::string& deviceName, const bool wantDuplicate
}
BLEDevice::init(deviceName);
}
bleScan = BLEDevice::getScan();
#ifndef BLESCANNER_USE_LATEST_NIMBLE
@@ -98,4 +99,9 @@ void Scanner::onResult(NimBLEAdvertisedDevice* advertisedDevice) {
}
}
void Scanner::whitelist(BLEAddress bleAddress) {
BLEDevice::whiteListAdd(bleAddress);
bleScan->setFilterPolicy(BLE_HCI_SCAN_FILT_USE_WL);
}
} // namespace BleScanner

View File

@@ -84,6 +84,14 @@ class Scanner : public Publisher, BLEAdvertisedDeviceCallbacks {
* @param advertisedDevice
*/
void onResult(NimBLEAdvertisedDevice* advertisedDevice) override;
/**
* @brief Whitelist a specific BLE Address
*
* @param whiteListBleAddress
*/
void whitelist(BLEAddress bleAddress);
private:
uint32_t scanDuration = 0; //default indefinite scanning time

View File

@@ -295,8 +295,6 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
if(isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response");
disconnect();
NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
ble_gap_conn_cancel();
} else {
// workaround; if the controller doesn't cancel the connection
// at the timeout, cancel it here.

View File

@@ -13,7 +13,7 @@ default_envs = esp32dev
boards_dir = boards
[env]
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.07.11/platform-espressif32.zip
platform_packages =
framework = arduino, espidf
build_type = release
@@ -25,13 +25,13 @@ build_unflags =
-Werror=all
-Wall
build_flags =
-fexceptions
-DTLS_CA_MAX_SIZE=2200
-DTLS_CERT_MAX_SIZE=1500
-DTLS_KEY_MAX_SIZE=1800
-DESP_PLATFORM
-DESP32
-DARDUINO_ARCH_ESP32
-fexceptions
-DTLS_CA_MAX_SIZE=2200
-DTLS_CERT_MAX_SIZE=1500
-DTLS_KEY_MAX_SIZE=1800
-DESP_PLATFORM
-DESP32
-DARDUINO_ARCH_ESP32
-DCONFIG_BTDM_BLE_SCAN_DUPL=y
-DCONFIG_ASYNC_TCP_MAX_ACK_TIME=3000
-DCONFIG_ASYNC_TCP_PRIORITY=10
@@ -63,14 +63,15 @@ extra_scripts =
pre:pio_package_pre.py
post:pio_package.py
build_flags =
${env.build_flags}
${env.build_flags}
-DNUKI_ALT_CONNECT
-DBLESCANNER_USE_LATEST_NIMBLE
-DNUKI_USE_LATEST_NIMBLE
-DNUKI_NO_WDT_RESET
-DNUKI_MUTEX_RECURSIVE
-DNUKI_64BIT_TIME
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_BT_NIMBLE_LOG_LEVEL=0
[env:esp32-c3]
@@ -93,11 +94,12 @@ extra_scripts =
pre:pio_package_pre.py
post:pio_package.py
build_flags =
${env.build_flags}
-DFRAMEWORK_ARDUINO_SOLO1
${env.build_flags}
-DFRAMEWORK_ARDUINO_SOLO1
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_BT_NIMBLE_LOG_LEVEL=0
-DNUKI_ALT_CONNECT
-DNUKI_64BIT_TIME
lib_deps =
BleScanner=symlink://lib/BleScanner
@@ -111,99 +113,104 @@ lib_ignore =
extends = env:esp32dev
custom_build = debug
build_flags =
${env.build_flags}
${env.build_flags}
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_BT_NIMBLE_LOG_LEVEL=0
-DNUKI_ALT_CONNECT
-DBLESCANNER_USE_LATEST_NIMBLE
-DNUKI_USE_LATEST_NIMBLE
-DNUKI_NO_WDT_RESET
-DNUKI_MUTEX_RECURSIVE
-DNUKI_64BIT_TIME
-DDEBUG_NUKIHUB
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
[env:esp32-c3_dbg]
extends = env:esp32-c3
custom_build = debug
build_flags =
${env.build_flags}
${env.build_flags}
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_BT_NIMBLE_LOG_LEVEL=0
-DNUKI_ALT_CONNECT
-DBLESCANNER_USE_LATEST_NIMBLE
-DNUKI_USE_LATEST_NIMBLE
-DNUKI_NO_WDT_RESET
-DNUKI_MUTEX_RECURSIVE
-DNUKI_64BIT_TIME
-DDEBUG_NUKIHUB
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
[env:esp32-c6_dbg]
extends = env:esp32-c6
custom_build = debug
build_flags =
${env.build_flags}
${env.build_flags}
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_BT_NIMBLE_LOG_LEVEL=0
-DNUKI_ALT_CONNECT
-DBLESCANNER_USE_LATEST_NIMBLE
-DNUKI_USE_LATEST_NIMBLE
-DNUKI_NO_WDT_RESET
-DNUKI_MUTEX_RECURSIVE
-DNUKI_64BIT_TIME
-DDEBUG_NUKIHUB
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
[env:esp32-s3_dbg]
extends = env:esp32-s3
custom_build = debug
build_flags =
${env.build_flags}
${env.build_flags}
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_BT_NIMBLE_LOG_LEVEL=0
-DNUKI_ALT_CONNECT
-DBLESCANNER_USE_LATEST_NIMBLE
-DNUKI_USE_LATEST_NIMBLE
-DNUKI_NO_WDT_RESET
-DNUKI_MUTEX_RECURSIVE
-DNUKI_64BIT_TIME
-DDEBUG_NUKIHUB
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
[env:esp32solo1_dbg]
extends = env:esp32solo1
custom_build = debug
build_flags =
${env.build_flags}
${env.build_flags}
-DFRAMEWORK_ARDUINO_SOLO1
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
-DNUKI_ALT_CONNECT
-DNUKI_64BIT_TIME
-DDEBUG_NUKIHUB
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA
-DDEBUG_SENSE_NUKI
-DDEBUG_NUKI_COMMAND
-DDEBUG_NUKI_CONNECT
-DDEBUG_NUKI_COMMUNICATION
;-DDEBUG_NUKI_HEX_DATA
-DDEBUG_NUKI_READABLE_DATA

View File

@@ -14,6 +14,10 @@ CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y
CONFIG_BT_NIMBLE_LOG_LEVEL=0
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=8
CONFIG_BT_NIMBLE_MAX_BONDS=8
CONFIG_BT_NIMBLE_NVS_PERSIST=y
CONFIG_BT_NIMBLE_GATT_MAX_PROCS=8
CONFIG_NIMBLE_PINNED_TO_CORE_0=y
CONFIG_NIMBLE_PINNED_TO_CORE=0
CONFIG_NIMBLE_TASK_STACK_SIZE=8192
@@ -37,6 +41,7 @@ CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30
CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8
CONFIG_BTDM_CTRL_MODEM_SLEEP=n
CONFIG_NIMBLE_HS_FLOW_CTRL=y
CONFIG_NIMBLE_HS_FLOW_CTRL_ITVL=1000
CONFIG_NIMBLE_HS_FLOW_CTRL_THRESH=2
@@ -44,8 +49,9 @@ CONFIG_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT=y
CONFIG_NIMBLE_RPA_TIMEOUT=900
CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=3
CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=3
CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=8
CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=8
CONFIG_BT_ACL_CONNECTIONS=8
CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0
CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0
CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0
@@ -64,4 +70,5 @@ CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n
CONFIG_IEEE802154_ENABLED=n
CONFIG_ARDUINO_SELECTIVE_COMPILATION=y
CONFIG_ARDUINO_SELECTIVE_HTTPClient=n
CONFIG_ARDUINO_SELECTIVE_WebServer=n
CONFIG_ARDUINO_SELECTIVE_WebServer=n
CONFIG_HEAP_TASK_TRACKING=n

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)

View File

@@ -13,7 +13,7 @@ default_envs = updater_esp32dev
boards_dir = ../boards
[env]
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.07.11/platform-espressif32.zip
platform_packages =
framework = arduino, espidf
build_type = release