From 7cb31f8c2273ac9f57f31f95987dde487803fc43 Mon Sep 17 00:00:00 2001 From: iranl Date: Tue, 14 Jan 2025 18:25:46 +0100 Subject: [PATCH] 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 2bb49f4..cba8669 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 6b73fb4..ea0aabb 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 48a755f..dc0696e 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 896e40e..a180fe9 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 3c109c6..b40d88d 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 be54d16..b5cdf38 100644 --- a/src/Gpio.h +++ b/src/Gpio.h @@ -59,8 +59,6 @@ public: 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 b039bfb..6149bd1 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 8ffa17a..c28d44e 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 45e2005..fa61ba9 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 37a45b8..e9ca80d 100644 --- a/src/NukiNetworkLock.h +++ b/src/NukiNetworkLock.h @@ -68,6 +68,7 @@ private: 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 @@ private: 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 07ed347..c672cff 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 40f52a5..7dbe709 100644 --- a/src/NukiWrapper.h +++ b/src/NukiWrapper.h @@ -30,7 +30,9 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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 b11b3f8..f94be25 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 @@ private: 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 @@ private: 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 cfcde2e..0ea95e1 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