From 49f6e5db0662f17d5ea4eeb017c1255982319570 Mon Sep 17 00:00:00 2001 From: iranl Date: Sun, 12 Jan 2025 21:18:58 +0100 Subject: [PATCH 1/5] Nuki Bluetooth API 2.3.0 --- lib/nuki_ble | 2 +- src/Config.h | 2 +- src/NukiWrapper.cpp | 5 +++++ src/PreferencesKeys.h | 6 ++++-- src/WebCfgServer.cpp | 22 ++++++++++++++++++++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/lib/nuki_ble b/lib/nuki_ble index fe06545e..48a755f4 160000 --- a/lib/nuki_ble +++ b/lib/nuki_ble @@ -1 +1 @@ -Subproject commit fe06545ee4e0f9605596493d641dd1ab4f2dacda +Subproject commit 48a755f49112979665bae1616150f90944197384 diff --git a/src/Config.h b/src/Config.h index 4aeb310b..896e40eb 100644 --- a/src/Config.h +++ b/src/Config.h @@ -5,7 +5,7 @@ #define NUKI_HUB_VERSION "9.08" #define NUKI_HUB_VERSION_INT (uint32_t)908 #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2025-01-10" +#define NUKI_HUB_DATE "2025-01-13" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 80bcbcf1..07ed347b 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -59,6 +59,11 @@ void NukiWrapper::initialize() _nukiLock.setDebugHexData(_preferences->getBool(preference_debug_hex_data, false)); _nukiLock.setDebugCommand(_preferences->getBool(preference_debug_command, false)); _nukiLock.registerLogger(Log); + + if (_preferences->getInt(preference_lock_gemini_pin, 0) > 0) + { + _nukiLock.saveUltraPincode(_preferences->getInt(preference_lock_gemini_pin, 0), false); + } _nukiLock.initialize(_preferences->getBool(preference_connect_mode, true)); _nukiLock.registerBleScanner(_bleScanner); diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index 7277beb4..b11b3f8b 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -73,6 +73,7 @@ #define preference_http_auth_type (char*)"httpdAuthType" #define preference_update_time (char*)"updateTime" #define preference_mqtt_ssl_enabled (char*)"mqttSSLena" +#define preference_lock_gemini_pin (char*)"geminiPin" // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT #define preference_find_best_rssi (char*)"nwbestrssi" @@ -473,7 +474,7 @@ class DebugPreferences preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_update_time, preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, - preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type, + preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type, preference_lock_gemini_pin, preference_rssi_publish_interval, preference_hostname, preference_network_timeout, preference_restart_on_disconnect, preference_hybrid_reboot_on_disconnect, preference_restart_ble_beacon_lost, preference_query_interval_lockstate, preference_timecontrol_topic_per_entry, preference_keypad_topic_per_entry, preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, @@ -493,7 +494,8 @@ class DebugPreferences }; std::vector _redact = { - preference_mqtt_user, preference_mqtt_password, preference_cred_user, preference_cred_password, preference_nuki_id_lock, preference_nuki_id_opener, preference_wifi_pass + preference_mqtt_user, preference_mqtt_password, preference_cred_user, preference_cred_password, preference_nuki_id_lock, preference_nuki_id_opener, preference_wifi_pass, + preference_lock_gemini_pin }; std::vector _boolPrefs = { diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 92087dd1..cfcde2e1 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -1482,12 +1482,16 @@ esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* r unsigned char authorizationId[4] = {0x00}; unsigned char secretKeyK[32] = {0x00}; uint16_t storedPincode = 0000; + uint32_t storedUltraPincode = 000000; + bool isUltra = false; Preferences nukiBlePref; nukiBlePref.begin("NukiHub", false); nukiBlePref.getBytes("bleAddress", currentBleAddress, 6); nukiBlePref.getBytes("secretKeyK", secretKeyK, 32); nukiBlePref.getBytes("authorizationId", authorizationId, 4); nukiBlePref.getBytes("securityPinCode", &storedPincode, 2); + nukiBlePref.getBytes("ultraPinCode", &storedUltraPincode, 4); + isUltra = nukiBlePref.getBool("isUltra", false); nukiBlePref.end(); char text[255]; text[0] = '\0'; @@ -1515,6 +1519,8 @@ esp_err_t WebCfgServer::sendSettings(PsychicRequest *request, PsychicResponse* r json["authorizationIdLock"] = text; memset(text, 0, sizeof(text)); json["securityPinCodeLock"] = storedPincode; + json["ultraPinCodeLock"] = storedUltraPincode; + json["isUltra"] = isUltra ? "1" : "0"; } if(_nukiOpener != nullptr) { @@ -1607,6 +1613,8 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S unsigned char authorizationId[4] = {0x00}; unsigned char secretKeyK[32] = {0x00}; unsigned char pincode[2] = {0x00}; + unsigned char ultraPincode[4] = {0x00}; + bool isUltra = false; unsigned char currentBleAddressOpn[6]; unsigned char authorizationIdOpn[4] = {0x00}; unsigned char secretKeyKOpn[32] = {0x00}; @@ -2579,6 +2587,16 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S configChanged = true; } } + else if(key == "GEMINIPIN") + { + if(_preferences->getInt(preference_lock_gemini_pin, 0) != value.toInt()) + { + _preferences->putInt(preference_lock_gemini_pin, value.toInt()); + Log->print(F("Setting changed: ")); + Log->println(key); + configChanged = true; + } + } else if(key == "LCKFORCEID") { if(_preferences->getBool(preference_lock_force_id, false) != (value == "1")) @@ -3317,6 +3335,9 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S nukiBlePref.putBytes("secretKeyK", secretKeyK, 32); nukiBlePref.putBytes("authorizationId", authorizationId, 4); nukiBlePref.putBytes("securityPinCode", pincode, 2); + nukiBlePref.putBytes("ultraPinCode", ultraPincode, 4); + nukiBlePref.putBool("isUltra", isUltra); + nukiBlePref.end(); Log->print(("Setting changed: ")); Log->println("Lock pairing data"); @@ -4356,6 +4377,7 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic printCheckBox(&response, "DBGREAD", "Enable Nuki readable data debug logging", _preferences->getBool(preference_debug_readable_data, false), ""); printCheckBox(&response, "DBGHEX", "Enable Nuki hex data debug logging", _preferences->getBool(preference_debug_hex_data, false), ""); printCheckBox(&response, "DBGCOMM", "Enable Nuki command debug logging", _preferences->getBool(preference_debug_command, false), ""); + printInputField(&response, "GEMINIPIN", "SmartLock Ultra PIN", _preferences->getInt(preference_lock_gemini_pin, 0), 6, ""); response.print(""); From 7cb31f8c2273ac9f57f31f95987dde487803fc43 Mon Sep 17 00:00:00 2001 From: iranl Date: Tue, 14 Jan 2025 18:25:46 +0100 Subject: [PATCH 2/5] Add Ultra --- HYBRID.md | 8 +- README.md | 30 +++-- lib/nuki_ble | 2 +- src/Config.h | 2 +- src/Gpio.cpp | 36 ------ src/Gpio.h | 2 - src/HomeAssistantDiscovery.cpp | 49 +++++++- src/NukiNetwork.cpp | 6 - src/NukiNetworkLock.cpp | 76 ++++++++----- src/NukiNetworkLock.h | 2 + src/NukiWrapper.cpp | 83 +++++++++++++- src/NukiWrapper.h | 6 +- src/PreferencesKeys.h | 24 ++-- src/WebCfgServer.cpp | 197 +++++++++++++++++++++++++-------- 14 files changed, 377 insertions(+), 146 deletions(-) diff --git a/HYBRID.md b/HYBRID.md index 2bb49f4c..cba86698 100644 --- a/HYBRID.md +++ b/HYBRID.md @@ -4,9 +4,9 @@ The purpose of this mode is to have Nuki Hub work in conjunction with the offici ### Requirements ### -- ESP32 running Nuki Hub 8.35 or higher -- For WiFi: Nuki lock 3.0 Pro or Nuki Lock 4.0 Pro -- For Thread: Nuki Lock 4.0 or Nuki Lock 4.0 Pro. Note that you do ***NOT*** need to buy the remote access addon for the Nuki Lock 4.0 +- ESP32 running Nuki Hub 9.08 or higher +- For WiFi: Nuki lock 3.0 Pro, Nuki Lock 4.0 Pro or Nuki Lock Ultra +- For Thread: Nuki Lock 4.0, Nuki Lock 4.0 Pro or Nuki Lock Ultra. Note that you do ***NOT*** need to buy the remote access addon for the Nuki Lock 4.0 - For Thread: The Nuki Lock needs to have network access to the same MQTT server as the one that Nuki Hub is conected to. Depending if the MQTT server is reachable over IPv6 you might need an OpenThread Border router that supports NAT64 and has this enabled. Currently this means an Apple Device or Home Assistant with the Matter server and OpenThread Border Router - The Nuki Opener does not have WiFI or Thread and thus doesn't benefit from the hybrid solutions added speed. You can however use and connect a Nuki Opener as usual which will function over regular BLE and can still connect Nuki Hub as a bridge to an Opener. @@ -38,7 +38,7 @@ The Hybrid Official MQTT over Thread + Nuki Hub solution allows for the best com - Optionally enable `Allow locking`. Note that if you enable this setting it is preferred to set ACL on your MQTT broker to only allow the Nuki lock and Nuki Hub MQTT user access to the topic `nuki/NUKI-ID/lockAction` to make sure that only Nuki Hub can execute commands on the lock (otherwise ACL settings through Nuki Hub can not be 100% enforced) - Make sure that MQTT is setup correctly by checking if you get a green check mark in the Nuki app -- Install Nuki Hub 8.35 or higher on a supported ESP32 device +- Install Nuki Hub 9.08 or higher on a supported ESP32 device - Make sure you are not paired as a bridge. Unpair your Nuki lock in Nuki Hub if Nuki Hub was paired as a bridge (this is mandatory even if you removed the bridge connection from the Nuki lock). - Enable `Enable hybrid official MQTT and Nuki Hub setup`. The `Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)` setting will be automatically be enabled. - Optionally enable `Enable sending actions through official MQTT`, if not enabled lock actions will be sent over BLE as usual (slower) diff --git a/README.md b/README.md index 6b73fb4c..ea0aabb9 100644 --- a/README.md +++ b/README.md @@ -28,15 +28,13 @@ Feel free to join us on Discord: https://discord.gg/9nPq85bP4p - Nuki Smart Lock 2.0 - Nuki Smart Lock 3.0 - Nuki Smart Lock 3.0 Pro (read FAQ below) -- Nuki Smart Lock 4.0 +- Nuki Smart Lock 4.0 (read FAQ below) - Nuki Smart Lock 4.0 Pro (read FAQ below) +- Nuki Smart Lock Ultra (read FAQ below) - Nuki Opener - Nuki Keypad 1.0 - Nuki Keypad 2.0 -Not (yet?) supported: -- Nuki Smart Lock Ultra - Supported Ethernet devices:
As an alternative to Wi-Fi (which is available on any supported ESP32), the following ESP32 modules with built-in wired ethernet are supported: - [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware) @@ -147,7 +145,7 @@ Next click on "MQTT Configuration" and enter the address and port (usually 1883) The firmware supports SSL encryption for MQTT, however most people don't use this.
See the "[MQTT Encryption](#mqtt-encryption-optional)" section of this README. -## Pairing with a Nuki Lock or Opener +## Pairing with a Nuki Lock (1.0-4.0) or Opener Make sure "Bluetooth pairing" is enabled for the Nuki device by enabling this setting in the official Nuki App in "Settings" > "Features & Configuration" > "Button and LED". After enabling the setting press the button on the Nuki device for a few seconds.
@@ -161,9 +159,22 @@ MQTT nodes like lock state and battery level should now reflect the reported val This is not recommended (unless when using [hybrid mode](/HYBRID.md)) and will lead to excessive battery drain and can lead to either device missing updates. Enable "Register as app" before pairing to allow this. Otherwise the Bridge will be unregistered when pairing the Nuki Hub. +## Pairing with a Nuki Lock Ultra + +Make sure "Bluetooth pairing" is enabled for the Nuki device by enabling this setting in the official Nuki App in "Settings" > "Features & Configuration" > "Button and LED". + +Before enabling pairing mode using the button on the Lock Ultra first setup NukiHub as follows: +- Enable both "Nuki Smartlock enabled" and "Nuki Smartlock Ultra enabled" settings on the "Basic Nuki Configuration" page and Save. Setting the "Nuki Smartlock Ultra enabled" will change multiple other NukiHub settings. +- Input your 6-digit Nuki Lock Ultra PIN on the "Credentials" page and Save +- Press the button on the Nuki device for a few seconds +- It is **strongly** recommended to setup and enable Hybrid mode over Thread/WiFi + official MQTT as NukiHub works best in Hybrid or Bridge mode and the Ultra does not support Bridge mode + +Pairing should be automatic if no lock is paired.
+When pairing is successful, the web interface should show "Paired: Yes".
+ ## Hybrid mode -Hybrid mode allows you to use the official Nuki MQTT implemenation on a Nuki Lock 3.0 Pro, Nuki Lock 4.0 or Nuki Lock 4.0 Pro in conjunction with Nuki Hub.
+Hybrid mode allows you to use the official Nuki MQTT implemenation on a Nuki Lock 3.0 Pro, Nuki Lock 4.0, Nuki Lock 4.0 Pro or Nuki Lock Ultra in conjunction with NukiHub.
See [hybrid mode](/HYBRID.md) for more information. ## Memory constraints @@ -251,7 +262,8 @@ In a browser navigate to the IP address assigned to the ESP32. #### Basic Nuki Configuration -- Nuki Smartlock enabled: Enable if you want Nuki Hub to connect to a Nuki Lock (1.0-4.0) +- Nuki Smartlock enabled: Enable if you want Nuki Hub to connect to a Nuki Lock (1.0-4.0 and Ultra) +- Nuki Smartlock Ultra enabled: Enable if you want Nuki Hub to connect to a Nuki Lock Ultra - Nuki Opener enabled: Enable if you want Nuki Hub to connect to a Nuki Opener - New Nuki Bluetooth connection mode (disable if there are connection issues): Enable to use the latest Nuki BLE connection mode (recommended). Disable if you have issues communicating with the lock/opener @@ -267,6 +279,7 @@ In a browser navigate to the IP address assigned to the ESP32. - 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 @@ -299,6 +312,7 @@ In a browser navigate to the IP address assigned to the ESP32. #### Nuki Lock PIN / Nuki Opener PIN - PIN Code: Fill with the Nuki Security Code of the Nuki Lock and/or Nuki Opener. Required for functions that require the security code to be sent to the lock/opener such as setting lock permissions/adding keypad codes, viewing the activity log or changing the Nuki device configuration. Set to "#" to remove the security code from the Nuki Hub configuration. +- PIN Code Ultra: Fill with the 6-digit Nuki Security Code of the Nuki Lock Ultra. Required for pairing (and many other functions) #### Unpair Nuki Lock / Unpair Nuki Opener @@ -484,6 +498,8 @@ Changing settings has to enabled first in the configuration portal. Check the se | autoLockEnabled | Whether auto lock is enabled. | 1 = enabled, 0 = disabled |`{ "autoLockEnabled": "1" }` | | immediateAutoLockEnabled | Whether auto lock should be performed immediately after the door has been closed. | 1 = enabled, 0 = disabled |`{ "immediateAutoLockEnabled": "1" }`| | autoUpdateEnabled | Whether automatic firmware updates should be enabled. | 1 = enabled, 0 = disabled |`{ "autoUpdateEnabled": "1" }` | +| motorSpeed | The desired motor speed (Ultra only) | "Standard", "Insane", "Gentle" |`{ "motorSpeed": "Standard" }` | +| enableSlowSpeedDuringNightMode | Whether the slow speed should be applied during Night Mode (Ultra only) | 1 = enabled, 0 = disabled |`{ "enableSlowSpeedDuringNightMode": "1" }` | | rebootNuki | Reboot the Nuki device immediately | 1 = reboot nuki |`{ "rebootNuki": "1" }` | ### Nuki Opener Configuration diff --git a/lib/nuki_ble b/lib/nuki_ble index 48a755f4..dc0696eb 160000 --- a/lib/nuki_ble +++ b/lib/nuki_ble @@ -1 +1 @@ -Subproject commit 48a755f49112979665bae1616150f90944197384 +Subproject commit dc0696ebc26fdea760495e5b8ae2ef288136287e diff --git a/src/Config.h b/src/Config.h index 896e40eb..a180fe91 100644 --- a/src/Config.h +++ b/src/Config.h @@ -5,7 +5,7 @@ #define NUKI_HUB_VERSION "9.08" #define NUKI_HUB_VERSION_INT (uint32_t)908 #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2025-01-13" +#define NUKI_HUB_DATE "2025-01-14" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/Gpio.cpp b/src/Gpio.cpp index 3c109c69..b40d88da 100644 --- a/src/Gpio.cpp +++ b/src/Gpio.cpp @@ -16,12 +16,6 @@ Gpio::Gpio(Preferences* preferences) { _inst = this; loadPinConfiguration(); - - if(_preferences->getBool(preference_gpio_locking_enabled, false)) - { - migrateObsoleteSetting(); - } - _inst->init(); } @@ -541,33 +535,3 @@ void Gpio::setPinOutput(const uint8_t& pin, const uint8_t& state) { digitalWrite(pin, state); } - -void Gpio::migrateObsoleteSetting() -{ - _pinConfiguration.clear(); - - PinEntry entry1; - entry1.pin = 27; - entry1.role = PinRole::InputUnlatch; - - PinEntry entry2; - entry2.pin = 32; - entry2.role = PinRole::InputLock; - - PinEntry entry3; - entry3.pin = 33; - entry3.role = PinRole::InputUnlock; - - _pinConfiguration.push_back(entry1); - _pinConfiguration.push_back(entry2); - _pinConfiguration.push_back(entry3); - - savePinConfiguration(_pinConfiguration); - - _preferences->remove(preference_gpio_locking_enabled); - Log->println("Migrated gpio control setting"); - delay(200); - restartEsp(RestartReason::GpioConfigurationUpdated); -} - - diff --git a/src/Gpio.h b/src/Gpio.h index be54d16d..b5cdf38c 100644 --- a/src/Gpio.h +++ b/src/Gpio.h @@ -59,8 +59,6 @@ class Gpio Gpio(Preferences* preferences); static void init(); - void migrateObsoleteSetting(); - void addCallback(std::function callback); void loadPinConfiguration(); diff --git a/src/HomeAssistantDiscovery.cpp b/src/HomeAssistantDiscovery.cpp index b039bfb2..6149bd1c 100644 --- a/src/HomeAssistantDiscovery.cpp +++ b/src/HomeAssistantDiscovery.cpp @@ -772,7 +772,7 @@ void HomeAssistantDiscovery::publishHASSConfigAdditionalLockEntities(char *devic _preferences->getBytes(preference_acl, &aclPrefs, sizeof(aclPrefs)); uint32_t basicLockConfigAclPrefs[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if(_preferences->getBool(preference_conf_info_enabled, true)) { @@ -1878,6 +1878,53 @@ void HomeAssistantDiscovery::publishHASSConfigAdditionalLockEntities(char *devic { removeHassTopic((char*)"switch", (char*)"auto_update_enabled", uidString); } + + // Motor speed + if((int)advancedLockConfigAclPrefs[23] == 1) + { + JsonDocument json; + json = createHassJson(uidString, "_motor_speed", "Motor speed", name, baseTopic, String("~") + mqtt_topic_config_advanced_json, deviceType, "", "", "config", String("~") + mqtt_topic_config_action, {{ (char*)"val_tpl", (char*)"{{value_json.motorSpeed}}" }, { (char*)"en", (char*)"true" }, { (char*)"cmd_tpl", (char*)"{ \"motorSpeed\": \"{{ value }}\" }" }}); + json["options"][0] = "Standard"; + json["options"][1] = "Insane"; + json["options"][2] = "Gentle"; + serializeJson(json, _buffer, _bufferSize); + String path = createHassTopicPath("select", "motor_speed", uidString); + _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, _buffer); + } + else + { + removeHassTopic((char*)"select", (char*)"motor_speed", uidString); + } + + if((int)advancedLockConfigAclPrefs[24] == 1) + { + // Slow speed during night mode enabled + publishHassTopic("switch", + "enable_slow_speed_during_nightmode", + uidString, + "_enable_slow_speed_during_nightmode", + "Enable slow speed during nightmode", + name, + baseTopic, + String("~") + mqtt_topic_config_advanced_json, + deviceType, + "", + "", + "config", + String("~") + mqtt_topic_config_action, + { + { (char*)"en", (char*)"true" }, + { (char*)"pl_on", (char*)"{ \"enableSlowSpeedDuringNightMode\": \"1\"}" }, + { (char*)"pl_off", (char*)"{ \"enableSlowSpeedDuringNightMode\": \"0\"}" }, + { (char*)"val_tpl", (char*)"{{value_json.enableSlowSpeedDuringNightMode}}" }, + { (char*)"stat_on", (char*)"1" }, + { (char*)"stat_off", (char*)"0" } + }); + } + else + { + removeHassTopic((char*)"switch", (char*)"enable_slow_speed_during_nightmode", uidString); + } if((int)advancedLockConfigAclPrefs[22] == 1) { diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index 8ffa17a8..c28d44e9 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -31,12 +31,6 @@ NukiNetwork::NukiNetwork(Preferences *preferences) : _preferences(preferences) #endif { - // Remove obsolete W5500 hardware detection configuration - if(_preferences->getInt(preference_network_hardware_gpio) != 0) - { - _preferences->remove(preference_network_hardware_gpio); - } - _inst = this; _webEnabled = _preferences->getBool(preference_webserver_enabled, true); diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 45e20058..fa61ba91 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -46,6 +46,7 @@ void NukiNetworkLock::initialize() _haEnabled = _preferences->getString(preference_mqtt_hass_discovery, "") != ""; _disableNonJSON = _preferences->getBool(preference_disable_non_json, false); _hybridRebootOnDisconnect = _preferences->getBool(preference_hybrid_reboot_on_disconnect, false); + _isUltra = _preferences->getBool(preference_lock_gemini_enabled, false); _network->initTopic(_mqttPath, mqtt_topic_lock_action, "--"); _network->subscribe(_mqttPath, mqtt_topic_lock_action); @@ -532,41 +533,41 @@ void NukiNetworkLock::publishKeyTurnerState(const NukiLock::KeyTurnerState& keyT json["door_sensor_state"] = str; } - if (keyTurnerState.network != 255) + if (keyTurnerState.remoteAccessStatus != 255) { - json["remoteAccessEnabled"] = ((keyTurnerState.network & 1) == 1) ? 1 : 0; - json["bridgePaired"] = (((keyTurnerState.network >> 1) & 1) == 1) ? 1 : 0; - json["sseConnectedViaWifi"] = (((keyTurnerState.network >> 2) & 1) == 1) ? 1 : 0; - json["sseConnectionEstablished"] = (((keyTurnerState.network >> 3) & 1) == 1) ? 1 : 0; - json["isSseConnectedViaThread"] = (((keyTurnerState.network >> 4) & 1) == 1) ? 1 : 0; - json["threadSseUplinkEnabledByUser"] = (((keyTurnerState.network >> 5) & 1) == 1) ? 1 : 0; - json["nat64AvailableViaThread"] = (((keyTurnerState.network >> 6) & 1) == 1) ? 1 : 0; + json["remoteAccessEnabled"] = ((keyTurnerState.remoteAccessStatus & 1) == 1) ? 1 : 0; + json["bridgePaired"] = (((keyTurnerState.remoteAccessStatus >> 1) & 1) == 1) ? 1 : 0; + json["sseConnectedViaWifi"] = (((keyTurnerState.remoteAccessStatus >> 2) & 1) == 1) ? 1 : 0; + json["sseConnectionEstablished"] = (((keyTurnerState.remoteAccessStatus >> 3) & 1) == 1) ? 1 : 0; + json["isSseConnectedViaThread"] = (((keyTurnerState.remoteAccessStatus >> 4) & 1) == 1) ? 1 : 0; + json["threadSseUplinkEnabledByUser"] = (((keyTurnerState.remoteAccessStatus >> 5) & 1) == 1) ? 1 : 0; + json["nat64AvailableViaThread"] = (((keyTurnerState.remoteAccessStatus >> 6) & 1) == 1) ? 1 : 0; } - if (keyTurnerState.bleConnectionStrength != 255) + if (keyTurnerState.bleConnectionStrength != 1) { json["bleConnectionStrength"] = keyTurnerState.bleConnectionStrength; } - if (keyTurnerState.wifiConnectionStrength != 255) + if (keyTurnerState.wifiConnectionStrength != 1) { json["wifiConnectionStrength"] = keyTurnerState.wifiConnectionStrength; } - if (keyTurnerState.wifi != 255) + if (keyTurnerState.wifiConnectionStatus != 255) { - json["wifiStatus"] = (keyTurnerState.wifi & 3); - json["sseStatus"] = ((keyTurnerState.wifi >> 2) & 3); - json["wifiQuality"] = ((keyTurnerState.wifi >> 4) & 15); + json["wifiStatus"] = (keyTurnerState.wifiConnectionStatus & 3); + json["sseStatus"] = ((keyTurnerState.wifiConnectionStatus >> 2) & 3); + json["wifiQuality"] = ((keyTurnerState.wifiConnectionStatus >> 4) & 15); } - if (keyTurnerState.mqtt != 255) + if (keyTurnerState.mqttConnectionStatus != 255) { - json["mqttStatus"] = (keyTurnerState.mqtt & 3); - json["mqttConnectionChannel"] = ((keyTurnerState.mqtt >> 2) & 1); + json["mqttStatus"] = (keyTurnerState.mqttConnectionStatus & 3); + json["mqttConnectionChannel"] = ((keyTurnerState.mqttConnectionStatus >> 2) & 1); } - if (keyTurnerState.thread != 255) + if (keyTurnerState.threadConnectionStatus != 255) { - json["threadConnectionStatus"] = (keyTurnerState.thread & 3); - json["threadSseStatus"] = ((keyTurnerState.thread >> 2) & 3); - json["isCommissioningModeActive"] = (keyTurnerState.thread & 16) != 0 ? 1 : 0; - json["isWifiDisabledBecauseOfThread"] = (keyTurnerState.thread & 32) != 0 ? 1 : 0; + json["threadConnectionStatus"] = (keyTurnerState.threadConnectionStatus & 3); + json["threadSseStatus"] = ((keyTurnerState.threadConnectionStatus >> 2) & 3); + json["isCommissioningModeActive"] = (keyTurnerState.threadConnectionStatus & 16) != 0 ? 1 : 0; + json["isWifiDisabledBecauseOfThread"] = (keyTurnerState.threadConnectionStatus & 32) != 0 ? 1 : 0; } json["auth_id"] = getAuthId(); @@ -876,9 +877,8 @@ void NukiNetworkLock::publishConfig(const NukiLock::Config &config) _network->timeZoneIdToString(config.timeZoneId, str); json["timeZone"] = str; json["deviceType"] = (config.deviceType == 255 ? 0 : config.deviceType); - json["channel"] = (config.network == 255 ? 0 : config.network); - json["wifiCapable"] = (config.network == 255 ? 0 : config.network & 1); - json["threadCapable"] = (config.network == 255 ? 0 : ((config.network & 2) != 0 ? 1 : 0)); + json["wifiCapable"] = (config.capabilities == 255 ? 0 : config.capabilities & 1); + json["threadCapable"] = (config.capabilities == 255 ? 0 : ((config.capabilities & 2) != 0 ? 1 : 0)); json["matterStatus"] = (config.matterStatus == 255 ? 0 : config.matterStatus); json["productVariant"] = (config.productVariant == 255 ? 0 : config.productVariant); @@ -936,6 +936,13 @@ void NukiNetworkLock::publishAdvancedConfig(const NukiLock::AdvancedConfig &conf json["autoLockEnabled"] = config.autoLockEnabled; json["immediateAutoLockEnabled"] = config.immediateAutoLockEnabled; json["autoUpdateEnabled"] = config.autoUpdateEnabled; + if (_isUltra) + { + memset(str, 0, sizeof(str)); + motorSpeedToString(config.motorSpeed, str); + json["motorSpeed"] = str; + json["enableSlowSpeedDuringNightMode"] = config.enableSlowSpeedDuringNightMode; + } json["rebootNuki"] = 0; serializeJson(json, _buffer, _bufferSize); @@ -1659,6 +1666,25 @@ void NukiNetworkLock::buttonPressActionToString(const NukiLock::ButtonPressActio } } +void NukiNetworkLock::motorSpeedToString(const NukiLock::MotorSpeed speed, char* str) +{ + switch (speed) + { + case NukiLock::MotorSpeed::Standard: + strcpy(str, "Standard"); + break; + case NukiLock::MotorSpeed::Insane: + strcpy(str, "Insane"); + break; + case NukiLock::MotorSpeed::Gentle: + strcpy(str, "Gentle"); + break; + default: + strcpy(str, "undefined"); + break; + } +} + void NukiNetworkLock::homeKitStatusToString(const int hkstatus, char* str) { switch (hkstatus) diff --git a/src/NukiNetworkLock.h b/src/NukiNetworkLock.h index 37a45b8d..e9ca80d8 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -68,6 +68,7 @@ class NukiNetworkLock : public MqttReceiver void publishKeypadEntry(const String topic, NukiLock::KeypadEntry entry); void buttonPressActionToString(const NukiLock::ButtonPressAction btnPressAction, char* str); + void motorSpeedToString(const NukiLock::MotorSpeed speed, char* str); void homeKitStatusToString(const int hkstatus, char* str); void fobActionToString(const int fobact, char* str); @@ -90,6 +91,7 @@ class NukiNetworkLock : public MqttReceiver bool _disableNonJSON = false; bool _offConnected = false; bool _hybridRebootOnDisconnect = false; + bool _isUltra = false; String _keypadCommandName = ""; String _keypadCommandCode = ""; diff --git a/src/NukiWrapper.cpp b/src/NukiWrapper.cpp index 07ed347b..c672cff0 100644 --- a/src/NukiWrapper.cpp +++ b/src/NukiWrapper.cpp @@ -60,7 +60,7 @@ void NukiWrapper::initialize() _nukiLock.setDebugCommand(_preferences->getBool(preference_debug_command, false)); _nukiLock.registerLogger(Log); - if (_preferences->getInt(preference_lock_gemini_pin, 0) > 0) + if (_preferences->getInt(preference_lock_gemini_pin, 0) > 0 && _preferences->getBool(preference_lock_gemini_enabled, false)) { _nukiLock.saveUltraPincode(_preferences->getInt(preference_lock_gemini_pin, 0), false); } @@ -135,6 +135,7 @@ void NukiWrapper::readSettings() _forceDoorsensor = _preferences->getBool(preference_lock_force_doorsensor, false); _forceKeypad = _preferences->getBool(preference_lock_force_keypad, false); _forceId = _preferences->getBool(preference_lock_force_id, false); + _isUltra = _preferences->getBool(preference_lock_gemini_enabled, false); _preferences->getBytes(preference_conf_lock_basic_acl, &_basicLockConfigaclPrefs, sizeof(_basicLockConfigaclPrefs)); _preferences->getBytes(preference_conf_lock_advanced_acl, &_advancedLockConfigaclPrefs, sizeof(_advancedLockConfigaclPrefs)); @@ -428,7 +429,14 @@ void NukiWrapper::lockngounlatch() bool NukiWrapper::isPinSet() { - return _nukiLock.getSecurityPincode() != 0; + if (_isUltra) + { + return _nukiLock.getUltraPincode() != 0; + } + else + { + return _nukiLock.getSecurityPincode() != 0; + } } bool NukiWrapper::isPinValid() @@ -441,11 +449,21 @@ void NukiWrapper::setPin(const uint16_t pin) _nukiLock.saveSecurityPincode(pin); } +void NukiWrapper::setUltraPin(const uint32_t pin) +{ + _nukiLock.saveUltraPincode(pin); +} + uint16_t NukiWrapper::getPin() { return _nukiLock.getSecurityPincode(); } +uint32_t NukiWrapper::getUltraPin() +{ + return _nukiLock.getUltraPincode(); +} + void NukiWrapper::unpair() { _nukiLock.unPairNuki(); @@ -1422,6 +1440,23 @@ Nuki::BatteryType NukiWrapper::batteryTypeToEnum(const char* str) return (Nuki::BatteryType)0xff; } +NukiLock::MotorSpeed NukiWrapper::motorSpeedToEnum(const char* str) +{ + if(strcmp(str, "Standard") == 0) + { + return NukiLock::MotorSpeed::Standard; + } + else if(strcmp(str, "Insane") == 0) + { + return NukiLock::MotorSpeed::Insane; + } + else if(strcmp(str, "Gentle") == 0) + { + return NukiLock::MotorSpeed::Gentle; + } + return (NukiLock::MotorSpeed)0xff; +} + void NukiWrapper::onOfficialUpdateReceived(const char *topic, const char *value) { _nukiOfficial->onOfficialUpdateReceived(topic, value); @@ -1465,7 +1500,7 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) Nuki::CmdResult cmdResult; const char *basicKeys[16] = {"name", "latitude", "longitude", "autoUnlatch", "pairingEnabled", "buttonEnabled", "ledEnabled", "ledBrightness", "timeZoneOffset", "dstMode", "fobAction1", "fobAction2", "fobAction3", "singleLock", "advertisingMode", "timeZone"}; - const char *advancedKeys[23] = {"unlockedPositionOffsetDegrees", "lockedPositionOffsetDegrees", "singleLockedPositionOffsetDegrees", "unlockedToLockedTransitionOffsetDegrees", "lockNgoTimeout", "singleButtonPressAction", "doubleButtonPressAction", "detachedCylinder", "batteryType", "automaticBatteryTypeDetection", "unlatchDuration", "autoLockTimeOut", "autoUnLockDisabled", "nightModeEnabled", "nightModeStartTime", "nightModeEndTime", "nightModeAutoLockEnabled", "nightModeAutoUnlockDisabled", "nightModeImmediateLockOnStart", "autoLockEnabled", "immediateAutoLockEnabled", "autoUpdateEnabled", "rebootNuki"}; + const char *advancedKeys[25] = {"unlockedPositionOffsetDegrees", "lockedPositionOffsetDegrees", "singleLockedPositionOffsetDegrees", "unlockedToLockedTransitionOffsetDegrees", "lockNgoTimeout", "singleButtonPressAction", "doubleButtonPressAction", "detachedCylinder", "batteryType", "automaticBatteryTypeDetection", "unlatchDuration", "autoLockTimeOut", "autoUnLockDisabled", "nightModeEnabled", "nightModeStartTime", "nightModeEndTime", "nightModeAutoLockEnabled", "nightModeAutoUnlockDisabled", "nightModeImmediateLockOnStart", "autoLockEnabled", "immediateAutoLockEnabled", "autoUpdateEnabled", "rebootNuki", "motorSpeed", "enableSlowSpeedDuringNightMode"}; bool basicUpdated = false; bool advancedUpdated = false; @@ -1857,7 +1892,7 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) } } - for(int j=0; j < 23; j++) + for(int j=0; j < 25; j++) { if(json[advancedKeys[j]].is()) { @@ -2354,6 +2389,46 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) jsonResult[advancedKeys[j]] = "invalidValue"; } } + else if(strcmp(advancedKeys[j], "motorSpeed") == 0) + { + NukiLock::MotorSpeed motorSpeed = nukiInst->motorSpeedToEnum(jsonchar); + + if((int)motorSpeed != 0xff) + { + if(_nukiAdvancedConfig.motorSpeed == motorSpeed) + { + jsonResult[advancedKeys[j]] = "unchanged"; + } + else + { + cmdResult = _nukiLock.setMotorSpeed(motorSpeed); + } + } + else + { + jsonResult[advancedKeys[j]] = "invalidValue"; + } + } + else if(strcmp(advancedKeys[j], "enableSlowSpeedDuringNightMode") == 0) + { + const uint8_t keyvalue = atoi(jsonchar); + + if(keyvalue == 0 || keyvalue == 1) + { + if(_nukiAdvancedConfig.enableSlowSpeedDuringNightMode == keyvalue) + { + jsonResult[advancedKeys[j]] = "unchanged"; + } + else + { + cmdResult = _nukiLock.enableSlowSpeedDuringNightMode((keyvalue > 0)); + } + } + else + { + jsonResult[advancedKeys[j]] = "invalidValue"; + } + } if(cmdResult != Nuki::CmdResult::Success) { diff --git a/src/NukiWrapper.h b/src/NukiWrapper.h index 40f52a5b..7dbe709e 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -30,7 +30,9 @@ class NukiWrapper : public Nuki::SmartlockEventHandler bool isPinSet(); bool isPinValid(); void setPin(const uint16_t pin); + void setUltraPin(const uint32_t pin); uint16_t getPin(); + uint32_t getUltraPin(); void unpair(); void disableWatchdog(); @@ -88,6 +90,7 @@ class NukiWrapper : public Nuki::SmartlockEventHandler uint8_t fobActionToInt(const char *str); NukiLock::ButtonPressAction buttonPressActionToEnum(const char* str); Nuki::BatteryType batteryTypeToEnum(const char* str); + NukiLock::MotorSpeed motorSpeedToEnum(const char* str); std::string _deviceName; NukiDeviceId* _deviceId = nullptr; @@ -137,6 +140,7 @@ class NukiWrapper : public Nuki::SmartlockEventHandler bool _forceKeypad = false; bool _keypadEnabled = false; bool _forceId = false; + bool _isUltra = false; uint _maxKeypadCodeCount = 0; uint _maxTimeControlEntryCount = 0; uint _maxAuthEntryCount = 0; @@ -160,7 +164,7 @@ class NukiWrapper : public Nuki::SmartlockEventHandler int64_t _lastRssi = 0; int64_t _disableBleWatchdogTs = 0; uint32_t _basicLockConfigaclPrefs[16]; - uint32_t _advancedLockConfigaclPrefs[23]; + uint32_t _advancedLockConfigaclPrefs[25]; std::string _firmwareVersion = ""; std::string _hardwareVersion = ""; volatile NukiLock::LockAction _nextLockAction = (NukiLock::LockAction)0xff; diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index b11b3f8b..f94be259 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -74,10 +74,10 @@ #define preference_update_time (char*)"updateTime" #define preference_mqtt_ssl_enabled (char*)"mqttSSLena" #define preference_lock_gemini_pin (char*)"geminiPin" +#define preference_lock_gemini_enabled (char*)"geminiena" // CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT #define preference_find_best_rssi (char*)"nwbestrssi" -#define preference_ntw_reconfigure (char*)"ntwRECONF" #define preference_auth_max_entries (char*)"authmaxentry" #define preference_auth_info_enabled (char*)"authInfoEna" #define preference_auth_topic_per_entry (char*)"authPerEntry" @@ -128,6 +128,7 @@ #define preference_hybrid_reboot_on_disconnect (char*)"hybridRbtLck" //NOT USER CHANGABLE +#define preference_ntw_reconfigure (char*)"ntwRECONF" #define preference_updater_version (char*)"updVer" #define preference_updater_build (char*)"updBuild" #define preference_updater_date (char*)"updDate" @@ -151,10 +152,6 @@ //OBSOLETE #define preference_access_level (char*)"accLvl" -#define preference_gpio_locking_enabled (char*)"gpiolck" -#define preference_network_hardware_gpio (char*)"nwhwdt" -#define preference_presence_detection_timeout (char*)"prdtimeout" -#define preference_network_wifi_fallback_disabled (char*)"nwwififb" #define preference_mqtt_opener_path (char*)"mqttoppath" inline void initPreferences(Preferences* preferences) @@ -176,7 +173,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); @@ -207,6 +204,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBool(preference_register_as_app, false); preferences->putBool(preference_register_opener_as_app, false); preferences->putBool(preference_mqtt_ssl_enabled, false); + preferences->putBool(preference_lock_gemini_enabled, false); preferences->putInt(preference_mqtt_broker_port, 1883); preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE); @@ -276,7 +274,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); @@ -291,7 +289,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); @@ -306,7 +304,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); @@ -321,7 +319,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); @@ -336,7 +334,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBytes(preference_conf_lock_basic_acl, (byte*)(&basicLockConfigAclPrefs), sizeof(basicLockConfigAclPrefs)); uint32_t basicOpenerConfigAclPrefs[14] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; preferences->putBytes(preference_conf_opener_basic_acl, (byte*)(&basicOpenerConfigAclPrefs), sizeof(basicOpenerConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + uint32_t advancedLockConfigAclPrefs[25] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); @@ -483,7 +481,7 @@ class DebugPreferences preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_mqtt_ssl_enabled, preference_official_hybrid_enabled, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, 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_ble_tx_power, preference_webserial_enabled, preference_find_best_rssi, + preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_webserial_enabled, preference_find_best_rssi, preference_lock_gemini_enabled, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count, @@ -509,7 +507,7 @@ class DebugPreferences preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_http_auth_type, preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled, - preference_hybrid_reboot_on_disconnect + preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled }; std::vector _bytePrefs = { diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index cfcde2e1..0ea95e1f 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -1613,7 +1613,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S unsigned char authorizationId[4] = {0x00}; unsigned char secretKeyK[32] = {0x00}; unsigned char pincode[2] = {0x00}; - unsigned char ultraPincode[4] = {0x00}; + unsigned char ultraPincode[4] = {0x00}; bool isUltra = false; unsigned char currentBleAddressOpn[6]; unsigned char authorizationIdOpn[4] = {0x00}; @@ -1622,7 +1622,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S uint32_t aclPrefs[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t basicLockConfigAclPrefs[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t basicOpenerConfigAclPrefs[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint32_t advancedLockConfigAclPrefs[23] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t advancedLockConfigAclPrefs[25] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int params = request->params(); @@ -2032,16 +2032,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S configChanged = true; } } - else if(key == "NWHWWIFIFB") - { - if(_preferences->getBool(preference_network_wifi_fallback_disabled, false) != (value == "1")) - { - _preferences->putBool(preference_network_wifi_fallback_disabled, (value == "1")); - Log->print(("Setting changed: ")); - Log->println(key); - //configChanged = true; - } - } else if(key == "RSSI") { if(_preferences->getInt(preference_rssi_publish_interval, 60) != value.toInt()) @@ -2587,16 +2577,6 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S configChanged = true; } } - else if(key == "GEMINIPIN") - { - if(_preferences->getInt(preference_lock_gemini_pin, 0) != value.toInt()) - { - _preferences->putInt(preference_lock_gemini_pin, value.toInt()); - Log->print(F("Setting changed: ")); - Log->println(key); - configChanged = true; - } - } else if(key == "LCKFORCEID") { if(_preferences->getBool(preference_lock_force_id, false) != (value == "1")) @@ -3010,6 +2990,14 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S { advancedLockConfigAclPrefs[22] = ((value == "1") ? 1 : 0); } + else if(key == "CONFLCKMTRSPD") + { + advancedLockConfigAclPrefs[23] = ((value == "1") ? 1 : 0); + } + else if(key == "CONFLCKESSDNM") + { + advancedLockConfigAclPrefs[24] = ((value == "1") ? 1 : 0); + } else if(key == "CONFOPNNAME") { basicOpenerConfigAclPrefs[0] = ((value == "1") ? 1 : 0); @@ -3180,6 +3168,23 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S configChanged = true; } } + else if(key == "GEMINIENA") + { + if(_preferences->getBool(preference_lock_gemini_enabled, false) != (value == "1")) + { + _preferences->putBool(preference_lock_gemini_enabled, (value == "1")); + if (value == "1") + { + _preferences->putBool(preference_register_as_app, true); + _preferences->putBool(preference_lock_enabled, true); + _preferences->putBool(preference_official_hybrid_enabled, true); + _preferences->putBool(preference_official_hybrid_actions, true); + } + Log->print(("Setting changed: ")); + Log->println(key); + configChanged = true; + } + } else if(key == "OPENA") { if(_preferences->getBool(preference_opener_enabled, false) != (value == "1")) @@ -3229,21 +3234,45 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S { if(value == "#") { - message = "Nuki Lock PIN cleared"; - _nuki->setPin(0xffff); + if (_preferences->getBool(preference_lock_gemini_enabled, false)) + { + message = "Nuki Lock Ultra PIN cleared"; + _nuki->setUltraPin(0xffffffff); + _preferences->putInt(preference_lock_gemini_pin, 0); + } + else + { + message = "Nuki Lock PIN cleared"; + _nuki->setPin(0xffff); + } Log->print(("Setting changed: ")); Log->println(key); configChanged = true; } else { - if(_nuki->getPin() != value.toInt()) + if (_preferences->getBool(preference_lock_gemini_enabled, false)) { - message = "Nuki Lock PIN saved"; - _nuki->setPin(value.toInt()); - Log->print(("Setting changed: ")); - Log->println(key); - configChanged = true; + if(_nuki->getUltraPin() != value.toInt()) + { + message = "Nuki Lock Ultra PIN saved"; + _nuki->setUltraPin(value.toInt()); + _preferences->putInt(preference_lock_gemini_pin, value.toInt()); + Log->print(("Setting changed: ")); + Log->println(key); + configChanged = true; + } + } + else + { + if(_nuki->getPin() != value.toInt()) + { + message = "Nuki Lock PIN saved"; + _nuki->setPin(value.toInt()); + Log->print(("Setting changed: ")); + Log->println(key); + configChanged = true; + } } } } @@ -3298,6 +3327,10 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S authorizationId[(i/2)] = std::stoi(value.substring(i, i+2).c_str(), nullptr, 16); } } + else if(key == "LCKISULTRA" && (value == "1")) + { + isUltra = true; + } else if(key == "OPNBLEADDR") { if(value.length() == 12) for(int i=0; igetBytes(preference_acl, &curAclPrefs, sizeof(curAclPrefs)); @@ -3441,7 +3474,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S break; } } - for(int i=0; i < 23; i++) + for(int i=0; i < 25; i++) { if(curAdvancedLockConfigAclPrefs[i] != advancedLockConfigAclPrefs[i]) { @@ -3662,6 +3695,13 @@ bool WebCfgServer::processImport(PsychicRequest *request, PsychicResponse* resp, nukiBlePref.putBytes("authorizationId", authorizationId, 4); } } + if(!doc["isUltra"].isNull()) + { + if (doc["isUltra"].as().length() >0) + { + nukiBlePref.putBool("isUltra", (doc["isUltra"].as() == "1" ? true : false)); + } + } nukiBlePref.end(); if(!doc["securityPinCodeLock"].isNull() && _nuki != nullptr) { @@ -3674,6 +3714,19 @@ bool WebCfgServer::processImport(PsychicRequest *request, PsychicResponse* resp, _nuki->setPin(0xffff); } } + if(!doc["ultraPinCodeLock"].isNull() && _nuki != nullptr) + { + if(doc["ultraPinCodeLock"].as().length() > 0) + { + _nuki->setUltraPin(doc["ultraPinCodeLock"].as()); + _preferences->putInt(preference_lock_gemini_pin, doc["ultraPinCodeLock"].as()); + } + else + { + _nuki->setUltraPin(0xffffffff); + _preferences->putInt(preference_lock_gemini_pin, 0); + } + } nukiBlePref.begin("NukiHubopener", false); if(!doc["bleAddressOpener"].isNull()) { @@ -4339,6 +4392,7 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic printInputField(&response, "LCKBLEADDR", "currentBleAddress", "", 12, ""); printInputField(&response, "LCKSECRETK", "secretKeyK", "", 64, ""); printInputField(&response, "LCKAUTHID", "authorizationId", "", 8, ""); + printCheckBox(&response, "LCKISULTRA", "isUltra", false, ""); } if(_preferences->getBool(preference_opener_enabled, false)) { @@ -4377,8 +4431,6 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic printCheckBox(&response, "DBGREAD", "Enable Nuki readable data debug logging", _preferences->getBool(preference_debug_readable_data, false), ""); printCheckBox(&response, "DBGHEX", "Enable Nuki hex data debug logging", _preferences->getBool(preference_debug_hex_data, false), ""); printCheckBox(&response, "DBGCOMM", "Enable Nuki command debug logging", _preferences->getBool(preference_debug_command, false), ""); - printInputField(&response, "GEMINIPIN", "SmartLock Ultra PIN", _preferences->getInt(preference_lock_gemini_pin, 0), 6, ""); - response.print(""); response.print("
"); @@ -4544,7 +4596,7 @@ esp_err_t WebCfgServer::buildAccLvlHtml(PsychicRequest *request, PsychicResponse { uint32_t basicLockConfigAclPrefs[16]; _preferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23]; + uint32_t advancedLockConfigAclPrefs[25]; _preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs)); response.print("

