From 1db1022819a0470879b4b35094f54d561dd168ca Mon Sep 17 00:00:00 2001 From: technyon Date: Sat, 9 Apr 2022 20:20:52 +0200 Subject: [PATCH] allow to set config values --- Network.cpp | 31 +++++++++++++++++++++++---- Network.h | 9 ++++++-- NukiWrapper.cpp | 44 +++++++++++++++++++++++++++++++++++---- NukiWrapper.h | 6 +++++- WebCfgServer.cpp | 54 +++++++++++++++++++++++++++++++++++++++--------- WebCfgServer.h | 4 ++-- 6 files changed, 125 insertions(+), 23 deletions(-) diff --git a/Network.cpp b/Network.cpp index b0f75ce..5ea6a54 100644 --- a/Network.cpp +++ b/Network.cpp @@ -12,6 +12,11 @@ Network::Network(Preferences* preferences) _preferences(preferences) { nwInst = this; + + _configTopics.reserve(3); + _configTopics.push_back(mqtt_topic_config_button_enabled); + _configTopics.push_back(mqtt_topic_config_led_enabled); + _configTopics.push_back(mqtt_topic_config_led_brightness); } void Network::initialize() @@ -129,9 +134,11 @@ bool Network::reconnect() _mqttConnected = true; delay(200); subscribe(mqtt_topic_lockstate_action); - subscribe(mqtt_topic_config_button_enabled); - subscribe(mqtt_topic_config_led_enabled); - subscribe(mqtt_topic_config_led_brightness); + + for(auto topic : _configTopics) + { + subscribe(topic); + } } else { @@ -197,6 +204,17 @@ void Network::onMqttDataReceived(char *&topic, byte *&payload, unsigned int &len } publishString(mqtt_topic_lockstate_action, ""); } + + for(auto configTopic : _configTopics) + { + if(comparePrefixedPath(topic, configTopic)) + { + if(_configUpdateReceivedCallback != nullptr) + { + _configUpdateReceivedCallback(configTopic, value); + } + } + } } void Network::publishKeyTurnerState(const Nuki::KeyTurnerState& keyTurnerState, const Nuki::KeyTurnerState& lastKeyTurnerState) @@ -264,11 +282,16 @@ void Network::publishPresenceDetection(char *csv) _presenceCsv = csv; } -void Network::setLockActionReceived(void (*lockActionReceivedCallback)(const char *)) +void Network::setLockActionReceivedCallback(void (*lockActionReceivedCallback)(const char *)) { _lockActionReceivedCallback = lockActionReceivedCallback; } +void Network::setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char *, const char *)) +{ + _configUpdateReceivedCallback = configUpdateReceivedCallback; +} + void Network::publishFloat(const char* topic, const float value, const uint8_t precision) { char str[30]; diff --git a/Network.h b/Network.h index c72918b..55a9fb1 100644 --- a/Network.h +++ b/Network.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "NukiConstants.h" #include "SpiffsCookie.h" @@ -22,7 +23,8 @@ public: void publishConfig(const Nuki::Config& config); void publishPresenceDetection(char* csv); - void setLockActionReceived(void (*lockActionReceivedCallback)(const char* value)); + void setLockActionReceivedCallback(void (*lockActionReceivedCallback)(const char* value)); + void setConfigUpdateReceivedCallback(void (*configUpdateReceivedCallback)(const char* path, const char* value)); void restartAndConfigureWifi(); @@ -56,7 +58,10 @@ private: char* _presenceCsv = nullptr; + std::vector _configTopics; + bool _firstTunerStatePublish = true; - void (*_lockActionReceivedCallback)(const char* value) = NULL; + void (*_lockActionReceivedCallback)(const char* value) = nullptr; + void (*_configUpdateReceivedCallback)(const char* path, const char* value) = nullptr; }; diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 4659be8..8cfaccb 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -1,6 +1,7 @@ #include "NukiWrapper.h" #include #include "PreferencesKeys.h" +#include "MqttTopics.h" NukiWrapper* nukiInst; @@ -18,7 +19,8 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, uint32_t id, Network* ne memset(&_keyTurnerState, sizeof(Nuki::KeyTurnerState), 0); _keyTurnerState.lockState = Nuki::LockState::Undefined; - network->setLockActionReceived(nukiInst->onLockActionReceived); + network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback); + network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback); } @@ -112,6 +114,11 @@ void NukiWrapper::update() memcpy(&_lastKeyTurnerState, &_keyTurnerState, sizeof(Nuki::KeyTurnerState)); } +void NukiWrapper::setPin(const uint16_t pin) +{ + _nukiBle.saveSecurityPincode(pin); +} + void NukiWrapper::updateKeyTurnerState() { _nukiBle.requestKeyTurnerState(&_keyTurnerState); @@ -162,11 +169,42 @@ Nuki::LockAction NukiWrapper::lockActionToEnum(const char *str) return (Nuki::LockAction)0xff; } -void NukiWrapper::onLockActionReceived(const char *value) +void NukiWrapper::onLockActionReceivedCallback(const char *value) { nukiInst->_nextLockAction = nukiInst->lockActionToEnum(value); } +void NukiWrapper::onConfigUpdateReceivedCallback(const char *topic, const char *value) +{ + nukiInst->onConfigUpdateReceived(topic, value); +} + + +void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value) +{ + if(strcmp(topic, mqtt_topic_config_button_enabled) == 0) + { + bool newValue = atoi(value) > 0; + if(!_nukiConfigValid || _nukiConfig.buttonEnabled == newValue) return; + _nukiBle.enableButton(newValue); + _nextConfigUpdateTs = millis() + 300; + } + if(strcmp(topic, mqtt_topic_config_led_enabled) == 0) + { + bool newValue = atoi(value) > 0; + if(!_nukiConfigValid || _nukiConfig.ledEnabled == newValue) return; + _nukiBle.enableLedFlash(newValue); + _nextConfigUpdateTs = millis() + 300; + } + else if(strcmp(topic, mqtt_topic_config_led_brightness) == 0) + { + int newValue = atoi(value); + if(!_nukiConfigValid || _nukiConfig.ledBrightness == newValue) return; + _nukiBle.setLedBrightness(newValue); + _nextConfigUpdateTs = millis() + 300; + } +} + const Nuki::KeyTurnerState &NukiWrapper::keyTurnerState() { return _keyTurnerState; @@ -192,8 +230,6 @@ void NukiWrapper::notify(Nuki::EventType eventType) void NukiWrapper::readConfig() { - Serial.print(F("Reading config. Result: ")); Nuki::CmdResult result = _nukiBle.requestConfig(&_nukiConfig); _nukiConfigValid = result == Nuki::CmdResult::Success; - Serial.println(result); } diff --git a/NukiWrapper.h b/NukiWrapper.h index deef33b..9dec384 100644 --- a/NukiWrapper.h +++ b/NukiWrapper.h @@ -14,6 +14,8 @@ public: void initialize(); void update(); + void setPin(const uint16_t pin); + const Nuki::KeyTurnerState& keyTurnerState(); const bool isPaired(); @@ -22,7 +24,9 @@ public: void notify(Nuki::EventType eventType) override; private: - static void onLockActionReceived(const char* value); + static void onLockActionReceivedCallback(const char* value); + static void onConfigUpdateReceivedCallback(const char* topic, const char* value); + void onConfigUpdateReceived(const char* topic, const char* value); void updateKeyTurnerState(); void updateBatteryState(); diff --git a/WebCfgServer.cpp b/WebCfgServer.cpp index fa5eaa7..bb2cf81 100644 --- a/WebCfgServer.cpp +++ b/WebCfgServer.cpp @@ -57,30 +57,38 @@ void WebCfgServer::initialize() String response = ""; buildConfirmHtml(response, "Restarting. Connect to ESP access point to reconfigure WiFi.", 0); server.send(200, "text/html", response); - waitAndProcess(2000); + waitAndProcess(true, 2000); _network->restartAndConfigureWifi(); }); server.on("/method=get", [&]() { if (_hasCredentials && !server.authenticate(_credUser, _credPassword)) { return server.requestAuthentication(); } - bool configChanged = processArgs(); - if(configChanged) + String message = ""; + bool restartEsp = processArgs(message); + if(restartEsp) { String response = ""; - buildConfirmHtml(response, "Configuration saved ... restarting."); + buildConfirmHtml(response, message); server.send(200, "text/html", response); Serial.println(F("Restarting")); - waitAndProcess(1000); + waitAndProcess(true, 1000); ESP.restart(); } + else + { + String response = ""; + buildConfirmHtml(response, message, 3); + server.send(200, "text/html", response); + waitAndProcess(false, 1000); + } }); server.begin(); } -bool WebCfgServer::processArgs() +bool WebCfgServer::processArgs(String& message) { bool configChanged = false; bool clearMqttCredentials = false; @@ -159,6 +167,19 @@ bool WebCfgServer::processArgs() _preferences->putString(preference_cred_password, value); configChanged = true; } + else if(key == "NUKIPIN") + { + if(value == "#") + { + message = "PIN cleared"; + _nuki->setPin(0xffff); + } + else + { + message = "PIN saved"; + _nuki->setPin(value.toInt()); + } + } } if(clearMqttCredentials) @@ -177,6 +198,7 @@ bool WebCfgServer::processArgs() if(configChanged) { + message = "Configuration saved ... restarting."; _enabled = false; _preferences->end(); } @@ -250,15 +272,20 @@ void WebCfgServer::buildCredHtml(String &response) buildHtmlHeader(response); response.concat("
"); - response.concat("

Credentials

"); response.concat(""); printInputField(response, "CREDUSER", "User (# to clear)", _preferences->getString(preference_cred_user).c_str(), 20); printInputField(response, "CREDPASS", "Password", "*", 30, true); response.concat("
"); - response.concat("
"); + response.concat("
"); + response.concat("

"); + response.concat("

NUKI Pin Code

"); + response.concat(""); + printInputField(response, "NUKIPIN", "PIN Code (# to clear)", "*", 20, true); + response.concat("
"); + response.concat("
"); response.concat("
"); response.concat("\n"); @@ -361,12 +388,19 @@ void WebCfgServer::printParameter(String& response, const char *description, con } -void WebCfgServer::waitAndProcess(const uint32_t duration) +void WebCfgServer::waitAndProcess(const bool blocking, const uint32_t duration) { unsigned long timeout = millis() + duration; while(millis() < timeout) { server.handleClient(); - delay(10); + if(blocking) + { + delay(10); + } + else + { + vTaskDelay( 50 / portTICK_PERIOD_MS); + } } } diff --git a/WebCfgServer.h b/WebCfgServer.h index a832770..e07fc4b 100644 --- a/WebCfgServer.h +++ b/WebCfgServer.h @@ -28,7 +28,7 @@ public: private: - bool processArgs(); + bool processArgs(String& message); void buildHtml(String& response); void buildCredHtml(String& response); void buildConfirmHtml(String& response, const String &message, uint32_t redirectDelay = 5); @@ -40,7 +40,7 @@ private: void printParameter(String& response, const char* description, const char* value); - void waitAndProcess(const uint32_t duration); + void waitAndProcess(const bool blocking, const uint32_t duration); WebServer server; NukiWrapper* _nuki;