diff --git a/src/NukiNetworkLock.cpp b/src/NukiNetworkLock.cpp index 30eb9ef..e246b7d 100644 --- a/src/NukiNetworkLock.cpp +++ b/src/NukiNetworkLock.cpp @@ -7,8 +7,12 @@ #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"); +extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end"); NukiNetworkLock::NukiNetworkLock(NukiNetwork* network, Preferences* preferences, char* buffer, size_t bufferSize) : _network(network), @@ -204,30 +208,102 @@ void NukiNetworkLock::onMqttDataReceived(const char* topic, byte* payload, const else if(comparePrefixedPath(topic, mqtt_topic_update) && strcmp(value, "1") == 0 && _preferences->getBool(preference_update_from_mqtt, false)) { Log->println(F("Update requested via MQTT.")); - String currentVersion = NUKI_HUB_VERSION; + + bool otaManifestSuccess = false; + JsonDocument doc; - if(atof(_preferences->getString(preference_latest_version).c_str()) >= atof(currentVersion.c_str())) - { - _preferences->putString(preference_ota_updater_url, GITHUB_LATEST_UPDATER_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_LATEST_RELEASE_BINARY_URL); + 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; } - else if(currentVersion.indexOf("beta") > 0) + + if (otaManifestSuccess) { - _preferences->putString(preference_ota_updater_url, GITHUB_BETA_RELEASE_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_BETA_UPDATER_BINARY_URL); - } - else if(currentVersion.indexOf("master") > 0) - { - _preferences->putString(preference_ota_updater_url, GITHUB_MASTER_RELEASE_BINARY_URL); - _preferences->putString(preference_ota_main_url, GITHUB_MASTER_UPDATER_BINARY_URL); + 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 { - _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("Failed to retrieve OTA manifest, OTA update aborted.")); } - delay(200); - restartEsp(RestartReason::OTAReboot); } else if(comparePrefixedPath(topic, mqtt_topic_webserver_action)) { diff --git a/src/NukiOpenerWrapper.cpp b/src/NukiOpenerWrapper.cpp index 34448e9..2eadaef 100644 --- a/src/NukiOpenerWrapper.cpp +++ b/src/NukiOpenerWrapper.cpp @@ -2906,7 +2906,10 @@ void NukiOpenerWrapper::readConfig() char resultStr[20]; NukiOpener::cmdResultToString(result, resultStr); Log->print(F("Opener config result: ")); - Log->println(resultStr); + Log->print(resultStr); + Log->print("("); + Log->print(result); + Log->println(")"); postponeBleWatchdog(); } diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index a6e4bd5..78e076f 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -283,9 +283,9 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) #else String build_type = "debug"; #endif - response->print("

"); - response->print("

"); - response->print("

"); + response->print("

"); + response->print("

"); + response->print("

"); response->print("

"); response->print("Current version: "); @@ -358,6 +358,16 @@ void WebCfgServer::buildOtaHtml(AsyncWebServerRequest *request, bool debug) response->print("), "); response->print(doc["master"]["time"].as()); response->print("
"); + + String currentVersion = NUKI_HUB_VERSION; + const char* latestVersion; + + if(atof(doc["release"]["version"]) >= atof(currentVersion.c_str())) latestVersion = doc["release"]["fullversion"]; + else if(currentVersion.indexOf("beta") > 0) latestVersion = doc["beta"]["fullversion"]; + else if(currentVersion.indexOf("master") > 0) latestVersion = doc["master"]["fullversion"]; + else latestVersion = doc["release"]["fullversion"]; + + if(strcmp(latestVersion, _preferences->getString(preference_latest_version).c_str()) != 0) _preferences->putString(preference_latest_version, latestVersion); } #endif response->print("
"); @@ -2746,9 +2756,9 @@ void WebCfgServer::buildAdvancedConfigHtml(AsyncWebServerRequest *request) printInputField(response, "TSKNTWK", "Task size Network (min 12288, max 32768)", _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, "inputmaxauthlog"); - printInputField(response, "KPMAX", "Max keypad entries (min 1, max 100)", _preferences->getInt(preference_keypad_max_entries, MAX_KEYPAD), 3, "inputmaxkeypad"); - printInputField(response, "TCMAX", "Max timecontrol entries (min 1, max 50)", _preferences->getInt(preference_timecontrol_max_entries, MAX_TIMECONTROL), 3, "inputmaxtimecontrol"); + 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\""); printCheckBox(response, "SHOWSECRETS", "Show Pairing secrets on Info page (for 120s after next boot)", _preferences->getBool(preference_show_secrets), ""); if(_preferences->getBool(preference_lock_enabled, true)) { @@ -3875,7 +3885,7 @@ void WebCfgServer::printInputField(AsyncResponseStream *response, const char *description, const char *value, const size_t& maxLength, - const char *id, + const char *args, const bool& isPassword, const bool& showLengthRestriction) { @@ -3896,19 +3906,10 @@ void WebCfgServer::printInputField(AsyncResponseStream *response, response->print(""); response->print("print(id); - } - else - { - response->print(" id=\""); - response->print(id); - response->print("\""); - } + response->print(" "); + response->print(args); } if(strcmp(value, "") != 0) { @@ -3928,11 +3929,11 @@ void WebCfgServer::printInputField(AsyncResponseStream *response, const char *description, const int value, size_t maxLength, - const char *id) + const char *args) { char valueStr[20]; itoa(value, valueStr, 10); - printInputField(response, token, description, valueStr, maxLength, id); + printInputField(response, token, description, valueStr, maxLength, args); } void WebCfgServer::printCheckBox(AsyncResponseStream *response, const char *token, const char *description, const bool value, const char *htmlClass) diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index 770575e..d85619b 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -68,8 +68,8 @@ private: void processUnpair(AsyncWebServerRequest *request, bool opener); void processUpdate(AsyncWebServerRequest *request); void processFactoryReset(AsyncWebServerRequest *request); - void printInputField(AsyncResponseStream *response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* id, const bool& isPassword = false, const bool& showLengthRestriction = false); - void printInputField(AsyncResponseStream *response, const char* token, const char* description, const int value, size_t maxLength, const char* id); + void printInputField(AsyncResponseStream *response, const char* token, const char* description, const char* value, const size_t& maxLength, const char* args, const bool& isPassword = false, const bool& showLengthRestriction = false); + void printInputField(AsyncResponseStream *response, const char* token, const char* description, const int value, size_t maxLength, const char* args); void printCheckBox(AsyncResponseStream *response, const char* token, const char* description, const bool value, const char* htmlClass); void printCheckBox(String &partString, const char* token, const char* description, const bool value, const char* htmlClass); void printTextarea(AsyncResponseStream *response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false);