diff --git a/src/Config.h b/src/Config.h index 7ef96ef..fc4c53e 100644 --- a/src/Config.h +++ b/src/Config.h @@ -4,7 +4,7 @@ #define NUKI_HUB_VERSION "9.02" #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "2024-11-04" +#define NUKI_HUB_DATE "2024-11-06" #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/NukiNetwork.cpp b/src/NukiNetwork.cpp index 4b91d97..23cc9fa 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -237,6 +237,14 @@ void NukiNetwork::initialize() if(!disableNetwork) { + String mqttPath = _preferences->getString(preference_mqtt_lock_path, ""); + + size_t len = mqttPath.length(); + for(int i=0; i < len; i++) + { + _nukiHubPath[i] = mqttPath.charAt(i); + } + _hostname = _preferences->getString(preference_hostname, ""); if(_hostname == "") @@ -719,6 +727,32 @@ void NukiNetwork::initTopic(const char *prefix, const char *path, const char *va _initTopics[pathStr] = valueStr; } +void NukiNetwork::buildMqttPath(const char *path, char *outPath) +{ + int offset = 0; + char inPath[181] = {0}; + + memcpy(inPath, _nukiHubPath, sizeof(_nukiHubPath)); + + for(const char& c : inPath) + { + if(c == 0x00) + { + break; + } + outPath[offset] = c; + ++offset; + } + int i=0; + while(outPath[i] != 0x00) + { + outPath[offset] = path[i]; + ++i; + ++offset; + } + outPath[i+1] = 0x00; +} + void NukiNetwork::buildMqttPath(char* outPath, std::initializer_list paths) { int offset = 0; @@ -769,6 +803,8 @@ void NukiNetwork::onMqttDataReceived(const espMqttClientTypes::MessageProperties if(_mqttConnectedTs == -1 || (millis() - _mqttConnectedTs < 2000)) return; parseGpioTopics(properties, topic, payload, len, index, total); + + onMqttDataReceived(topic, (byte*)payload, index); for(auto receiver : _mqttReceivers) { @@ -776,6 +812,155 @@ void NukiNetwork::onMqttDataReceived(const espMqttClientTypes::MessageProperties } } +void NukiNetwork::onMqttDataReceived(const char* topic, byte* payload, const unsigned int length) +{ + char* data = (char*)payload; + + if(comparePrefixedPath(topic, mqtt_topic_reset) && strcmp(data, "1") == 0) + { + Log->println(F("Restart requested via MQTT.")); + clearWifiFallback(); + delay(200); + restartEsp(RestartReason::RequestedViaMqtt); + } + else if(comparePrefixedPath(topic, mqtt_topic_update) && strcmp(data, "1") == 0 && _preferences->getBool(preference_update_from_mqtt, false)) + { + Log->println(F("Update requested via MQTT.")); + + bool otaManifestSuccess = false; + JsonDocument doc; + + NetworkClientSecure *client = new NetworkClientSecure; + if (client) + { + client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start); + { + HTTPClient https; + https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + https.useHTTP10(true); + + if (https.begin(*client, GITHUB_OTA_MANIFEST_URL)) + { + int httpResponseCode = https.GET(); + + if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY) + { + DeserializationError jsonError = deserializeJson(doc, https.getStream()); + + if (!jsonError) + { + otaManifestSuccess = true; + } + } + } + https.end(); + } + delete client; + } + + if (otaManifestSuccess) + { + String currentVersion = NUKI_HUB_VERSION; + + if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) + { + if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as()) == 0) + { + Log->println(F("Nuki Hub is already on the latest release version, OTA update aborted.")); + } + else + { + _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); + _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); + Log->println(F("Updating to latest release version.")); + delay(200); + restartEsp(RestartReason::OTAReboot); + } + } + else if(currentVersion.indexOf("beta") > 0) + { + if(strcmp(NUKI_HUB_VERSION, doc["beta"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["beta"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["beta"]["time"].as()) == 0) + { + Log->println(F("Nuki Hub is already on the latest beta version, OTA update aborted.")); + } + else + { + _preferences->putString(preference_ota_updater_url, GITHUB_BETA_RELEASE_BINARY_URL); + _preferences->putString(preference_ota_main_url, GITHUB_BETA_UPDATER_BINARY_URL); + Log->println(F("Updating to latest beta version.")); + delay(200); + restartEsp(RestartReason::OTAReboot); + } + } + else if(currentVersion.indexOf("master") > 0) + { + if(strcmp(NUKI_HUB_VERSION, doc["master"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["master"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["master"]["time"].as()) == 0) + { + Log->println(F("Nuki Hub is already on the latest development version, OTA update aborted.")); + } + else + { + _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_RELEASE_BINARY_URL); + _preferences->putString(preference_ota_main_url, GITHUB_MASTER_UPDATER_BINARY_URL); + Log->println(F("Updating to latest developmemt version.")); + delay(200); + restartEsp(RestartReason::OTAReboot); + } + } + else + { + if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as()) == 0) + { + Log->println(F("Nuki Hub is already on the latest release version, OTA update aborted.")); + } + else + { + _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); + _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); + Log->println(F("Updating to latest release version.")); + delay(200); + restartEsp(RestartReason::OTAReboot); + } + } + } + else + { + Log->println(F("Failed to retrieve OTA manifest, OTA update aborted.")); + } + } + else if(comparePrefixedPath(topic, mqtt_topic_webserver_action)) + { + if(strcmp(data, "") == 0 || + strcmp(data, "--") == 0) + { + return; + } + + if(strcmp(data, "1") == 0) + { + if(_preferences->getBool(preference_webserver_enabled, true) || forceEnableWebServer) + { + return; + } + Log->println(F("Webserver enabled, restarting.")); + _preferences->putBool(preference_webserver_enabled, true); + + } + else if (strcmp(data, "0") == 0) + { + if(!_preferences->getBool(preference_webserver_enabled, true) && !forceEnableWebServer) + { + return; + } + Log->println(F("Webserver disabled, restarting.")); + _preferences->putBool(preference_webserver_enabled, false); + } + + clearWifiFallback(); + delay(200); + restartEsp(RestartReason::ReconfigureWebServer); + } +} void NukiNetwork::parseGpioTopics(const espMqttClientTypes::MessageProperties &properties, const char *topic, const uint8_t *payload, size_t& len, size_t& index, size_t& total) { @@ -4046,6 +4231,14 @@ uint16_t NukiNetwork::subscribe(const char *topic, uint8_t qos) return _device->mqttSubscribe(topic, qos); } +bool NukiNetwork::comparePrefixedPath(const char *fullPath, const char *subPath) +{ + char prefixedPath[500]; + buildMqttPath(subPath, prefixedPath); + + return strcmp(fullPath, prefixedPath) == 0; +} + void NukiNetwork::addReconnectedCallback(std::function reconnectedCallback) { _reconnectedCallbacks.push_back(reconnectedCallback); diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index a867311..0cf6265 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -119,10 +119,12 @@ private: #ifndef NUKI_HUB_UPDATER static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); void onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); + void onMqttDataReceived(const char* topic, byte* payload, const unsigned int length); void onMqttConnect(const bool& sessionPresent); void onMqttDisconnect(const espMqttClientTypes::DisconnectReason& reason); void parseGpioTopics(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); void gpioActionCallback(const GpioAction& action, const int& pin); + bool comparePrefixedPath(const char* fullPath, const char* subPath); String createHassTopicPath(const String& mqttDeviceType, const String& mqttDeviceName, const String& uidString); JsonDocument createHassJson(const String& uidString, @@ -139,6 +141,7 @@ private: std::vector> additionalEntries = {} ); void buildMqttPath(char* outPath, std::initializer_list paths); + void buildMqttPath(const char *path, char *outPath); const char* _lastWillPayload = "offline"; char _mqttConnectionStateTopic[211] = {0}; diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 9855787..1086f02 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -7,8 +7,6 @@ #include "RestartReason.h" #include #include -#include -#include extern bool forceEnableWebServer; extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_bundle_start"); @@ -169,152 +167,7 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const return; } - if(comparePrefixedPath(topic, mqtt_topic_reset) && strcmp(data, "1") == 0) - { - Log->println(F("Restart requested via MQTT.")); - _network->clearWifiFallback(); - delay(200); - restartEsp(RestartReason::RequestedViaMqtt); - } - else if(comparePrefixedPath(topic, mqtt_topic_update) && strcmp(data, "1") == 0 && _preferences->getBool(preference_update_from_mqtt, false)) - { - Log->println(F("Update requested via MQTT.")); - - bool otaManifestSuccess = false; - JsonDocument doc; - - NetworkClientSecure *client = new NetworkClientSecure; - if (client) - { - client->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start); - { - HTTPClient https; - https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - https.useHTTP10(true); - - if (https.begin(*client, GITHUB_OTA_MANIFEST_URL)) - { - int httpResponseCode = https.GET(); - - if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY) - { - DeserializationError jsonError = deserializeJson(doc, https.getStream()); - - if (!jsonError) - { - otaManifestSuccess = true; - } - } - } - https.end(); - } - delete client; - } - - if (otaManifestSuccess) - { - String currentVersion = NUKI_HUB_VERSION; - - if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) - { - if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as()) == 0) - { - Log->println(F("Nuki Hub is already on the latest release version, OTA update aborted.")); - } - else - { - _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); - Log->println(F("Updating to latest release version.")); - delay(200); - restartEsp(RestartReason::OTAReboot); - } - } - else if(currentVersion.indexOf("beta") > 0) - { - if(strcmp(NUKI_HUB_VERSION, doc["beta"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["beta"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["beta"]["time"].as()) == 0) - { - Log->println(F("Nuki Hub is already on the latest beta version, OTA update aborted.")); - } - else - { - _preferences->putString(preference_ota_updater_url, GITHUB_BETA_RELEASE_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_BETA_UPDATER_BINARY_URL); - Log->println(F("Updating to latest beta version.")); - delay(200); - restartEsp(RestartReason::OTAReboot); - } - } - else if(currentVersion.indexOf("master") > 0) - { - if(strcmp(NUKI_HUB_VERSION, doc["master"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["master"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["master"]["time"].as()) == 0) - { - Log->println(F("Nuki Hub is already on the latest development version, OTA update aborted.")); - } - else - { - _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_RELEASE_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_MASTER_UPDATER_BINARY_URL); - Log->println(F("Updating to latest developmemt version.")); - delay(200); - restartEsp(RestartReason::OTAReboot); - } - } - else - { - if(strcmp(NUKI_HUB_VERSION, doc["release"]["fullversion"].as()) == 0 && strcmp(NUKI_HUB_BUILD, doc["release"]["build"].as()) == 0 && strcmp(NUKI_HUB_DATE, doc["release"]["time"].as()) == 0) - { - Log->println(F("Nuki Hub is already on the latest release version, OTA update aborted.")); - } - else - { - _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); - Log->println(F("Updating to latest release version.")); - delay(200); - restartEsp(RestartReason::OTAReboot); - } - } - } - else - { - Log->println(F("Failed to retrieve OTA manifest, OTA update aborted.")); - } - } - else if(comparePrefixedPath(topic, mqtt_topic_webserver_action)) - { - if(strcmp(data, "") == 0 || - strcmp(data, "--") == 0) - { - return; - } - - if(strcmp(data, "1") == 0) - { - if(_preferences->getBool(preference_webserver_enabled, true) || forceEnableWebServer) - { - return; - } - Log->println(F("Webserver enabled, restarting.")); - _preferences->putBool(preference_webserver_enabled, true); - - } - else if (strcmp(data, "0") == 0) - { - if(!_preferences->getBool(preference_webserver_enabled, true) && !forceEnableWebServer) - { - return; - } - Log->println(F("Webserver disabled, restarting.")); - _preferences->putBool(preference_webserver_enabled, false); - } - - publishString(mqtt_topic_webserver_action, "--", true); - _network->clearWifiFallback(); - delay(200); - restartEsp(RestartReason::ReconfigureWebServer); - } - else if(comparePrefixedPath(topic, mqtt_topic_lock_log_rolling_last)) + if(comparePrefixedPath(topic, mqtt_topic_lock_log_rolling_last)) { if(strcmp(data, "") == 0 || strcmp(data, "--") == 0)