Nuki Lock Access Control

"); @@ -4612,6 +4664,8 @@ esp_err_t WebCfgServer::buildAccLvlHtml(PsychicRequest *request, PsychicResponse printCheckBox(&response, "CONFLCKIALENA", "Immediate auto lock enabled", ((int)advancedLockConfigAclPrefs[20] == 1), "chk_config_lock"); printCheckBox(&response, "CONFLCKAUENA", "Auto update enabled", ((int)advancedLockConfigAclPrefs[21] == 1), "chk_config_lock"); printCheckBox(&response, "CONFLCKRBTNUKI", "Reboot Nuki", ((int)advancedLockConfigAclPrefs[22] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKMTRSPD", "Motor speed", ((int)advancedLockConfigAclPrefs[23] == 1), "chk_config_lock"); + printCheckBox(&response, "CONFLCKESSDNM", "Enable slow speed during nightmode", ((int)advancedLockConfigAclPrefs[24] == 1), "chk_config_lock"); response.print("
"); response.print("
"); } @@ -4699,8 +4753,9 @@ esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request, PsychicResp response.print(""); response.print("

Basic Nuki Configuration

"); response.print(""); - printCheckBox(&response, "LOCKENA", "Nuki Lock enabled", _preferences->getBool(preference_lock_enabled), ""); - printCheckBox(&response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled), ""); + printCheckBox(&response, "LOCKENA", "Nuki Lock enabled", _preferences->getBool(preference_lock_enabled, true), ""); + printCheckBox(&response, "GEMINIENA", "Nuki Smartlock Ultra enabled", _preferences->getBool(preference_lock_gemini_enabled, false), ""); + printCheckBox(&response, "OPENA", "Nuki Opener enabled", _preferences->getBool(preference_opener_enabled, false), ""); printCheckBox(&response, "CONNMODE", "New Nuki Bluetooth connection mode (disable if there are connection issues)", _preferences->getBool(preference_connect_mode, true), ""); response.print("

"); response.print("

Advanced Nuki Configuration

"); @@ -4715,7 +4770,7 @@ esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request, PsychicResp } printInputField(&response, "NRTRY", "Number of retries if command failed", _preferences->getInt(preference_command_nr_of_retries), 10, ""); printInputField(&response, "TRYDLY", "Delay between retries (milliseconds)", _preferences->getInt(preference_command_retry_delay), 10, ""); - if(_preferences->getBool(preference_lock_enabled, true)) + if(_preferences->getBool(preference_lock_enabled, true) && !_preferences->getBool(preference_lock_gemini_enabled, false)) { printCheckBox(&response, "REGAPP", "Lock: Nuki Bridge is running alongside Nuki Hub (needs re-pairing if changed)", _preferences->getBool(preference_register_as_app), ""); } @@ -4885,6 +4940,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print(_preferences->getString(preference_cred_user, "").length() > 0 ? "***" : "Not set"); response.print("\nWeb configurator password: "); response.print(_preferences->getString(preference_cred_password, "").length() > 0 ? "***" : "Not set"); + response.print("\nWeb configurator authentication: "); + response.print(_preferences->getBool(preference_http_auth_type, false) ? "Digest" : "Basic"); response.print("\nWeb configurator enabled: "); response.print(_preferences->getBool(preference_webserver_enabled, true) ? "Yes" : "No"); response.print("\nHTTP SSL: "); @@ -4912,6 +4969,16 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* #endif response.print("\nPublish debug information enabled: "); response.print(_preferences->getBool(preference_publish_debug_info, false) ? "Yes" : "No"); + response.print("\nNuki connect debug logging enabled: "); + response.print(_preferences->getBool(preference_debug_connect, false) ? "Yes" : "No"); + response.print("\nNuki communication debug logging enabled: "); + response.print(_preferences->getBool(preference_debug_communication, false) ? "Yes" : "No"); + response.print("\nNuki readable data debug logging enabled: "); + response.print(_preferences->getBool(preference_debug_readable_data, false) ? "Yes" : "No"); + response.print("\nNuki hex data debug logging enabled: "); + response.print(_preferences->getBool(preference_debug_hex_data, false) ? "Yes" : "No"); + response.print("\nNuki command debug logging enabled: "); + response.print(_preferences->getBool(preference_debug_command, false) ? "Yes" : "No"); response.print("\nMQTT log enabled: "); response.print(_preferences->getBool(preference_mqtt_log_enabled, false) ? "Yes" : "No"); response.print("\nWebserial enabled: "); @@ -4963,8 +5030,6 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("\nStatic IP DNS server: "); response.print(_preferences->getString(preference_ip_dns_server, "")); } - -#ifndef CONFIG_IDF_TARGET_ESP32H2 if(_network->networkDeviceName() == "Built-in Wi-Fi") { response.print("\nRSSI Publish interval (s): "); @@ -4981,9 +5046,16 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("\nFind WiFi AP with strongest signal: "); response.print(_preferences->getBool(preference_find_best_rssi, false) ? "Yes" : "No"); } -#endif + /* + else if(network->networkDeviceType() == NetworkDeviceType::CUSTOM) + { + + } + */ response.print("\nRestart ESP32 on network disconnect enabled: "); response.print(_preferences->getBool(preference_restart_on_disconnect, false) ? "Yes" : "No"); + response.print("\nDisable Network if not connected within 60s: "); + response.print(_preferences->getBool(preference_disable_network_not_connected, false) ? "Yes" : "No"); response.print("\nMQTT Timeout until restart (s): "); if(_preferences->getInt(preference_network_timeout, 60) < 0) { @@ -5039,6 +5111,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("Disabled"); } response.print("\n\n------------ BLUETOOTH ------------"); + response.print("\nBluetooth connection mode: "); + response.print(_preferences->getBool(preference_connect_mode, false) ? "New" : "Old"); response.print("\nBluetooth TX power (dB): "); response.print(_preferences->getInt(preference_ble_tx_power, 9)); response.print("\nBluetooth command nr of retries: "); @@ -5086,6 +5160,12 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print(_preferences->getBool(preference_timecontrol_topic_per_entry, false) ? "Yes" : "No"); response.print("\nMax timecontrol entries to retrieve: "); response.print(_preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL)); + response.print("\nEnable authorization control: "); + response.print(_preferences->getBool(preference_auth_info_enabled, false) ? "Yes" : "No"); + response.print("\nPublish authorization topic per entry: "); + response.print(_preferences->getBool(preference_auth_topic_per_entry, false) ? "Yes" : "No"); + response.print("\nMax authorization entries to retrieve: "); + response.print(_preferences->getInt(preference_auth_max_entries, MAX_AUTH)); response.print("\n\n------------ HOME ASSISTANT ------------"); response.print("\nHome Assistant auto discovery enabled: "); if(_preferences->getString(preference_mqtt_hass_discovery, "").length() > 0) @@ -5110,6 +5190,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* else { response.print("\nLock enabled: Yes"); + response.print("\nLock Ultra enabled: "); + response.print(_preferences->getBool(preference_lock_gemini_enabled, false) ? "Yes" : "No"); response.print("\nPaired: "); response.print(_nuki->isPaired() ? "Yes" : "No"); response.print("\nNuki Hub device ID: "); @@ -5133,6 +5215,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* } response.print("\nTimecontrol highest entries count: "); response.print(_preferences->getInt(preference_lock_max_timecontrol_entry_count, 0)); + response.print("\nAuthorizations highest entries count: "); + response.print(_preferences->getInt(preference_lock_max_auth_entry_count, 0)); response.print("\nRegister as: "); response.print(_preferences->getBool(preference_register_as_app, false) ? "App" : "Bridge"); response.print("\n\n------------ HYBRID MODE ------------"); @@ -5145,21 +5229,27 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print("\nHybrid mode enabled: Yes"); response.print("\nHybrid mode connected: "); response.print(_nuki->offConnected() ? "Yes": "No"); + response.print("\nReboot Nuki lock on official MQTT failure: "); + response.print(_preferences->getBool(preference_hybrid_reboot_on_disconnect, false) ? "Yes" : "No"); response.print("\nSending actions through official MQTT enabled: "); response.print(_preferences->getBool(preference_official_hybrid_actions, false) ? "Yes" : "No"); - /* NOT IMPLEMENTED (YET?) if(_preferences->getBool(preference_official_hybrid_actions, false)) { response.print("\nRetry actions through BLE enabled: "); response.print(_preferences->getBool(preference_official_hybrid_retry, false) ? "Yes" : "No"); } - */ response.print("\nTime between status updates when official MQTT is offline (s): "); response.print(_preferences->getInt(preference_query_interval_hybrid_lockstate, 600)); } + response.print("\nForce Lock ID: "); + response.print(_preferences->getBool(preference_lock_force_id, false) ? "Yes" : "No"); + response.print("\nForce Lock Keypad: "); + response.print(_preferences->getBool(preference_lock_force_keypad, false) ? "Yes" : "No"); + response.print("\nForce Lock Doorsensor: "); + response.print(_preferences->getBool(preference_lock_force_doorsensor, false) ? "Yes" : "No"); uint32_t basicLockConfigAclPrefs[16]; _preferences->getBytes(preference_conf_lock_basic_acl, &basicLockConfigAclPrefs, sizeof(basicLockConfigAclPrefs)); - uint32_t advancedLockConfigAclPrefs[23]; + uint32_t advancedLockConfigAclPrefs[25]; _preferences->getBytes(preference_conf_lock_advanced_acl, &advancedLockConfigAclPrefs, sizeof(advancedLockConfigAclPrefs)); response.print("\n\n------------ NUKI LOCK ACL ------------"); response.print("\nLock: "); @@ -5259,6 +5349,10 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* response.print((int)advancedLockConfigAclPrefs[21] ? "Allowed" : "Disallowed"); response.print("\nReboot Nuki: "); response.print((int)advancedLockConfigAclPrefs[22] ? "Allowed" : "Disallowed"); + response.print("\nMotor speed: "); + response.print((int)advancedLockConfigAclPrefs[23] ? "Allowed" : "Disallowed"); + response.print("\nEnable slow speed during nightmode: "); + response.print((int)advancedLockConfigAclPrefs[24] ? "Allowed" : "Disallowed"); if(_preferences->getBool(preference_show_secrets)) { @@ -5294,6 +5388,8 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* uint32_t authorizationIdInt = authorizationId[0] + 256U*authorizationId[1] + 65536U*authorizationId[2] + 16777216U*authorizationId[3]; response.print("\nAuthorizationId (UINT32_T): "); response.print(authorizationIdInt); + response.print("\nPaired to Nuki Lock Ultra: "); + response.print(nukiBlePref.getBool("isUltra", false) ? "Yes" : "No"); } } @@ -5326,10 +5422,16 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* } response.print("\nTimecontrol highest entries count: "); response.print(_preferences->getInt(preference_opener_max_timecontrol_entry_count, 0)); + response.print("\nAuthorizations highest entries count: "); + response.print(_preferences->getInt(preference_opener_max_auth_entry_count, 0)); response.print("\nRegister as: "); response.print(_preferences->getBool(preference_register_opener_as_app, false) ? "App" : "Bridge"); response.print("\nNuki Opener Lock/Unlock action set to Continuous mode in Home Assistant: "); response.print(_preferences->getBool(preference_opener_continuous_mode, false) ? "Yes" : "No"); + response.print("\nForce Opener ID: "); + response.print(_preferences->getBool(preference_opener_force_id, false) ? "Yes" : "No"); + response.print("\nForce Opener Keypad: "); + response.print(_preferences->getBool(preference_opener_force_keypad, false) ? "Yes" : "No"); uint32_t basicOpenerConfigAclPrefs[14]; _preferences->getBytes(preference_conf_opener_basic_acl, &basicOpenerConfigAclPrefs, sizeof(basicOpenerConfigAclPrefs)); uint32_t advancedOpenerConfigAclPrefs[21]; @@ -5453,10 +5555,15 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* sprintf(tmp, "%02x", authorizationIdOpn[i]); response.print(tmp); } + uint32_t authorizationIdOpnInt = authorizationIdOpn[0] + 256U*authorizationIdOpn[1] + 65536U*authorizationIdOpn[2] + 16777216U*authorizationIdOpn[3]; + response.print("\nAuthorizationId (UINT32_T): "); + response.print(authorizationIdOpnInt); } } response.print("\n\n------------ GPIO ------------\n"); + response.print("\nRetain Input GPIO MQTT state: "); + response.print(_preferences->getBool(preference_retain_gpio, false) ? "Yes" : "No"); String gpioStr = ""; _gpio->getConfigurationText(gpioStr, _gpio->pinConfiguration()); response.print(gpioStr); @@ -5844,4 +5951,4 @@ String WebCfgServer::getPreselectionForGpio(const uint8_t &pin) return String((int8_t)PinRole::Disabled); } -#endif +#endif \ No newline at end of file From 1c4b5b0e33f0d94aed6d72b6c43dbe054df3ec6e Mon Sep 17 00:00:00 2001 From: iranl Date: Wed, 15 Jan 2025 16:19:31 +0100 Subject: [PATCH 3/5] Ultra and other improvements --- lib/nuki_ble | 2 +- src/Config.h | 2 +- src/HomeAssistantDiscovery.cpp | 4 +-- src/NukiNetworkLock.cpp | 50 ++++++++++++++++++++-------------- src/NukiNetworkLock.h | 1 + src/NukiNetworkOpener.cpp | 38 ++++++++++++++------------ src/NukiNetworkOpener.h | 1 + src/NukiWrapper.cpp | 4 +-- src/WebCfgServer.cpp | 2 +- 9 files changed, 59 insertions(+), 45 deletions(-) diff --git a/lib/nuki_ble b/lib/nuki_ble index dc0696eb..a579d58a 160000 --- a/lib/nuki_ble +++ b/lib/nuki_ble @@ -1 +1 @@ -Subproject commit dc0696ebc26fdea760495e5b8ae2ef288136287e +Subproject commit a579d58a1c0d3d6119a780f4376b77046795cc16 diff --git a/src/Config.h b/src/Config.h index a180fe91..85ffa72a 100644 --- a/src/Config.h +++ b/src/Config.h @@ -5,7 +5,7 @@ #define NUKI_HUB_VERSION "9.08" #define NUKI_HUB_VERSION_INT (uint32_t)908 #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2025-01-14" +#define NUKI_HUB_DATE "2025-01-15" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/HomeAssistantDiscovery.cpp b/src/HomeAssistantDiscovery.cpp index 6149bd1c..84c246f0 100644 --- a/src/HomeAssistantDiscovery.cpp +++ b/src/HomeAssistantDiscovery.cpp @@ -1537,7 +1537,7 @@ void HomeAssistantDiscovery::publishHASSConfigAdditionalLockEntities(char *devic removeHassTopic((char*)"switch", (char*)"detached_cylinder", uidString); } - if((int)advancedLockConfigAclPrefs[8] == 1) + if((int)advancedLockConfigAclPrefs[8] == 1 && !_preferences->getBool(preference_lock_gemini_enabled, false)) { JsonDocument json; json = createHassJson(uidString, "_battery_type", "Battery type", name, baseTopic, String("~") + mqtt_topic_config_advanced_json, deviceType, "", "", "config", String("~") + mqtt_topic_config_action, {{ (char*)"val_tpl", (char*)"{{value_json.batteryType}}" }, { (char*)"en", (char*)"true" }, { (char*)"cmd_tpl", (char*)"{ \"batteryType\": \"{{ value }}\" }" }}); @@ -1553,7 +1553,7 @@ void HomeAssistantDiscovery::publishHASSConfigAdditionalLockEntities(char *devic removeHassTopic((char*)"select", (char*)"battery_type", uidString); } - if((int)advancedLockConfigAclPrefs[9] == 1) + if((int)advancedLockConfigAclPrefs[9] == 1 && !_preferences->getBool(preference_lock_gemini_enabled, false)) { // Automatic battery type detection publishHassTopic("switch", diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index fa61ba91..8ecd0b19 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -920,10 +920,14 @@ void NukiNetworkLock::publishAdvancedConfig(const NukiLock::AdvancedConfig &conf buttonPressActionToString(config.doubleButtonPressAction, str); json["doubleButtonPressAction"] = str; json["detachedCylinder"] = config.detachedCylinder; - memset(str, 0, sizeof(str)); - _network->batteryTypeToString(config.batteryType, str); - json["batteryType"] = str; - json["automaticBatteryTypeDetection"] = config.automaticBatteryTypeDetection; + + if (!_isUltra) + { + memset(str, 0, sizeof(str)); + _network->batteryTypeToString(config.batteryType, str); + json["batteryType"] = str; + json["automaticBatteryTypeDetection"] = config.automaticBatteryTypeDetection; + } json["unlatchDuration"] = config.unlatchDuration; json["autoLockTimeOut"] = config.autoLockTimeOut; json["autoUnLockDisabled"] = config.autoUnLockDisabled; @@ -1152,24 +1156,28 @@ void NukiNetworkLock::publishKeypad(const std::list& entr } else { - for(int i=0; iremoveTopic(codeTopic, "id"); - _network->removeTopic(codeTopic, "enabled"); - _network->removeTopic(codeTopic, "code"); - _network->removeTopic(codeTopic, "name"); - _network->removeTopic(codeTopic, "createdYear"); - _network->removeTopic(codeTopic, "createdMonth"); - _network->removeTopic(codeTopic, "createdDay"); - _network->removeTopic(codeTopic, "createdHour"); - _network->removeTopic(codeTopic, "createdMin"); - _network->removeTopic(codeTopic, "createdSec"); - _network->removeTopic(codeTopic, "lockCount"); + _clearNonJsonKeypad = false; + for(int i=0; iremoveTopic(codeTopic, "id"); + _network->removeTopic(codeTopic, "enabled"); + _network->removeTopic(codeTopic, "code"); + _network->removeTopic(codeTopic, "name"); + _network->removeTopic(codeTopic, "createdYear"); + _network->removeTopic(codeTopic, "createdMonth"); + _network->removeTopic(codeTopic, "createdDay"); + _network->removeTopic(codeTopic, "createdHour"); + _network->removeTopic(codeTopic, "createdMin"); + _network->removeTopic(codeTopic, "createdSec"); + _network->removeTopic(codeTopic, "lockCount"); + } } for(int j=entries.size(); j& en } else { - for(int i=0; iremoveTopic(codeTopic, "id"); - _network->removeTopic(codeTopic, "enabled"); - _network->removeTopic(codeTopic, "code"); - _network->removeTopic(codeTopic, "name"); - _network->removeTopic(codeTopic, "createdYear"); - _network->removeTopic(codeTopic, "createdMonth"); - _network->removeTopic(codeTopic, "createdDay"); - _network->removeTopic(codeTopic, "createdHour"); - _network->removeTopic(codeTopic, "createdMin"); - _network->removeTopic(codeTopic, "createdSec"); - _network->removeTopic(codeTopic, "lockCount"); + _clearNonJsonKeypad = false; + for(int i=0; iremoveTopic(codeTopic, "id"); + _network->removeTopic(codeTopic, "enabled"); + _network->removeTopic(codeTopic, "code"); + _network->removeTopic(codeTopic, "name"); + _network->removeTopic(codeTopic, "createdYear"); + _network->removeTopic(codeTopic, "createdMonth"); + _network->removeTopic(codeTopic, "createdDay"); + _network->removeTopic(codeTopic, "createdHour"); + _network->removeTopic(codeTopic, "createdMin"); + _network->removeTopic(codeTopic, "createdSec"); + _network->removeTopic(codeTopic, "lockCount"); + } } for(int j=entries.size(); jbatteryTypeToEnum(jsonchar); - if((int)battype != 0xff) + if((int)battype != 0xff && !_isUltra) { if(_nukiAdvancedConfig.batteryType == battype) { @@ -2116,7 +2116,7 @@ void NukiWrapper::onConfigUpdateReceived(const char *value) { const uint8_t keyvalue = atoi(jsonchar); - if(keyvalue == 0 || keyvalue == 1) + if((keyvalue == 0 || keyvalue == 1) && !_isUltra) { if(_nukiAdvancedConfig.automaticBatteryTypeDetection == keyvalue) { diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 0ea95e1f..7ea95eeb 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -5112,7 +5112,7 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* } response.print("\n\n------------ BLUETOOTH ------------"); response.print("\nBluetooth connection mode: "); - response.print(_preferences->getBool(preference_connect_mode, false) ? "New" : "Old"); + response.print(_preferences->getBool(preference_connect_mode, true) ? "New" : "Old"); response.print("\nBluetooth TX power (dB): "); response.print(_preferences->getInt(preference_ble_tx_power, 9)); response.print("\nBluetooth command nr of retries: "); From e42e8eb5b30a3cbbe014f71a5f0094c2f0bce5cd Mon Sep 17 00:00:00 2001 From: iranl Date: Wed, 15 Jan 2025 21:17:41 +0100 Subject: [PATCH 4/5] NTP improvements --- README.md | 2 ++ sdkconfig.defaults | 4 +++- src/PreferencesKeys.h | 10 +++++++--- src/WebCfgServer.cpp | 33 +++++++++++++++++++++++++++++---- src/main.cpp | 22 +++++++++++++++++----- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ea0aabb9..c14d9bc4 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,8 @@ In a browser navigate to the IP address assigned to the ESP32. - 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. +- Update Nuki Hub and Lock/Opener time using NTP: Enable to update the ESP32 time and Nuki Lock and/or Nuki Opener time every 12 hours using a NTP time server +- NTP server: Set to the NTP server you want to use, defaults to "pool.ntp.org". If DHCP is used and NTP servers are provided using DHCP these will take precedence over the specified NTP server. ### Access Level Configuration diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 650f0461..c3091fa6 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -118,4 +118,6 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=8 CONFIG_ESP_WIFI_RX_BA_WIN=6 CONFIG_ESP_WIFI_IRAM_OPT=n CONFIG_ESP_WIFI_RX_IRAM_OPT=n -CONFIG_MBEDTLS_DYNAMIC_BUFFER=y \ No newline at end of file +CONFIG_MBEDTLS_DYNAMIC_BUFFER=y +CONFIG_LWIP_DHCP_GET_NTP_SRV=y +CONFIG_LWIP_SNTP_UPDATE_DELAY=43200000 \ No newline at end of file diff --git a/src/PreferencesKeys.h b/src/PreferencesKeys.h index f94be259..e99f3cbc 100644 --- a/src/PreferencesKeys.h +++ b/src/PreferencesKeys.h @@ -72,6 +72,7 @@ #define preference_connect_mode (char*)"nukiConnMode" #define preference_http_auth_type (char*)"httpdAuthType" #define preference_update_time (char*)"updateTime" +#define preference_time_server (char*)"timeServer" #define preference_mqtt_ssl_enabled (char*)"mqttSSLena" #define preference_lock_gemini_pin (char*)"geminiPin" #define preference_lock_gemini_enabled (char*)"geminiena" @@ -116,6 +117,7 @@ #define preference_network_timeout (char*)"nettmout" #define preference_restart_on_disconnect (char*)"restdisc" #define preference_publish_debug_info (char*)"pubdbg" +#define preference_enable_debug_mode (char*)"enadbg" #define preference_official_hybrid_actions (char*)"hybridAct" #define preference_official_hybrid_retry (char*)"hybridRtry" #define preference_keypad_check_code_enabled (char*)"kpChkEna" @@ -178,6 +180,7 @@ inline void initPreferences(Preferences* preferences) uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs)); preferences->putString(preference_mqtt_lock_path, "nukihub"); + preferences->putString(preference_time_server, "pool.ntp.org"); preferences->putBool(preference_check_updates, true); preferences->putBool(preference_opener_continuous_mode, false); @@ -232,6 +235,7 @@ inline void initPreferences(Preferences* preferences) preferences->putBool(preference_connect_mode, true); preferences->putBool(preference_http_auth_type, false); preferences->putBool(preference_retain_gpio, false); + preferences->putBool(preference_enable_debug_mode, false); #ifndef CONFIG_IDF_TARGET_ESP32H2 WiFi.begin(); @@ -469,7 +473,7 @@ class DebugPreferences preference_started_before, preference_config_version, preference_device_id_lock, preference_device_id_opener, preference_nuki_id_lock, preference_nuki_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_webserver_enabled, preference_lock_enabled, preference_lock_pin_status, preference_mqtt_lock_path, preference_opener_enabled, preference_opener_pin_status, - preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_update_time, + preference_opener_continuous_mode, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_update_time, preference_time_server, preference_lock_max_timecontrol_entry_count, preference_opener_max_timecontrol_entry_count, preference_enable_bootloop_reset, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, preference_buffer_size, preference_ip_dhcp_enabled, preference_ip_address, preference_ip_subnet, preference_ip_gateway, preference_ip_dns_server, preference_network_hardware, preference_http_auth_type, preference_lock_gemini_pin, @@ -478,7 +482,7 @@ class DebugPreferences preference_query_interval_configuration, preference_query_interval_battery, preference_query_interval_keypad, preference_keypad_control_enabled, preference_keypad_info_enabled, preference_keypad_publish_code, preference_timecontrol_control_enabled, preference_timecontrol_info_enabled, preference_conf_info_enabled, preference_register_as_app, preference_register_opener_as_app, preference_command_nr_of_retries, preference_command_retry_delay, preference_cred_user, - preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_mqtt_ssl_enabled, + preference_cred_password, preference_disable_non_json, preference_publish_authdata, preference_publish_debug_info, preference_mqtt_ssl_enabled, preference_enable_debug_mode, preference_official_hybrid_enabled, preference_query_interval_hybrid_lockstate, preference_official_hybrid_actions, preference_official_hybrid_retry, 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_ble_tx_power, preference_webserial_enabled, preference_find_best_rssi, preference_lock_gemini_enabled, @@ -507,7 +511,7 @@ class DebugPreferences preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi, preference_http_auth_type, preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode, preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled, - preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled + preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode }; std::vector _bytePrefs = { diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index 7ea95eeb..f7efb19d 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -233,12 +233,12 @@ void WebCfgServer::initialize() } else if (value == "debugon") { - _preferences->putBool(preference_publish_debug_info, true); + _preferences->putBool(preference_enable_debug_mode, true); return buildConfirmHtml(request, resp, "Debug On", 3, true); } else if (value == "debugoff") { - _preferences->putBool(preference_publish_debug_info, false); + _preferences->putBool(preference_enable_debug_mode, false); return buildConfirmHtml(request, resp, "Debug Off", 3, true); } else if (value == "export") @@ -1882,6 +1882,16 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S configChanged = true; } } + else if(key == "TIMESRV") + { + if(_preferences->getString(preference_time_server, "pool.ntp.org") != value) + { + _preferences->putString(preference_time_server, value); + Log->print(("Setting changed: ")); + Log->println(key); + configChanged = true; + } + } else if(key == "NWHW") { if(_preferences->getInt(preference_network_hardware, 0) != value.toInt()) @@ -2547,6 +2557,16 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S configChanged = true; } } + else if(key == "DBGHEAP") + { + if(_preferences->getBool(preference_publish_debug_info, false) != (value == "1")) + { + _preferences->putBool(preference_publish_debug_info, (value == "1")); + Log->print(("Setting changed: ")); + Log->println(key); + configChanged = true; + } + } else if(key == "DBGREAD") { if(_preferences->getBool(preference_debug_readable_data, false) != (value == "1")) @@ -3961,7 +3981,7 @@ esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp { buildNavigationMenuEntry(&response, "Custom Ethernet Configuration", "/get?page=custntw"); } - if (_preferences->getBool(preference_publish_debug_info, false)) + if (_preferences->getBool(preference_enable_debug_mode, false)) { buildNavigationMenuEntry(&response, "Advanced Configuration", "/get?page=advanced"); } @@ -4431,6 +4451,7 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic printCheckBox(&response, "DBGREAD", "Enable Nuki readable data debug logging", _preferences->getBool(preference_debug_readable_data, false), ""); printCheckBox(&response, "DBGHEX", "Enable Nuki hex data debug logging", _preferences->getBool(preference_debug_hex_data, false), ""); printCheckBox(&response, "DBGCOMM", "Enable Nuki command debug logging", _preferences->getBool(preference_debug_command, false), ""); + printCheckBox(&response, "DBGHEAP", "Pubish free heap over MQTT", _preferences->getBool(preference_publish_debug_info, false), ""); response.print(""); response.print("
"); @@ -4781,6 +4802,8 @@ esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request, PsychicResp 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, ""); printCheckBox(&response, "UPTIME", "Update Nuki Hub and Lock/Opener time using NTP", _preferences->getBool(preference_update_time, false), ""); + printInputField(&response, "TIMESRV", "NTP server", _preferences->getString(preference_time_server, "pool.ntp.org").c_str(), 255, ""); + response.print(""); response.print("
"); response.print(""); @@ -4967,7 +4990,9 @@ esp_err_t WebCfgServer::buildInfoHtml(PsychicRequest *request, PsychicResponse* #else response.print("Disabled"); #endif - response.print("\nPublish debug information enabled: "); + response.print("\nAdvanced menu enabled: "); + response.print(_preferences->getBool(preference_enable_debug_mode, false) ? "Yes" : "No"); + response.print("\nPublish free heap over MQTT: "); response.print(_preferences->getBool(preference_publish_debug_info, false) ? "Yes" : "No"); response.print("\nNuki connect debug logging enabled: "); response.print(_preferences->getBool(preference_debug_connect, false) ? "Yes" : "No"); diff --git a/src/main.cpp b/src/main.cpp index 43a39cf0..4509efad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,8 +29,7 @@ #include "RestartReason.h" #include "EspMillis.h" #include "NimBLEDevice.h" -#include -#include "esp_sntp.h" +#include "esp_netif_sntp.h" /* #ifdef DEBUG_NUKIHUB @@ -827,9 +826,22 @@ void setup() if(preferences->getBool(preference_update_time, false)) { - sntp_set_sync_interval(12 * 60 * 60 * 1000UL); - sntp_set_time_sync_notification_cb(cbSyncTime); - configTime(0, 0, "pool.ntp.org"); + esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(preferences->getString(preference_time_server, "pool.ntp.org").c_str()); + config.start = false; + config.server_from_dhcp = true; + config.renew_servers_after_new_IP = true; + config.index_of_first_server = 1; + + if (network->networkDeviceType() == NetworkDeviceType::WiFi) + { + config.ip_event_to_renew = IP_EVENT_STA_GOT_IP; + } + else + { + config.ip_event_to_renew = IP_EVENT_ETH_GOT_IP; + } + config.sync_cb = cbSyncTime; + esp_netif_sntp_init(&config); } #endif From a7d539b851e574336d5f83c8849bd81879d04322 Mon Sep 17 00:00:00 2001 From: iranl Date: Wed, 15 Jan 2025 22:31:18 +0100 Subject: [PATCH 5/5] Web configurator improvements --- README.md | 33 ++++++++++++++++++ src/Config.h | 2 -- src/NukiNetwork.cpp | 4 +-- src/WebCfgServer.cpp | 82 ++++++++++++++++++++++---------------------- src/WebCfgServer.h | 1 + 5 files changed, 77 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index c14d9bc4..85cc049f 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,39 @@ Both of the above options will not backup pairing data, so you will have to manu To import settings copy and paste the contents of the JSON file that is created by any of the above export options and select "Import". After importing the device will reboot. +### Advanced Configuration + +The advanced configuration menu is not reachable from the main menu of the web configurator by default.
+You can reach the menu directly by browsing to http://NUKIHUBIP/?get=advanced or enable showing it in the main menu by browsing to http://NUKIHUBIP/?get=debugon once (http://NUKIHUBIP/?get=debugoff to disable). + +Note that the following options can break NukiHub and cause bootloops that will require you to erase your ESP and reflash following the instructions for first-time flashing. + +- Disable Network if not connected within 60s: Enable to allow NukiHub to function without a network connection (for example when only using NukiHub with GPIO) +- Enable Bootloop prevention: Enable to reset the following stack size and max entry settings to default if NukiHub detects a bootloop. +- Char buffer size (min 4096, max 65536): Set the character buffer size, needs to be enlarged to support large amounts of auth/keypad/timecontrol/authorization entries. Default 4096. +- Task size Network (min 12288, max 65536): Set the Network task stack size, needs to be enlarged to support large amounts of auth/keypad/timecontrol/authorization entries. Default 12288. +- Task size Nuki (min 8192, max 65536): Set the Nuki task stack size. Default 8192. +- Max auth log entries (min 1, max 100): The maximum amount of log entries that will be requested from the lock/opener, default 5. +- Max keypad entries (min 1, max 200): The maximum amount of keypad codes that will be requested from the lock/opener, default 10. +- Max timecontrol entries (min 1, max 100): The maximum amount of timecontrol entries that will be requested from the lock/opener, default 10. +- Max authorization entries (min 1, max 100): The maximum amount of authorization entries that will be requested from the lock/opener, default 10. +- Show Pairing secrets on Info page: Enable to show the pairing secrets on the info page. Will be disabled on reboot. +- Manually set lock pairing data: Enable to save the pairing data fields and manually set pairing info for the lock. +- Manually set opener pairing data: Enable to save the pairing data fields and manually set pairing info for the opener. +- Custom URL to update Nuki Hub updater: Set to a HTTPS address to update to a custom NukiHub updater binary on next boot of the NukiHub partition. +- Custom URL to update Nuki Hub: Set to a HTTPS address to update to a custom NukiHub binary on next boot of the NukiHub updater partition. +- Force Lock ID to current ID: Enable to force the current Lock ID, irrespective of the config received from the lock. +- Force Lock Keypad connected: Enable to force NukiHub to function as if a keypad was connected, irrespective of the config received from the lock. +- Force Lock Doorsensor connected: Enable to force NukiHub to function as if a doorsensor was connected, irrespective of the config received from the lock. +- Force Opener ID to current ID: Enable to force the current Opener ID, irrespective of the config received from the opener. +- Force Opener Keypad: Enable to force NukiHub to function as if a keypad was connected, irrespective of the config received from the opener. +- Enable Nuki connect debug logging: Enable to log debug information regarding Nuki BLE connection to MQTT and/or Serial. +- Enable Nuki communication debug logging: Enable to log debug information regarding Nuki BLE communication to MQTT and/or Serial. +- Enable Nuki readable data debug logging: Enable to log human readable debug information regarding Nuki BLE to MQTT and/or Serial. +- Enable Nuki hex data debug logging: Enable to log hex debug information regarding Nuki BLE to MQTT and/or Serial. +- Enable Nuki command debug logging: Enable to log debug information regarding Nuki BLE commands to MQTT and/or Serial. +- Pubish free heap over MQTT: Enable to publish free heap to MQTT. + ## Exposed MQTT Topics ### Lock diff --git a/src/Config.h b/src/Config.h index 85ffa72a..ec1e067d 100644 --- a/src/Config.h +++ b/src/Config.h @@ -120,8 +120,6 @@ #ifndef NUKI_HUB_UPDATER #define MQTT_QOS_LEVEL 1 -#define MQTT_CLEAN_SESSIONS false -#define MQTT_KEEP_ALIVE 60 #define GPIO_DEBOUNCE_TIME 200 #define CHAR_BUFFER_SIZE 4096 #define NUKI_TASK_SIZE 8192 diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index c28d44e9..8cbc306a 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -306,8 +306,8 @@ void NukiNetwork::initialize() Log->println(_mqttPort); _device->mqttSetClientId(_hostnameArr); - _device->mqttSetCleanSession(MQTT_CLEAN_SESSIONS); - _device->mqttSetKeepAlive(MQTT_KEEP_ALIVE); + _device->mqttSetCleanSession(false); + _device->mqttSetKeepAlive(60); char gpioPath[250]; bool rebGpio = rebuildGpio(); diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index f7efb19d..5423b949 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -6,6 +6,7 @@ #include #include "FS.h" #include "SPIFFS.h" +#include "esp_random.h" #ifdef CONFIG_SOC_SPIRAM_SUPPORTED #include "esp_psram.h" #endif @@ -124,11 +125,6 @@ void WebCfgServer::initialize() #ifndef CONFIG_IDF_TARGET_ESP32H2 _psychicServer->on("/ssidlist", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) - { - return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); - } - return buildSSIDListHtml(request, resp); }); _psychicServer->on("/savewifi", HTTP_POST, [&](PsychicRequest *request, PsychicResponse* resp) @@ -186,11 +182,6 @@ void WebCfgServer::initialize() { _psychicServer->on("/get", HTTP_GET, [&](PsychicRequest *request, PsychicResponse* resp) { - if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) - { - return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); - } - String value = ""; if(request->hasParam("page")) { @@ -200,6 +191,14 @@ void WebCfgServer::initialize() value = p->value(); } } + + if (value != "status") + { + if(strlen(_credUser) > 0 && strlen(_credPassword) > 0 && !request->authenticate(_credUser, _credPassword)) + { + return request->requestAuthentication(auth_type, "Nuki Hub", "You must log in."); + } + } if (value == "reboot") { @@ -251,7 +250,18 @@ void WebCfgServer::initialize() } else if (value == "status") { - return buildStatusHtml(request, resp); + if(request->hasParam("token")) + { + const PsychicWebParameter* p2 = request->getParam("token"); + if(p2->value().toInt() == _randomInt) + { + return buildStatusHtml(request, resp); + } + } + resp->setCode(200); + resp->setContentType("text/html"); + resp->setContent(""); + return resp->send(); } else if (value == "acclvl") { @@ -2398,7 +2408,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "TSKNTWK") { - if(value.toInt() > 12287 && value.toInt() < 32769) + if(value.toInt() > 12287 && value.toInt() < 65537) { if(_preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE) != value.toInt()) { @@ -2411,7 +2421,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "TSKNUKI") { - if(value.toInt() > 8191 && value.toInt() < 32769) + if(value.toInt() > 8191 && value.toInt() < 65537) { if(_preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE) != value.toInt()) { @@ -2424,7 +2434,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "ALMAX") { - if(value.toInt() > 0 && value.toInt() < 51) + if(value.toInt() > 0 && value.toInt() < 101) { if(_preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG) != value.toInt()) { @@ -2437,7 +2447,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "KPMAX") { - if(value.toInt() > 0 && value.toInt() < 101) + if(value.toInt() > 0 && value.toInt() < 201) { if(_preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD) != value.toInt()) { @@ -2450,7 +2460,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "TCMAX") { - if(value.toInt() > 0 && value.toInt() < 51) + if(value.toInt() > 0 && value.toInt() < 101) { if(_preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL) != value.toInt()) { @@ -2463,7 +2473,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "AUTHMAX") { - if(value.toInt() > 0 && value.toInt() < 51) + if(value.toInt() > 0 && value.toInt() < 101) { if(_preferences->getInt(preference_auth_max_entries, MAX_AUTH) != value.toInt()) { @@ -2476,7 +2486,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S } else if(key == "BUFFSIZE") { - if(value.toInt() > 4095 && value.toInt() < 32769) + if(value.toInt() > 4095 && value.toInt() < 65537) { if(_preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE) != value.toInt()) { @@ -2566,7 +2576,7 @@ bool WebCfgServer::processArgs(PsychicRequest *request, PsychicResponse* resp, S Log->println(key); configChanged = true; } - } + } else if(key == "DBGREAD") { if(_preferences->getBool(preference_debug_readable_data, false) != (value == "1")) @@ -3904,7 +3914,8 @@ esp_err_t WebCfgServer::buildCustomNetworkConfigHtml(PsychicRequest *request, Ps esp_err_t WebCfgServer::buildHtml(PsychicRequest *request, PsychicResponse* resp) { - String header = ""; + _randomInt = esp_random(); + String header = (String)""; PsychicStreamResponse response(resp, "text/html"); response.beginSend(); buildHtmlHeader(&response, header); @@ -4396,15 +4407,15 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic printCheckBox(&response, "DISNTWNOCON", "Disable Network if not connected within 60s", _preferences->getBool(preference_disable_network_not_connected, false), ""); //printCheckBox(&response, "WEBLOG", "Enable WebSerial logging", _preferences->getBool(preference_webserial_enabled), ""); printCheckBox(&response, "BTLPRST", "Enable Bootloop prevention (Try to reset these settings to default on bootloop)", true, ""); - printInputField(&response, "BUFFSIZE", "Char buffer size (min 4096, max 32768)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6, ""); + printInputField(&response, "BUFFSIZE", "Char buffer size (min 4096, max 65536)", _preferences->getInt(preference_buffer_size, CHAR_BUFFER_SIZE), 6, ""); response.print("Advised minimum char buffer size based on current settings"); - printInputField(&response, "TSKNTWK", "Task size Network (min 12288, max 32768)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6, ""); + printInputField(&response, "TSKNTWK", "Task size Network (min 12288, max 65536)", _preferences->getInt(preference_task_size_network, NETWORK_TASK_SIZE), 6, ""); response.print("Advised minimum network task size based on current settings"); - printInputField(&response, "TSKNUKI", "Task size Nuki (min 8192, max 32768)", _preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), 6, ""); - printInputField(&response, "ALMAX", "Max auth log entries (min 1, max 50)", _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 3, "id=\"inputmaxauthlog\""); - printInputField(&response, "KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "id=\"inputmaxkeypad\""); - printInputField(&response, "TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "id=\"inputmaxtimecontrol\""); - printInputField(&response, "AUTHMAX", "Max authorization entries (min 1, max 50)", _preferences->getInt(preference_auth_max_entries, MAX_AUTH), 3, "id=\"inputmaxauth\""); + printInputField(&response, "TSKNUKI", "Task size Nuki (min 8192, max 65536)", _preferences->getInt(preference_task_size_nuki, NUKI_TASK_SIZE), 6, ""); + printInputField(&response, "ALMAX", "Max auth log entries (min 1, max 100)", _preferences->getInt(preference_authlog_max_entries, MAX_AUTHLOG), 3, "id=\"inputmaxauthlog\""); + printInputField(&response, "KPMAX", "Max keypad entries (min 1, max 200)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "id=\"inputmaxkeypad\""); + printInputField(&response, "TCMAX", "Max timecontrol entries (min 1, max 100)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "id=\"inputmaxtimecontrol\""); + printInputField(&response, "AUTHMAX", "Max authorization entries (min 1, max 100)", _preferences->getInt(preference_auth_max_entries, MAX_AUTH), 3, "id=\"inputmaxauth\""); printCheckBox(&response, "SHOWSECRETS", "Show Pairing secrets on Info page", _preferences->getBool(preference_show_secrets), ""); if(_preferences->getBool(preference_lock_enabled, true)) { @@ -4424,11 +4435,6 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic printInputField(&response, "OTAUPD", "Custom URL to update Nuki Hub updater", "", 255, ""); printInputField(&response, "OTAMAIN", "Custom URL to update Nuki Hub", "", 255, ""); - std::vector> optionsForce; - optionsForce.push_back(std::make_pair("0", "Do not force")); - optionsForce.push_back(std::make_pair("1", "Force unavailable")); - optionsForce.push_back(std::make_pair("2", "Force available")); - if(_nuki != nullptr) { char uidString[20]; @@ -4456,7 +4462,7 @@ esp_err_t WebCfgServer::buildAdvancedConfigHtml(PsychicRequest *request, Psychic response.print("
"); response.print(""); - response.print(""); + response.print(""); return response.endSend(); } @@ -4467,7 +4473,6 @@ esp_err_t WebCfgServer::buildStatusHtml(PsychicRequest *request, PsychicResponse bool mqttDone = false; bool lockDone = false; bool openerDone = false; - bool latestDone = false; json["stop"] = 0; @@ -4545,14 +4550,9 @@ esp_err_t WebCfgServer::buildStatusHtml(PsychicRequest *request, PsychicResponse if(_preferences->getBool(preference_check_updates)) { json["latestFirmware"] = _preferences->getString(preference_latest_version); - latestDone = true; - } - else - { - latestDone = true; } - if(mqttDone && lockDone && openerDone && latestDone) + if(mqttDone && lockDone && openerDone) { json["stop"] = 1; } @@ -4803,7 +4803,7 @@ esp_err_t WebCfgServer::buildNukiConfigHtml(PsychicRequest *request, PsychicResp printInputField(&response, "TXPWR", "BLE transmit power in dB (minimum -12, maximum 9)", _preferences->getInt(preference_ble_tx_power, 9), 10, ""); printCheckBox(&response, "UPTIME", "Update Nuki Hub and Lock/Opener time using NTP", _preferences->getBool(preference_update_time, false), ""); printInputField(&response, "TIMESRV", "NTP server", _preferences->getString(preference_time_server, "pool.ntp.org").c_str(), 255, ""); - + response.print(""); response.print("
"); response.print(""); diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index 0865d815..67ed731b 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -128,5 +128,6 @@ class WebCfgServer bool _allowRestartToPortal = false; uint8_t _partitionType = 0; size_t _otaContentLen = 0; + uint32_t _randomInt = 0; String _hostname; };