From 0d38a31327f4dfb9f026fa0ed77558242677e1ed Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Fri, 5 Jul 2024 23:20:08 +0700 Subject: [PATCH 01/36] feat: implement module commands. --- examples/OTAUpdate/OTAHelper.h | 170 ++++++++++++++++++++++++++++ examples/OTAUpdate/OTAUpdate.ino | 109 ++++++++++++++++++ examples/Settings/Settings.ino | 153 +++++++++++++++++++++++++ library.json | 2 +- library.properties | 2 +- src/SinricPro.h | 128 ++++++++++++++++++++- src/SinricProModuleCommandHandler.h | 86 ++++++++++++++ src/SinricProStrings.h | 3 + src/SinricProVersion.h | 4 +- 9 files changed, 648 insertions(+), 9 deletions(-) create mode 100644 examples/OTAUpdate/OTAHelper.h create mode 100644 examples/OTAUpdate/OTAUpdate.ino create mode 100644 examples/Settings/Settings.ino create mode 100644 src/SinricProModuleCommandHandler.h diff --git a/examples/OTAUpdate/OTAHelper.h b/examples/OTAUpdate/OTAHelper.h new file mode 100644 index 00000000..82888014 --- /dev/null +++ b/examples/OTAUpdate/OTAHelper.h @@ -0,0 +1,170 @@ +#include +#include +#include +#include + +// Task handle for the update task +TaskHandle_t updateTaskHandle = NULL; + +// Cloudflare Root CA Certificate (Baltimore CyberTrust Root) +const char rootCACertificate[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- +)EOF"; + +struct Version { + int major; + int minor; + int patch; + + String toString() const { + return String(major) + "." + String(minor) + "." + String(patch); + } +}; + +Version parseVersion(const String& versionStr) { + Version v; + int firstDot = versionStr.indexOf('.'); + int secondDot = versionStr.lastIndexOf('.'); + v.major = versionStr.substring(0, firstDot).toInt(); + v.minor = versionStr.substring(firstDot + 1, secondDot).toInt(); + v.patch = versionStr.substring(secondDot + 1).toInt(); + return v; +} + +bool isNewerVersion(const Version& currentVersion, const Version& newVersion) { + if (newVersion.major > currentVersion.major) return true; + if (newVersion.major < currentVersion.major) return false; + + if (newVersion.minor > currentVersion.minor) return true; + if (newVersion.minor < currentVersion.minor) return false; + + return newVersion.patch > currentVersion.patch; +} + +// Function to perform the OTA update +bool performUpdate(const String& url) { + WiFiClientSecure *client = new WiFiClientSecure; + if(client) { + client->setCACert(rootCACertificate); + { + // Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is + HTTPClient https; + + Serial.print("[HTTPS] begin...\n"); + if (https.begin(*client, url)) { + Serial.print("[HTTPS] GET...\n"); + // start connection and send HTTP header + int httpCode = https.GET(); + + // httpCode will be negative on error + if (httpCode > 0) { + // HTTP header has been send and Server response header has been handled + Serial.printf("[HTTPS] GET... code: %d\n", httpCode); + + // file found at server + if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { + // get length of document (is -1 when Server sends no Content-Length header) + int contentLength = https.getSize(); + Serial.printf("Content-Length: %d\n", contentLength); + + if (contentLength > 0) { + bool canBegin = Update.begin(contentLength); + if (canBegin) { + WiFiClient * stream = https.getStreamPtr(); + size_t written = Update.writeStream(*stream); + + if (written == contentLength) { + Serial.println("Written : " + String(written) + " successfully"); + } else { + Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); + } + + if (Update.end()) { + Serial.println("OTA done!"); + if (Update.isFinished()) { + Serial.println("Update successfully completed. Rebooting."); + return true; + } else { + Serial.println("Update not finished? Something went wrong!"); + } + } else { + Serial.println("Error Occurred. Error #: " + String(Update.getError())); + } + } else { + Serial.println("Not enough space to begin OTA"); + } + } else { + Serial.println("There was no content length in the response"); + } + } + } else { + Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str()); + } + + https.end(); + } else { + Serial.printf("[HTTPS] Unable to connect\n"); + } + } + delete client; + } else { + Serial.println("Unable to create client"); + } + + return false; +} + +// Task function for OTA update +void updateTask(void * parameter) { + String url = *((String*)parameter); + delete (String*)parameter; + + Serial.println("Starting OTA update"); + bool updateSuccess = performUpdate(url); + + if (updateSuccess) { + Serial.println("Update successful. Rebooting..."); + delay(1000); // Give some time for the message to be sent + ESP.restart(); + } else { + Serial.println("Update failed."); + } + + // Task is done, delete itself + vTaskDelete(NULL); +} + +void startOTAUpdate(const String& url) { + String* urlCopy = new String(url); + + // Create the task, passing the URL as a parameter + xTaskCreate( + updateTask, // Function to implement the task + "otaUpdateTask", // Name of the task + 10000, // Stack size in words + (void*)urlCopy, // Task input parameter + 1, // Priority of the task + &updateTaskHandle // Task handle + ); +} + diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino new file mode 100644 index 00000000..a6323b49 --- /dev/null +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -0,0 +1,109 @@ +/* + * Example for how to use SinricPro OTA Service: + * + * If you encounter any issues: + * - check the readme.md at https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md + * - ensure all dependent libraries are installed + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#arduinoide + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#dependencies + * - open serial monitor and check whats happening + * - check full user documentation at https://sinricpro.github.io/esp8266-esp32-sdk + * - visit https://github.com/sinricpro/esp8266-esp32-sdk/issues and check for existing issues or open a new one + */ + +// Uncomment the following line to enable serial debug output +#define ENABLE_DEBUG + +#define FIRMWARE_VERSION "1.1.1" // Your firmware version. Must be above SinricPro.h. Do not rename FIRMWARE_VERSION. + +#ifdef ENABLE_DEBUG +#define DEBUG_ESP_PORT Serial +#define NODEBUG_WEBSOCKETS +#define NDEBUG +#endif + +#include + +#if defined(ESP32) +#include +#else +#error "ESP8266 is not supported due to memory limitations" +#endif + +#include "SinricPro.h" +#include "OTAHelper.h" + +#define WIFI_SSID "YOUR-WIFI-SSID" +#define WIFI_PASS "YOUR-WIFI-PASSWORD" +#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" +#define BAUD_RATE 115200 // Change baudrate to your need + +bool handleOTAUpdate(const String& url, int major, int minor, int patch) { + Version currentVersion = parseVersion(FIRMWARE_VERSION); + Version newVersion = parseVersion(String(major) + "." + String(minor) + "." + String(patch)); + + bool updateAvailable = isNewerVersion(currentVersion, newVersion); + Serial.print("URL: "); + Serial.println(url.c_str()); + Serial.print("Current version: "); + Serial.println(currentVersion.toString()); + Serial.print("New version: "); + Serial.println(newVersion.toString()); + + if (updateAvailable) { + Serial.println("Update available!"); + startOTAUpdate(url); + } else { + Serial.println("Current version is up to date."); + } + + return true; // OTA Update started successfully +} + +// setup function for WiFi connection +void setupWiFi() { + Serial.printf("\r\n[Wifi]: Connecting"); + +#if defined(ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); + WiFi.setAutoReconnect(true); +#elif defined(ESP32) + WiFi.setSleep(false); + WiFi.setAutoReconnect(true); +#endif + + WiFi.begin(WIFI_SSID, WIFI_PASS); + + while (WiFi.status() != WL_CONNECTED) { + Serial.printf("."); + delay(250); + } + Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); +} + +// setup function for SinricPro +void setupSinricPro() { + // setup SinricPro + SinricPro.onConnected([]() { + Serial.printf("Connected to SinricPro\r\n"); + }); + SinricPro.onDisconnected([]() { + Serial.printf("Disconnected from SinricPro\r\n"); + }); + SinricPro.onOTAUpdate(handleOTAUpdate); + SinricPro.begin(APP_KEY, APP_SECRET); +} + +// main setup function +void setup() { + Serial.begin(BAUD_RATE); + Serial.printf("\r\n\r\n"); + setupWiFi(); + setupSinricPro(); +} + +void loop() { + SinricPro.handle(); +} diff --git a/examples/Settings/Settings.ino b/examples/Settings/Settings.ino new file mode 100644 index 00000000..75525bde --- /dev/null +++ b/examples/Settings/Settings.ino @@ -0,0 +1,153 @@ +/* + * Example for how to use SinricPro Settings: + * + * If you encounter any issues: + * - check the readme.md at https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md + * - ensure all dependent libraries are installed + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#arduinoide + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#dependencies + * - open serial monitor and check whats happening + * - check full user documentation at https://sinricpro.github.io/esp8266-esp32-sdk + * - visit https://github.com/sinricpro/esp8266-esp32-sdk/issues and check for existing issues or open a new one + */ + +// Uncomment the following line to enable serial debug output +#define ENABLE_DEBUG + +#ifdef ENABLE_DEBUG +#define DEBUG_ESP_PORT Serial +#define NODEBUG_WEBSOCKETS +#define NDEBUG +#endif + +#include +#if defined(ESP8266) +#include +#elif defined(ESP32) || defined(ARDUINO_ARCH_RP2040) +#include +#endif + +#include "SinricPro.h" +#include "ArduinoJson.h" + +#define WIFI_SSID "YOUR-WIFI-SSID" +#define WIFI_PASS "YOUR-WIFI-PASSWORD" +#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" +#define BAUD_RATE 115200 // Change baudrate to your need + +#define SET_WIFI "pro.sinric::set.wifi" +#define GET_CONNECTED_WIFI_INFO "pro.sinric::get.connected.wifi.info" + +struct WiFiInfo { + int32_t channel; + long rssi; + String encryptionType; +}; + +bool handleSetSetting(const String& id, const String& value) { + if (id == SET_WIFI) { + // Connect to another WiFi + // JsonDocument doc; + // deserializeJson(doc, value); + // const char* ssid = doc["ssid"]; + // const char* password = doc["password"]; + } + + return true; +} + +WiFiInfo getWiFiInfo(const char* ssid) { + struct WiFiInfo info; + + if (int32_t n = WiFi.scanNetworks()) { + for (uint8_t i = 0; i < n; i++) { + if (!strcmp(ssid, WiFi.SSID(i).c_str())) { + info.channel = WiFi.channel(i); + info.rssi = WiFi.RSSI(i); + + switch (WiFi.encryptionType(i)) { + case WIFI_AUTH_OPEN: info.encryptionType = "open"; break; + case WIFI_AUTH_WEP: info.encryptionType = "WEP"; break; + case WIFI_AUTH_WPA_PSK: info.encryptionType = "WPA"; break; + case WIFI_AUTH_WPA2_PSK: info.encryptionType = "WPA2"; break; + case WIFI_AUTH_WPA_WPA2_PSK: info.encryptionType = "WPA+WPA2"; break; + case WIFI_AUTH_WPA2_ENTERPRISE: info.encryptionType = "WPA2-EAP"; break; + case WIFI_AUTH_WPA3_PSK: info.encryptionType = "WPA3"; break; + case WIFI_AUTH_WPA2_WPA3_PSK: info.encryptionType = "WPA2+WPA3"; break; + case WIFI_AUTH_WAPI_PSK: info.encryptionType = "WAPI"; break; + default: info.encryptionType = "unknown"; break; + } + } + } + } + + // Delete the scan result to free memory + WiFi.scanDelete(); + + return info; +} + +bool handleGetSetting(const String& id, String& value) { + if (id == GET_CONNECTED_WIFI_INFO) { + WiFiInfo info = getWiFiInfo(WIFI_SSID); + + JsonDocument doc; + doc["rssi"] = info.rssi; + doc["channel"] = info.channel; + doc["ssid"] = info.encryptionType; + + serializeJson(doc, value); + } + + return true; +} + +// setup function for WiFi connection +void setupWiFi() { + Serial.printf("\r\n[Wifi]: Connecting"); + +#if defined(ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); + WiFi.setAutoReconnect(true); +#elif defined(ESP32) + WiFi.setSleep(false); + WiFi.setAutoReconnect(true); +#endif + + WiFi.begin(WIFI_SSID, WIFI_PASS); + + while (WiFi.status() != WL_CONNECTED) { + Serial.printf("."); + delay(250); + } + Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); +} + +// setup function for SinricPro +void setupSinricPro() { + + // setup SinricPro + SinricPro.onConnected([]() { + Serial.printf("Connected to SinricPro\r\n"); + }); + SinricPro.onDisconnected([]() { + Serial.printf("Disconnected from SinricPro\r\n"); + }); + SinricPro.onSetSetting(handleSetSetting); + SinricPro.onGetSetting(handleGetSetting); + SinricPro.begin(APP_KEY, APP_SECRET); +} + +// main setup function +void setup() { + Serial.begin(BAUD_RATE); + Serial.printf("\r\n\r\n"); + setupWiFi(); + setupSinricPro(); +} + +void loop() { + SinricPro.handle(); +} diff --git a/library.json b/library.json index b3e0fd93..e74dff18 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "maintainer": true } ], - "version": "3.1.0", + "version": "4.0.0", "frameworks": "arduino", "platforms": [ "espressif8266", diff --git a/library.properties b/library.properties index 361b3d86..8e003f79 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SinricPro -version=3.1.0 +version=4.0.0 author=Boris Jaeger maintainer=Boris Jaeger sentence=Library for https://sinric.pro - simple way to connect your device to alexa diff --git a/src/SinricPro.h b/src/SinricPro.h index c5c18931..d53817c9 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -17,6 +17,7 @@ #include "SinricProUDP.h" #include "SinricProWebsocket.h" #include "Timestamp.h" +#include "SinricProModuleCommandHandler.h" namespace SINRICPRO_NAMESPACE { /** @@ -37,6 +38,43 @@ using ConnectedCallbackHandler = std::function; */ using DisconnectedCallbackHandler = std::function; +/** + * @brief Function signature for OTA update callback. + * + * This typedef defines a function pointer type for OTA (Over-The-Air) update callbacks. + * The callback function should accept a URL string and a Version struct as parameters. + * + * @param url The URL from which the OTA update can be downloaded. + * @param major The major version number + * @param minor The minor version number + * @param patch The patch version number + * @return bool True if the update process started successful, false otherwise. + */ +using OTAUpdateCallbackHandler = std::function; + +/** + * @brief Function signature for setting a module setting. + * + * This callback is used to set a value for a specific setting identified by its ID. + * + * @param id The unique identifier of the setting to be set. + * @param value The new value to be assigned to the setting. + * @return bool Returns true if the setting was successfully updated, false otherwise. + */ +using SetSettingCallbackHandler = std::function; + +/** + * @brief Function signature for retrieving a module setting. + * + * This callback is used to get the current value of a specific setting identified by its ID. + * The value is passed by reference, allowing the callback to modify it directly. + * + * @param id The unique identifier of the setting to be retrieved. + * @param value A reference to a String that will be updated with the current value of the setting. + * @return bool Returns true if the setting was successfully retrieved, false otherwise. + */ +using GetSettingCallbackHandler = std::function; + using PongCallback = std::function; /** @@ -62,6 +100,9 @@ class SinricProClass : public SinricProInterface { void setResponseMessage(String&& message); unsigned long getTimestamp() override; Proxy operator[](const String deviceId); + void onOTAUpdate(OTAUpdateCallbackHandler cb); + void onSetSetting(SetSettingCallbackHandler cb); + void onGetSetting(GetSettingCallbackHandler cb); protected: template @@ -78,7 +119,8 @@ class SinricProClass : public SinricProInterface { void handleReceiveQueue(); void handleSendQueue(); - void handleRequest(JsonDocument& requestMessage, interface_t Interface); + void handleDeviceRequest(JsonDocument& requestMessage, interface_t Interface); + void handleModuleRequest(JsonDocument& requestMessage, interface_t Interface); void handleResponse(JsonDocument& responseMessage); JsonDocument prepareRequest(String deviceId, const char* action); @@ -112,6 +154,8 @@ class SinricProClass : public SinricProInterface { bool _begin = false; String responseMessageStr = ""; + + SinricProModuleCommandHandler _moduleCommandHandler; }; class SinricProClass::Proxy { @@ -277,8 +321,31 @@ void SinricProClass::handleResponse(JsonDocument& responseMessage) { #endif } -void SinricProClass::handleRequest(JsonDocument& requestMessage, interface_t Interface) { - DEBUG_SINRIC("[SinricPro.handleRequest()]: handling request\r\n"); +void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface_t Interface) { + DEBUG_SINRIC("[SinricPro.handleModuleScopeRequest()]: handling device sope request\r\n"); +#ifndef NODEBUG_SINRIC + serializeJsonPretty(requestMessage, DEBUG_ESP_PORT); +#endif + + JsonDocument responseMessage = prepareResponse(requestMessage); + responseMessage.remove(FSTR_SINRICPRO_deviceId); + + String action = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] | ""; + JsonObject request_value = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + JsonObject response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; + SinricProRequest request{ action, "", request_value, response_value}; + + bool success = _moduleCommandHandler.handleRequest(request); + + responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success; + + String responseString; + serializeJson(responseMessage, responseString); + sendQueue.push(new SinricProMessage(Interface, responseString.c_str())); +} + +void SinricProClass::handleDeviceRequest(JsonDocument& requestMessage, interface_t Interface) { + DEBUG_SINRIC("[SinricPro.handleDeviceRequest()]: handling device sope request\r\n"); #ifndef NODEBUG_SINRIC serializeJsonPretty(requestMessage, DEBUG_ESP_PORT); #endif @@ -345,9 +412,17 @@ void SinricProClass::handleReceiveQueue() { DEBUG_SINRIC("[SinricPro.handleReceiveQueue()]: Signature is valid. Processing message...\r\n"); extractTimestamp(jsonMessage); if (messageType == FSTR_SINRICPRO_response) handleResponse(jsonMessage); - if (messageType == FSTR_SINRICPRO_request) handleRequest(jsonMessage, rawMessage->getInterface()); + if (messageType == FSTR_SINRICPRO_request) { + String scope = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope] | FSTR_SINRICPRO_device; + if (FSTR_SINRICPRO_module == scope) { + handleModuleRequest(jsonMessage, rawMessage->getInterface()); + } else { + handleDeviceRequest(jsonMessage, rawMessage->getInterface()); + } + }; } else { - DEBUG_SINRIC("[SinricPro.handleReceiveQueue()]: Signature is invalid! Sending messsage to [dev/null] ;)\r\n"); + DEBUG_SINRIC("[SinricPro.handleReceiveQueue()]: Signature is invalid! \r\n"); + if (messageType == FSTR_SINRICPRO_request) handleDeviceRequest(jsonMessage, rawMessage->getInterface()); } delete rawMessage; } @@ -416,6 +491,48 @@ bool SinricProClass::isConnected() { return _websocketListener.isConnected(); }; +/** + * @brief Set callback function for OTA (Over-The-Air) updates. + * + * This method registers a callback function that will be called when an OTA update is available. + * The callback should handle the process of downloading and applying the update. + * + * @param cb A function pointer or lambda of type OTAUpdateCallbackHandler. + * The callback should return a boolean indicating whether the update was successful. + * + */ +void SinricProClass::onOTAUpdate(OTAUpdateCallbackHandler cb) { + _moduleCommandHandler.onOTAUpdate(cb); +} + +/** + * @brief Set callback function for setting a module setting. + * + * This method registers a callback function that will be called when a request to change + * a module setting is received. + * + * @param cb A function pointer or lambda of type SetSettingCallbackHandler. + * The callback should return a boolean indicating whether the setting was successfully updated. + * + */ +void SinricProClass::onSetSetting(SetSettingCallbackHandler cb) { + _moduleCommandHandler.onSetSetting(cb); +} + +/** + * @brief Set callback function for retrieving a module setting. + * + * This method registers a callback function that will be called when a request to get + * the current value of a module setting is received. + * + * @param cb A function pointer or lambda of type GetSettingCallbackHandler. + * The callback should return a boolean indicating whether the setting was successfully retrieved. + * + */ +void SinricProClass::onGetSetting(GetSettingCallbackHandler cb) { + _moduleCommandHandler.onGetSetting(cb); +} + /** * @brief Set callback function for websocket connected event * @@ -546,6 +663,7 @@ JsonDocument SinricProClass::prepareResponse(JsonDocument& requestMessage) { JsonObject payload = responseMessage[FSTR_SINRICPRO_payload].to(); payload[FSTR_SINRICPRO_action] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action]; payload[FSTR_SINRICPRO_clientId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_clientId]; + payload[FSTR_SINRICPRO_scope] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope]; payload[FSTR_SINRICPRO_createdAt] = 0; payload[FSTR_SINRICPRO_deviceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_deviceId]; if (requestMessage[FSTR_SINRICPRO_payload].containsKey(FSTR_SINRICPRO_instanceId)) payload[FSTR_SINRICPRO_instanceId] = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_instanceId]; diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h new file mode 100644 index 00000000..1da5d6ed --- /dev/null +++ b/src/SinricProModuleCommandHandler.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Sinric. All rights reserved. + * Licensed under Creative Commons Attribution-Share Alike (CC BY-SA) + * + * This file is part of the Sinric Pro (https://github.com/sinricpro/) + */ + +#pragma once + +#include "SinricProRequest.h" + +#include "SinricProNamespace.h" +namespace SINRICPRO_NAMESPACE { + +FSTR(OTA, otaUpdateAvailable); // "otaUpdateAvailable" +FSTR(OTA, url); // "url" +FSTR(OTA, version); // "version" +FSTR(OTA, major); // "major" +FSTR(OTA, minor); // "minor" +FSTR(OTA, patch); // "patch" +FSTR(SETTINGS, setSetting); // "setSetting" +FSTR(SETTINGS, getSetting); // "getSetting" +FSTR(SETTINGS, id); // "id" +FSTR(SETTINGS, value); // "value" + +using OTAUpdateCallbackHandler = std::function; +using SetSettingCallbackHandler = std::function; +using GetSettingCallbackHandler = std::function; + +class SinricProModuleCommandHandler { + public: + SinricProModuleCommandHandler(); + ~SinricProModuleCommandHandler(); + + bool handleRequest(SinricProRequest &request); + void onOTAUpdate(OTAUpdateCallbackHandler callback); + void onSetSetting(SetSettingCallbackHandler callback); + void onGetSetting(GetSettingCallbackHandler callback); + + private: + OTAUpdateCallbackHandler _otaUpdateCallbackHandler; + SetSettingCallbackHandler _setSettingCallbackHandler; + GetSettingCallbackHandler _getSettingCallbackHandler; +}; + +SinricProModuleCommandHandler::SinricProModuleCommandHandler() + : _otaUpdateCallbackHandler(nullptr), + _setSettingCallbackHandler(nullptr), + _getSettingCallbackHandler(nullptr) {} + +SinricProModuleCommandHandler::~SinricProModuleCommandHandler() {} + +void SinricProModuleCommandHandler::onOTAUpdate(OTAUpdateCallbackHandler callback) { + _otaUpdateCallbackHandler = callback; +} + +void SinricProModuleCommandHandler::onSetSetting(SetSettingCallbackHandler callback) { + _setSettingCallbackHandler = callback; +} + +void SinricProModuleCommandHandler::onGetSetting(GetSettingCallbackHandler callback) { + _getSettingCallbackHandler = callback; +} + +bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { + if (FSTR_OTA_otaUpdateAvailable == request.action && _otaUpdateCallbackHandler) { + String url = request.request_value[FSTR_OTA_url]; + int major = request.request_value[FSTR_OTA_version][FSTR_OTA_major]; + int minor = request.request_value[FSTR_OTA_version][FSTR_OTA_minor]; + int patch = request.request_value[FSTR_OTA_version][FSTR_OTA_patch]; + return _otaUpdateCallbackHandler(url, major, minor, patch); + } + else if (FSTR_SETTINGS_setSetting == request.action && _setSettingCallbackHandler) { + String id = request.request_value[FSTR_SETTINGS_id]; + String value = request.request_value[FSTR_SETTINGS_value]; + return _setSettingCallbackHandler(id, value); + } + else if (FSTR_SETTINGS_setSetting == request.action && _setSettingCallbackHandler) { + String id = request.request_value[FSTR_SETTINGS_id]; + String value = request.request_value[FSTR_SETTINGS_value]; + return _getSettingCallbackHandler(id, value); + } + return false; +} + +} // SINRICPRO_NAMESPACE diff --git a/src/SinricProStrings.h b/src/SinricProStrings.h index 61836df1..2f71fa68 100644 --- a/src/SinricProStrings.h +++ b/src/SinricProStrings.h @@ -38,5 +38,8 @@ FSTR(SINRICPRO, PERIODIC_POLL); // "PERIODIC_POLL" FSTR(SINRICPRO, PHYSICAL_INTERACTION); // "PHYSICAL_INTERACTION" FSTR(SINRICPRO, ALERT); // "ALERT" FSTR(SINRICPRO, OK); // "OK" +FSTR(SINRICPRO, scope); // "scope" +FSTR(SINRICPRO, module); // "module" +FSTR(SINRICPRO, device); // "device" } // SINRICPRO_NAMESPACE \ No newline at end of file diff --git a/src/SinricProVersion.h b/src/SinricProVersion.h index d3925093..3685dd8b 100644 --- a/src/SinricProVersion.h +++ b/src/SinricProVersion.h @@ -4,8 +4,8 @@ #define STR(x) STR_HELPER(x) // Version Configuration -#define SINRICPRO_VERSION_MAJOR 3 -#define SINRICPRO_VERSION_MINOR 1 +#define SINRICPRO_VERSION_MAJOR 4 +#define SINRICPRO_VERSION_MINOR 0 #define SINRICPRO_VERSION_REVISION 0 #define SINRICPRO_VERSION STR(SINRICPRO_VERSION_MAJOR) "." STR(SINRICPRO_VERSION_MINOR) "." STR(SINRICPRO_VERSION_REVISION) #define SINRICPRO_VERSION_STR "SinricPro (v" SINRICPRO_VERSION ")" From dd9b676143c26efbbda946e63f084d4dba9eaae0 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sat, 6 Jul 2024 13:04:48 +0700 Subject: [PATCH 02/36] feat: OTA and workflows --- .github/workflows/build-esp8266-esp32.yml | 2 +- .../{OTAHelper.h => ESP32OTAHelper.h} | 117 ++++++++---------- examples/OTAUpdate/ESP8266OTAHelper.h | 47 +++++++ examples/OTAUpdate/OTAUpdate.ino | 27 ++-- examples/OTAUpdate/SemVer.h | 31 +++++ examples/Settings/Settings.ino | 2 + src/SinricPro.h | 6 +- src/SinricProModuleCommandHandler.h | 6 +- 8 files changed, 155 insertions(+), 83 deletions(-) rename examples/OTAUpdate/{OTAHelper.h => ESP32OTAHelper.h} (55%) create mode 100644 examples/OTAUpdate/ESP8266OTAHelper.h create mode 100644 examples/OTAUpdate/SemVer.h diff --git a/.github/workflows/build-esp8266-esp32.yml b/.github/workflows/build-esp8266-esp32.yml index b92a9d9b..b4c71517 100644 --- a/.github/workflows/build-esp8266-esp32.yml +++ b/.github/workflows/build-esp8266-esp32.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate, examples/Settings] steps: diff --git a/examples/OTAUpdate/OTAHelper.h b/examples/OTAUpdate/ESP32OTAHelper.h similarity index 55% rename from examples/OTAUpdate/OTAHelper.h rename to examples/OTAUpdate/ESP32OTAHelper.h index 82888014..32ac0153 100644 --- a/examples/OTAUpdate/OTAHelper.h +++ b/examples/OTAUpdate/ESP32OTAHelper.h @@ -1,71 +1,54 @@ +#if defined(ESP32) + #include #include #include #include -// Task handle for the update task -TaskHandle_t updateTaskHandle = NULL; +#define FOR_i(from, to) for(int i = (from); i < (to); i++) + +// Task handle for the ota update task +TaskHandle_t otaUpdateTaskHandle = NULL; + +// Ref: https://projects.petrucci.ch/esp32/?page=ssl.php&url=otaupdates.sinric.pro -// Cloudflare Root CA Certificate (Baltimore CyberTrust Root) const char rootCACertificate[] PROGMEM = R"EOF( -----BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw +WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK +a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO +VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw +gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD +ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw +i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB +AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g +BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu +Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C +2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+ +bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG +6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV +XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO +koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq +cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI +E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e +K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX +GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL +sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd +VQD9F6Na/+zmXCc= -----END CERTIFICATE----- )EOF"; -struct Version { - int major; - int minor; - int patch; - - String toString() const { - return String(major) + "." + String(minor) + "." + String(patch); - } -}; - -Version parseVersion(const String& versionStr) { - Version v; - int firstDot = versionStr.indexOf('.'); - int secondDot = versionStr.lastIndexOf('.'); - v.major = versionStr.substring(0, firstDot).toInt(); - v.minor = versionStr.substring(firstDot + 1, secondDot).toInt(); - v.patch = versionStr.substring(secondDot + 1).toInt(); - return v; -} - -bool isNewerVersion(const Version& currentVersion, const Version& newVersion) { - if (newVersion.major > currentVersion.major) return true; - if (newVersion.major < currentVersion.major) return false; - - if (newVersion.minor > currentVersion.minor) return true; - if (newVersion.minor < currentVersion.minor) return false; - - return newVersion.patch > currentVersion.patch; -} // Function to perform the OTA update bool performUpdate(const String& url) { WiFiClientSecure *client = new WiFiClientSecure; + if(client) { client->setCACert(rootCACertificate); + //client->setInsecure(); { // Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is HTTPClient https; @@ -135,36 +118,42 @@ bool performUpdate(const String& url) { } // Task function for OTA update -void updateTask(void * parameter) { +void otaUpdateTask(void * parameter) { String url = *((String*)parameter); delete (String*)parameter; Serial.println("Starting OTA update"); - bool updateSuccess = performUpdate(url); + + FOR_i(1, 5) { + bool updateSuccess = performUpdate(url); - if (updateSuccess) { - Serial.println("Update successful. Rebooting..."); - delay(1000); // Give some time for the message to be sent - ESP.restart(); - } else { - Serial.println("Update failed."); - } + if (updateSuccess) { + Serial.println("Update successful. Rebooting..."); + ESP.restart(); + } else { + Serial.println("Update failed. Retrying.."); // To fix random DNS lookup errors + delay(1000); + } + } // Task is done, delete itself vTaskDelete(NULL); } -void startOTAUpdate(const String& url) { +bool startOTAUpdate(const String& url) { String* urlCopy = new String(url); // Create the task, passing the URL as a parameter xTaskCreate( - updateTask, // Function to implement the task - "otaUpdateTask", // Name of the task + otaUpdateTask, // Function to implement the task + "otaUpdateTask", // Name of the task 10000, // Stack size in words (void*)urlCopy, // Task input parameter 1, // Priority of the task - &updateTaskHandle // Task handle + &otaUpdateTaskHandle // Task handle ); + + return true; } +#endif \ No newline at end of file diff --git a/examples/OTAUpdate/ESP8266OTAHelper.h b/examples/OTAUpdate/ESP8266OTAHelper.h new file mode 100644 index 00000000..1cfe92c9 --- /dev/null +++ b/examples/OTAUpdate/ESP8266OTAHelper.h @@ -0,0 +1,47 @@ +#if defined(ESP8266) + +#include +#include +#include +#include "ESP8266httpUpdate.h" +#include + +// Function to perform the OTA update +bool startOTAUpdate(const String& url) { + Serial.println("Starting OTA update"); + Serial.printf("Available RAM: %d\n", ESP.getFreeHeap()); + + BearSSL::WiFiClientSecure client; + client.setInsecure(); + client.setBufferSizes(4096, 4096); + + // The line below is optional. It can be used to blink the LED on the board during flashing + // The LED will be on during download of one buffer of data from the network. The LED will + // be off during writing that buffer to flash + // On a good connection the LED should flash regularly. On a bad connection the LED will be + // on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second + // value is used to put the LED on. If the LED is on with HIGH, that value should be passed + //ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); + + auto http_ret = ESPhttpUpdate.update(client, url); + + //if success reboot + + switch (http_ret) { + case HTTP_UPDATE_OK: + Serial.printf("HTTP_UPDATE_OK\n"); + break; + + case HTTP_UPDATE_FAILED: + Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); + break; + + case HTTP_UPDATE_NO_UPDATES: + Serial.println("HTTP_UPDATE_NO_UPDATES"); + break; + } + + return false; +} + +#endif \ No newline at end of file diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index a6323b49..1039c00a 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -12,37 +12,40 @@ */ // Uncomment the following line to enable serial debug output -#define ENABLE_DEBUG +//#define ENABLE_DEBUG -#define FIRMWARE_VERSION "1.1.1" // Your firmware version. Must be above SinricPro.h. Do not rename FIRMWARE_VERSION. +// Your firmware version. Must be above SinricPro.h. Do not rename this. +#define FIRMWARE_VERSION "1.1.1" #ifdef ENABLE_DEBUG -#define DEBUG_ESP_PORT Serial -#define NODEBUG_WEBSOCKETS -#define NDEBUG + #define DEBUG_ESP_PORT Serial + #define NODEBUG_WEBSOCKETS + #define NDEBUG #endif #include #if defined(ESP32) -#include + #include + #include "ESP32OTAHelper.h" #else -#error "ESP8266 is not supported due to memory limitations" + #include + #include "ESP8266OTAHelper.h" #endif +#include "SemVer.h" #include "SinricPro.h" -#include "OTAHelper.h" #define WIFI_SSID "YOUR-WIFI-SSID" #define WIFI_PASS "YOUR-WIFI-PASSWORD" #define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" #define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" -#define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" + #define BAUD_RATE 115200 // Change baudrate to your need bool handleOTAUpdate(const String& url, int major, int minor, int patch) { - Version currentVersion = parseVersion(FIRMWARE_VERSION); - Version newVersion = parseVersion(String(major) + "." + String(minor) + "." + String(patch)); + Version currentVersion = parseVersion(FIRMWARE_VERSION); + Version newVersion = parseVersion(String(major) + "." + String(minor) + "." + String(patch)); bool updateAvailable = isNewerVersion(currentVersion, newVersion); Serial.print("URL: "); @@ -54,7 +57,7 @@ bool handleOTAUpdate(const String& url, int major, int minor, int patch) { if (updateAvailable) { Serial.println("Update available!"); - startOTAUpdate(url); + return startOTAUpdate(url); } else { Serial.println("Current version is up to date."); } diff --git a/examples/OTAUpdate/SemVer.h b/examples/OTAUpdate/SemVer.h new file mode 100644 index 00000000..f936ff0e --- /dev/null +++ b/examples/OTAUpdate/SemVer.h @@ -0,0 +1,31 @@ +#include + +struct Version { + int major; + int minor; + int patch; + + String toString() const { + return String(major) + "." + String(minor) + "." + String(patch); + } +}; + +Version parseVersion(const String& versionStr) { + Version v; + int firstDot = versionStr.indexOf('.'); + int secondDot = versionStr.lastIndexOf('.'); + v.major = versionStr.substring(0, firstDot).toInt(); + v.minor = versionStr.substring(firstDot + 1, secondDot).toInt(); + v.patch = versionStr.substring(secondDot + 1).toInt(); + return v; +} + +bool isNewerVersion(const Version& currentVersion, const Version& newVersion) { + if (newVersion.major > currentVersion.major) return true; + if (newVersion.major < currentVersion.major) return false; + + if (newVersion.minor > currentVersion.minor) return true; + if (newVersion.minor < currentVersion.minor) return false; + + return newVersion.patch > currentVersion.patch; +} \ No newline at end of file diff --git a/examples/Settings/Settings.ino b/examples/Settings/Settings.ino index 75525bde..f49aa4a3 100644 --- a/examples/Settings/Settings.ino +++ b/examples/Settings/Settings.ino @@ -79,6 +79,8 @@ WiFiInfo getWiFiInfo(const char* ssid) { case WIFI_AUTH_WAPI_PSK: info.encryptionType = "WAPI"; break; default: info.encryptionType = "unknown"; break; } + + break; } } } diff --git a/src/SinricPro.h b/src/SinricPro.h index d53817c9..46c7fa3c 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -328,8 +328,7 @@ void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface #endif JsonDocument responseMessage = prepareResponse(requestMessage); - responseMessage.remove(FSTR_SINRICPRO_deviceId); - + String action = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_action] | ""; JsonObject request_value = requestMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; JsonObject response_value = responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_value]; @@ -338,6 +337,7 @@ void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface bool success = _moduleCommandHandler.handleRequest(request); responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success; + responseMessage.remove(FSTR_SINRICPRO_deviceId); String responseString; serializeJson(responseMessage, responseString); @@ -414,7 +414,7 @@ void SinricProClass::handleReceiveQueue() { if (messageType == FSTR_SINRICPRO_response) handleResponse(jsonMessage); if (messageType == FSTR_SINRICPRO_request) { String scope = jsonMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_scope] | FSTR_SINRICPRO_device; - if (FSTR_SINRICPRO_module == scope) { + if (strcmp(FSTR_SINRICPRO_module, scope.c_str()) == 0) { handleModuleRequest(jsonMessage, rawMessage->getInterface()); } else { handleDeviceRequest(jsonMessage, rawMessage->getInterface()); diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index 1da5d6ed..517da139 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -63,19 +63,19 @@ void SinricProModuleCommandHandler::onGetSetting(GetSettingCallbackHandler callb } bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { - if (FSTR_OTA_otaUpdateAvailable == request.action && _otaUpdateCallbackHandler) { + if (strcmp(FSTR_OTA_otaUpdateAvailable, request.action.c_str()) == 0 && _otaUpdateCallbackHandler) { String url = request.request_value[FSTR_OTA_url]; int major = request.request_value[FSTR_OTA_version][FSTR_OTA_major]; int minor = request.request_value[FSTR_OTA_version][FSTR_OTA_minor]; int patch = request.request_value[FSTR_OTA_version][FSTR_OTA_patch]; return _otaUpdateCallbackHandler(url, major, minor, patch); } - else if (FSTR_SETTINGS_setSetting == request.action && _setSettingCallbackHandler) { + else if (strcmp(FSTR_SETTINGS_setSetting, request.action.c_str()) == 0 && _setSettingCallbackHandler) { String id = request.request_value[FSTR_SETTINGS_id]; String value = request.request_value[FSTR_SETTINGS_value]; return _setSettingCallbackHandler(id, value); } - else if (FSTR_SETTINGS_setSetting == request.action && _setSettingCallbackHandler) { + else if (strcmp(FSTR_SETTINGS_getSetting, request.action.c_str()) == 0 && _getSettingCallbackHandler) { String id = request.request_value[FSTR_SETTINGS_id]; String value = request.request_value[FSTR_SETTINGS_value]; return _getSettingCallbackHandler(id, value); From 297cef304ea067cbae2d5945d74aef7645d47053 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sat, 6 Jul 2024 13:31:13 +0700 Subject: [PATCH 03/36] fix: OTA update for Picow --- examples/OTAUpdate/ESP8266OTAHelper.h | 34 ++++++++++++++++++++------- examples/OTAUpdate/OTAUpdate.ino | 9 ++++--- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/examples/OTAUpdate/ESP8266OTAHelper.h b/examples/OTAUpdate/ESP8266OTAHelper.h index 1cfe92c9..491e88b1 100644 --- a/examples/OTAUpdate/ESP8266OTAHelper.h +++ b/examples/OTAUpdate/ESP8266OTAHelper.h @@ -1,17 +1,33 @@ -#if defined(ESP8266) +#if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) #include #include -#include -#include "ESP8266httpUpdate.h" -#include + +#if defined(ARDUINO_ARCH_RP2040) + #include + #include + #include + + #define OTA_CLASS httpUpdate + +#elif defined(ESP8266) + #include + #include + #include "ESP8266httpUpdate.h" + + #define OTA_CLASS ESPhttpUpdate +#endif // Function to perform the OTA update bool startOTAUpdate(const String& url) { Serial.println("Starting OTA update"); - Serial.printf("Available RAM: %d\n", ESP.getFreeHeap()); - - BearSSL::WiFiClientSecure client; + + #if defined(ARDUINO_ARCH_RP2040) + WiFiClientSecure client; + #elif defined(ESP8266) + BearSSL::WiFiClientSecure client; + #endif + client.setInsecure(); client.setBufferSizes(4096, 4096); @@ -23,7 +39,7 @@ bool startOTAUpdate(const String& url) { // value is used to put the LED on. If the LED is on with HIGH, that value should be passed //ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); - auto http_ret = ESPhttpUpdate.update(client, url); + auto http_ret = OTA_CLASS.update(client, url); //if success reboot @@ -33,7 +49,7 @@ bool startOTAUpdate(const String& url) { break; case HTTP_UPDATE_FAILED: - Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); + Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", OTA_CLASS.getLastError(), OTA_CLASS.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index 1039c00a..b49554f9 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -25,11 +25,14 @@ #include -#if defined(ESP32) +#if defined(ESP8266) + #include + #include "ESP8266OTAHelper.h" +#elif defined(ESP32) #include #include "ESP32OTAHelper.h" -#else - #include +#elif defined(ARDUINO_ARCH_RP2040) + #include #include "ESP8266OTAHelper.h" #endif From f81ba1363301d8e4e3782a044feb5ef69997912b Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sat, 6 Jul 2024 13:31:29 +0700 Subject: [PATCH 04/36] chore: version update --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 5f6b3c43..1ed03859 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,9 @@ # Changelog +## Version 4.0.0 +### New + - Support OTA Updates + - Module level command support for Settings + ## Version 3.1.0 Upgrade: - Upgrade to ArduinoJson 7 From eb42b83c00da84482dbb4dd3285eb7eedc4b0651 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 13:22:09 +0700 Subject: [PATCH 05/36] chore: remove get settings --- src/SinricPro.h | 17 +---------------- src/SinricProModuleCommandHandler.h | 7 ------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/SinricPro.h b/src/SinricPro.h index 46c7fa3c..edccbbca 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -101,8 +101,7 @@ class SinricProClass : public SinricProInterface { unsigned long getTimestamp() override; Proxy operator[](const String deviceId); void onOTAUpdate(OTAUpdateCallbackHandler cb); - void onSetSetting(SetSettingCallbackHandler cb); - void onGetSetting(GetSettingCallbackHandler cb); + void onSetSetting(SetSettingCallbackHandler cb); protected: template @@ -519,20 +518,6 @@ void SinricProClass::onSetSetting(SetSettingCallbackHandler cb) { _moduleCommandHandler.onSetSetting(cb); } -/** - * @brief Set callback function for retrieving a module setting. - * - * This method registers a callback function that will be called when a request to get - * the current value of a module setting is received. - * - * @param cb A function pointer or lambda of type GetSettingCallbackHandler. - * The callback should return a boolean indicating whether the setting was successfully retrieved. - * - */ -void SinricProClass::onGetSetting(GetSettingCallbackHandler cb) { - _moduleCommandHandler.onGetSetting(cb); -} - /** * @brief Set callback function for websocket connected event * diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index 517da139..a98dff7e 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -25,7 +25,6 @@ FSTR(SETTINGS, value); // "value" using OTAUpdateCallbackHandler = std::function; using SetSettingCallbackHandler = std::function; -using GetSettingCallbackHandler = std::function; class SinricProModuleCommandHandler { public: @@ -35,12 +34,10 @@ class SinricProModuleCommandHandler { bool handleRequest(SinricProRequest &request); void onOTAUpdate(OTAUpdateCallbackHandler callback); void onSetSetting(SetSettingCallbackHandler callback); - void onGetSetting(GetSettingCallbackHandler callback); private: OTAUpdateCallbackHandler _otaUpdateCallbackHandler; SetSettingCallbackHandler _setSettingCallbackHandler; - GetSettingCallbackHandler _getSettingCallbackHandler; }; SinricProModuleCommandHandler::SinricProModuleCommandHandler() @@ -57,10 +54,6 @@ void SinricProModuleCommandHandler::onOTAUpdate(OTAUpdateCallbackHandler callbac void SinricProModuleCommandHandler::onSetSetting(SetSettingCallbackHandler callback) { _setSettingCallbackHandler = callback; } - -void SinricProModuleCommandHandler::onGetSetting(GetSettingCallbackHandler callback) { - _getSettingCallbackHandler = callback; -} bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { if (strcmp(FSTR_OTA_otaUpdateAvailable, request.action.c_str()) == 0 && _otaUpdateCallbackHandler) { From 020bb09508286757a277f89deb31cf85ad3d7b07 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 13:22:29 +0700 Subject: [PATCH 06/36] chore: refactor settings example --- examples/Settings/Settings.ino | 96 +++++++++------------------------- 1 file changed, 25 insertions(+), 71 deletions(-) diff --git a/examples/Settings/Settings.ino b/examples/Settings/Settings.ino index f49aa4a3..6d7c5dbb 100644 --- a/examples/Settings/Settings.ino +++ b/examples/Settings/Settings.ino @@ -12,22 +12,23 @@ */ // Uncomment the following line to enable serial debug output -#define ENABLE_DEBUG +//#define ENABLE_DEBUG #ifdef ENABLE_DEBUG -#define DEBUG_ESP_PORT Serial -#define NODEBUG_WEBSOCKETS -#define NDEBUG + #define DEBUG_ESP_PORT Serial + #define NODEBUG_WEBSOCKETS + #define NDEBUG #endif #include #if defined(ESP8266) -#include + #include #elif defined(ESP32) || defined(ARDUINO_ARCH_RP2040) -#include + #include #endif #include "SinricPro.h" +#include "SinricProSwitch.h" #include "ArduinoJson.h" #define WIFI_SSID "YOUR-WIFI-SSID" @@ -37,75 +38,26 @@ #define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" #define BAUD_RATE 115200 // Change baudrate to your need -#define SET_WIFI "pro.sinric::set.wifi" -#define GET_CONNECTED_WIFI_INFO "pro.sinric::get.connected.wifi.info" +#define SET_WIFI_PRIMARY "pro.sinric::set.wifi.primary" +#define SET_WIFI_SECONDARY "pro.sinric::set.wifi.secondary" -struct WiFiInfo { - int32_t channel; - long rssi; - String encryptionType; -}; - -bool handleSetSetting(const String& id, const String& value) { - if (id == SET_WIFI) { - // Connect to another WiFi - // JsonDocument doc; - // deserializeJson(doc, value); - // const char* ssid = doc["ssid"]; - // const char* password = doc["password"]; - } - - return true; -} - -WiFiInfo getWiFiInfo(const char* ssid) { - struct WiFiInfo info; - - if (int32_t n = WiFi.scanNetworks()) { - for (uint8_t i = 0; i < n; i++) { - if (!strcmp(ssid, WiFi.SSID(i).c_str())) { - info.channel = WiFi.channel(i); - info.rssi = WiFi.RSSI(i); - - switch (WiFi.encryptionType(i)) { - case WIFI_AUTH_OPEN: info.encryptionType = "open"; break; - case WIFI_AUTH_WEP: info.encryptionType = "WEP"; break; - case WIFI_AUTH_WPA_PSK: info.encryptionType = "WPA"; break; - case WIFI_AUTH_WPA2_PSK: info.encryptionType = "WPA2"; break; - case WIFI_AUTH_WPA_WPA2_PSK: info.encryptionType = "WPA+WPA2"; break; - case WIFI_AUTH_WPA2_ENTERPRISE: info.encryptionType = "WPA2-EAP"; break; - case WIFI_AUTH_WPA3_PSK: info.encryptionType = "WPA3"; break; - case WIFI_AUTH_WPA2_WPA3_PSK: info.encryptionType = "WPA2+WPA3"; break; - case WIFI_AUTH_WAPI_PSK: info.encryptionType = "WAPI"; break; - default: info.encryptionType = "unknown"; break; - } - - break; - } - } - } - - // Delete the scan result to free memory - WiFi.scanDelete(); - - return info; +bool onSetDeviceSetting(const String& deviceId, const String& settingId, const String& settingValue) { + // Handle device settings. + return true; } -bool handleGetSetting(const String& id, String& value) { - if (id == GET_CONNECTED_WIFI_INFO) { - WiFiInfo info = getWiFiInfo(WIFI_SSID); +bool onSetModuleSetting(const String& id, const String& value) { + // Handle module settings. - JsonDocument doc; - doc["rssi"] = info.rssi; - doc["channel"] = info.channel; - doc["ssid"] = info.encryptionType; - - serializeJson(doc, value); + if (id == SET_WIFI_PRIMARY) { + // Set primary WiFi + } else if (id == SET_WIFI_SECONDARY) { + // Set secondary WiFi } - + return true; } - + // setup function for WiFi connection void setupWiFi() { Serial.printf("\r\n[Wifi]: Connecting"); @@ -129,7 +81,9 @@ void setupWiFi() { // setup function for SinricPro void setupSinricPro() { - + SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; + mySwitch1.onSetSetting(onSetDeviceSetting); + // setup SinricPro SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); @@ -137,8 +91,8 @@ void setupSinricPro() { SinricPro.onDisconnected([]() { Serial.printf("Disconnected from SinricPro\r\n"); }); - SinricPro.onSetSetting(handleSetSetting); - SinricPro.onGetSetting(handleGetSetting); + + SinricPro.onSetSetting(onSetModuleSetting); SinricPro.begin(APP_KEY, APP_SECRET); } From 143b1527efb2d6484544ff5c7ca568fcb250dfa8 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 13:31:32 +0700 Subject: [PATCH 07/36] chore: comments updated --- examples/OTAUpdate/ESP8266OTAHelper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/OTAUpdate/ESP8266OTAHelper.h b/examples/OTAUpdate/ESP8266OTAHelper.h index 491e88b1..25ad5543 100644 --- a/examples/OTAUpdate/ESP8266OTAHelper.h +++ b/examples/OTAUpdate/ESP8266OTAHelper.h @@ -29,7 +29,7 @@ bool startOTAUpdate(const String& url) { #endif client.setInsecure(); - client.setBufferSizes(4096, 4096); + client.setBufferSizes(4096, 4096); // For OTA to work on limited RAM // The line below is optional. It can be used to blink the LED on the board during flashing // The LED will be on during download of one buffer of data from the network. The LED will From 2a7e714a79c42fe2550d7e3bd680cce00fde0d4d Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 13:34:12 +0700 Subject: [PATCH 08/36] chore: code refactor --- src/SinricPro.h | 12 ------------ src/SinricProModuleCommandHandler.h | 8 +------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/SinricPro.h b/src/SinricPro.h index edccbbca..2ebaccd3 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -63,18 +63,6 @@ using OTAUpdateCallbackHandler = std::function; -/** - * @brief Function signature for retrieving a module setting. - * - * This callback is used to get the current value of a specific setting identified by its ID. - * The value is passed by reference, allowing the callback to modify it directly. - * - * @param id The unique identifier of the setting to be retrieved. - * @param value A reference to a String that will be updated with the current value of the setting. - * @return bool Returns true if the setting was successfully retrieved, false otherwise. - */ -using GetSettingCallbackHandler = std::function; - using PongCallback = std::function; /** diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index a98dff7e..d859334a 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -42,8 +42,7 @@ class SinricProModuleCommandHandler { SinricProModuleCommandHandler::SinricProModuleCommandHandler() : _otaUpdateCallbackHandler(nullptr), - _setSettingCallbackHandler(nullptr), - _getSettingCallbackHandler(nullptr) {} + _setSettingCallbackHandler(nullptr) {} SinricProModuleCommandHandler::~SinricProModuleCommandHandler() {} @@ -68,11 +67,6 @@ bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { String value = request.request_value[FSTR_SETTINGS_value]; return _setSettingCallbackHandler(id, value); } - else if (strcmp(FSTR_SETTINGS_getSetting, request.action.c_str()) == 0 && _getSettingCallbackHandler) { - String id = request.request_value[FSTR_SETTINGS_id]; - String value = request.request_value[FSTR_SETTINGS_value]; - return _getSettingCallbackHandler(id, value); - } return false; } From a168bac3b17e304c43357fc6106056c5d1a9f65e Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 14:54:33 +0700 Subject: [PATCH 09/36] fix: version --- changelog.md | 2 +- library.json | 2 +- library.properties | 2 +- src/SinricProModuleCommandHandler.h | 1 - src/SinricProVersion.h | 4 ++-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 1ed03859..0ddc8eb3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,5 @@ # Changelog -## Version 4.0.0 +## Version 3.2.1 ### New - Support OTA Updates - Module level command support for Settings diff --git a/library.json b/library.json index e74dff18..d6440f57 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "maintainer": true } ], - "version": "4.0.0", + "version": "3.2.1", "frameworks": "arduino", "platforms": [ "espressif8266", diff --git a/library.properties b/library.properties index 8e003f79..fa0787c3 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SinricPro -version=4.0.0 +version=3.2.1 author=Boris Jaeger maintainer=Boris Jaeger sentence=Library for https://sinric.pro - simple way to connect your device to alexa diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index d859334a..02ea8e56 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -19,7 +19,6 @@ FSTR(OTA, major); // "major" FSTR(OTA, minor); // "minor" FSTR(OTA, patch); // "patch" FSTR(SETTINGS, setSetting); // "setSetting" -FSTR(SETTINGS, getSetting); // "getSetting" FSTR(SETTINGS, id); // "id" FSTR(SETTINGS, value); // "value" diff --git a/src/SinricProVersion.h b/src/SinricProVersion.h index 3685dd8b..875d214f 100644 --- a/src/SinricProVersion.h +++ b/src/SinricProVersion.h @@ -4,8 +4,8 @@ #define STR(x) STR_HELPER(x) // Version Configuration -#define SINRICPRO_VERSION_MAJOR 4 -#define SINRICPRO_VERSION_MINOR 0 +#define SINRICPRO_VERSION_MAJOR 3 +#define SINRICPRO_VERSION_MINOR 2 #define SINRICPRO_VERSION_REVISION 0 #define SINRICPRO_VERSION STR(SINRICPRO_VERSION_MAJOR) "." STR(SINRICPRO_VERSION_MINOR) "." STR(SINRICPRO_VERSION_REVISION) #define SINRICPRO_VERSION_STR "SinricPro (v" SINRICPRO_VERSION ")" From 4f08ab4bc909838e91d0c34d7a3a7611c78b76a5 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 14:55:27 +0700 Subject: [PATCH 10/36] fix: add examples/OTAUpdate, examples/Settings --- .github/workflows/build-rpipicow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-rpipicow.yml b/.github/workflows/build-rpipicow.yml index e02d2a4b..8c3b3dca 100644 --- a/.github/workflows/build-rpipicow.yml +++ b/.github/workflows/build-rpipicow.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate, examples/Settings] steps: - name: Step 1 - Checkout Repo From 6a7a708ffee183c8cf9aa169e8d6f9a0ca78d057 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 7 Jul 2024 14:59:20 +0700 Subject: [PATCH 11/36] fix: example --- examples/Settings/Settings.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Settings/Settings.ino b/examples/Settings/Settings.ino index 6d7c5dbb..6676be5f 100644 --- a/examples/Settings/Settings.ino +++ b/examples/Settings/Settings.ino @@ -82,7 +82,7 @@ void setupWiFi() { // setup function for SinricPro void setupSinricPro() { SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; - mySwitch1.onSetSetting(onSetDeviceSetting); + mySwitch.onSetSetting(onSetDeviceSetting); // setup SinricPro SinricPro.onConnected([]() { From edd3bd222518da5ee49568b9bf01214a9848a2ab Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 8 Jul 2024 17:22:03 +0700 Subject: [PATCH 12/36] fix: OTA on esp32/esp8266/picow --- examples/OTAUpdate/Cert.h | 32 ++++++++++ examples/OTAUpdate/ESP32OTAHelper.h | 87 ++------------------------- examples/OTAUpdate/ESP8266OTAHelper.h | 34 ++++++++--- examples/OTAUpdate/OTAUpdate.ino | 16 ++++- src/SinricPro.h | 2 +- 5 files changed, 79 insertions(+), 92 deletions(-) create mode 100644 examples/OTAUpdate/Cert.h diff --git a/examples/OTAUpdate/Cert.h b/examples/OTAUpdate/Cert.h new file mode 100644 index 00000000..498da1bc --- /dev/null +++ b/examples/OTAUpdate/Cert.h @@ -0,0 +1,32 @@ + +// Ref: https://projects.petrucci.ch/esp32/?page=ssl.php&url=otaupdates.sinric.pro + + +const char rootCACertificate[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw +WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK +a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO +VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw +gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD +ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw +i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB +AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g +BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu +Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C +2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+ +bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG +6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV +XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO +koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq +cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI +E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e +K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX +GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL +sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd +VQD9F6Na/+zmXCc= +-----END CERTIFICATE----- +)EOF"; \ No newline at end of file diff --git a/examples/OTAUpdate/ESP32OTAHelper.h b/examples/OTAUpdate/ESP32OTAHelper.h index 32ac0153..2b10ed9e 100644 --- a/examples/OTAUpdate/ESP32OTAHelper.h +++ b/examples/OTAUpdate/ESP32OTAHelper.h @@ -4,55 +4,18 @@ #include #include #include - -#define FOR_i(from, to) for(int i = (from); i < (to); i++) - -// Task handle for the ota update task -TaskHandle_t otaUpdateTaskHandle = NULL; - -// Ref: https://projects.petrucci.ch/esp32/?page=ssl.php&url=otaupdates.sinric.pro - -const char rootCACertificate[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw -WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg -RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK -a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO -VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw -gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD -ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw -i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB -AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g -BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu -Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C -2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+ -bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG -6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV -XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO -koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq -cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI -E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e -K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX -GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL -sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd -VQD9F6Na/+zmXCc= ------END CERTIFICATE----- -)EOF"; - +#include "Cert.h" // Function to perform the OTA update -bool performUpdate(const String& url) { +bool startOTAUpdate(const String& url) { WiFiClientSecure *client = new WiFiClientSecure; if(client) { client->setCACert(rootCACertificate); - //client->setInsecure(); { // Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is - HTTPClient https; - + HTTPClient https; + Serial.print("[HTTPS] begin...\n"); if (https.begin(*client, url)) { Serial.print("[HTTPS] GET...\n"); @@ -86,6 +49,7 @@ bool performUpdate(const String& url) { Serial.println("OTA done!"); if (Update.isFinished()) { Serial.println("Update successfully completed. Rebooting."); + ESP.restart(); return true; } else { Serial.println("Update not finished? Something went wrong!"); @@ -115,45 +79,6 @@ bool performUpdate(const String& url) { } return false; -} - -// Task function for OTA update -void otaUpdateTask(void * parameter) { - String url = *((String*)parameter); - delete (String*)parameter; - - Serial.println("Starting OTA update"); - - FOR_i(1, 5) { - bool updateSuccess = performUpdate(url); - - if (updateSuccess) { - Serial.println("Update successful. Rebooting..."); - ESP.restart(); - } else { - Serial.println("Update failed. Retrying.."); // To fix random DNS lookup errors - delay(1000); - } - } - - // Task is done, delete itself - vTaskDelete(NULL); -} - -bool startOTAUpdate(const String& url) { - String* urlCopy = new String(url); - - // Create the task, passing the URL as a parameter - xTaskCreate( - otaUpdateTask, // Function to implement the task - "otaUpdateTask", // Name of the task - 10000, // Stack size in words - (void*)urlCopy, // Task input parameter - 1, // Priority of the task - &otaUpdateTaskHandle // Task handle - ); - - return true; -} +} #endif \ No newline at end of file diff --git a/examples/OTAUpdate/ESP8266OTAHelper.h b/examples/OTAUpdate/ESP8266OTAHelper.h index 25ad5543..0d589e31 100644 --- a/examples/OTAUpdate/ESP8266OTAHelper.h +++ b/examples/OTAUpdate/ESP8266OTAHelper.h @@ -2,6 +2,7 @@ #include #include +#include "Cert.h" #if defined(ARDUINO_ARCH_RP2040) #include @@ -17,19 +18,37 @@ #define OTA_CLASS ESPhttpUpdate #endif - + +String extractOTAHostname(const String& url) { + int index = url.indexOf("//") + 2; + if (index < 0) { + return ""; // Handle invalid URL format + } + + int endIndex = url.indexOf("/", index); + if (endIndex < 0) { + endIndex = url.length(); + } + + return url.substring(index, endIndex); +} + // Function to perform the OTA update bool startOTAUpdate(const String& url) { - Serial.println("Starting OTA update"); - + #if defined(ARDUINO_ARCH_RP2040) WiFiClientSecure client; + client.setBufferSizes(4096, 4096); // For OTA to work on limited RAM #elif defined(ESP8266) - BearSSL::WiFiClientSecure client; + BearSSL::WiFiClientSecure client; + // Use MFLN to reduce the memory usage + String host = extractOTAHostname(url); + bool mfln = client.probeMaxFragmentLength(host, 443, 512); + Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); + if (mfln) { client.setBufferSizes(512, 512); } else client.setBufferSizes(4096, 4096); #endif - client.setInsecure(); - client.setBufferSizes(4096, 4096); // For OTA to work on limited RAM + client.setInsecure(); // The line below is optional. It can be used to blink the LED on the board during flashing // The LED will be on during download of one buffer of data from the network. The LED will @@ -39,9 +58,10 @@ bool startOTAUpdate(const String& url) { // value is used to put the LED on. If the LED is on with HIGH, that value should be passed //ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); + Serial.printf("Starting the OTA update. This may take few mins to complete!\n"); auto http_ret = OTA_CLASS.update(client, url); - //if success reboot + //if success reboot will reboot! switch (http_ret) { case HTTP_UPDATE_OK: diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index b49554f9..5ba3e306 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -17,6 +17,8 @@ // Your firmware version. Must be above SinricPro.h. Do not rename this. #define FIRMWARE_VERSION "1.1.1" +// Sketch -> Export Compiled Binary to export + #ifdef ENABLE_DEBUG #define DEBUG_ESP_PORT Serial #define NODEBUG_WEBSOCKETS @@ -38,11 +40,13 @@ #include "SemVer.h" #include "SinricPro.h" +#include "SinricProSwitch.h" #define WIFI_SSID "YOUR-WIFI-SSID" #define WIFI_PASS "YOUR-WIFI-PASSWORD" -#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" -#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "SWITCH_ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" #define BAUD_RATE 115200 // Change baudrate to your need @@ -65,7 +69,8 @@ bool handleOTAUpdate(const String& url, int major, int minor, int patch) { Serial.println("Current version is up to date."); } - return true; // OTA Update started successfully + // Does not reach here. Gets rebooted above. + return true; } // setup function for WiFi connection @@ -86,11 +91,16 @@ void setupWiFi() { Serial.printf("."); delay(250); } + + WiFi.config(WiFi.localIP(), IPAddress(8, 8, 8, 8)); // set to Google DNS to avoid DNS look-up errors + Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); } // setup function for SinricPro void setupSinricPro() { + SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; + // setup SinricPro SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); diff --git a/src/SinricPro.h b/src/SinricPro.h index 2ebaccd3..8a044b8f 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -324,7 +324,7 @@ void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface bool success = _moduleCommandHandler.handleRequest(request); responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success; - responseMessage.remove(FSTR_SINRICPRO_deviceId); + responseMessage[FSTR_SINRICPRO_payload].remove(FSTR_SINRICPRO_deviceId); String responseString; serializeJson(responseMessage, responseString); From c1dcff29618bef81094e06ff5e999df57bd57a63 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 8 Jul 2024 17:31:25 +0700 Subject: [PATCH 13/36] fix: WiFi.config --- examples/OTAUpdate/OTAUpdate.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index 5ba3e306..dd669249 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -92,7 +92,9 @@ void setupWiFi() { delay(250); } - WiFi.config(WiFi.localIP(), IPAddress(8, 8, 8, 8)); // set to Google DNS to avoid DNS look-up errors + #if defined(ESP32) + WiFi.config(WiFi.localIP(), IPAddress(8, 8, 8, 8)); // Use Google DNS to fix DNS look-up errors. + #endif Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); } From 011d4bed341f733d129a3ad9829db7e53caac9df Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 8 Jul 2024 17:31:41 +0700 Subject: [PATCH 14/36] fix: logging --- src/SinricProModuleCommandHandler.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index 02ea8e56..ece1bbeb 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -65,6 +65,8 @@ bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { String id = request.request_value[FSTR_SETTINGS_id]; String value = request.request_value[FSTR_SETTINGS_value]; return _setSettingCallbackHandler(id, value); + } else { + DEBUG_SINRIC("[SinricProModuleCommandHandler:handleRequest]: action: %s not supported!\r\n", request.action.c_str()); } return false; } From 37784a575f3071ed76bdaaa5062c1b6c21f51de0 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 8 Jul 2024 20:13:25 +0700 Subject: [PATCH 15/36] fix: add esp32 dns lookup fix --- examples/OTAUpdate/OTAUpdate.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index dd669249..5a8d6e5f 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -92,9 +92,10 @@ void setupWiFi() { delay(250); } - #if defined(ESP32) - WiFi.config(WiFi.localIP(), IPAddress(8, 8, 8, 8)); // Use Google DNS to fix DNS look-up errors. - #endif + // Enable to fix random DNS lookup errors + // #if defined(ESP32) + // WiFi.config(WiFi.localIP(), IPAddress(8, 8, 8, 8)); + // #endif Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); } From baa8582ad4b1969b97eac1f842e5709141446b32 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 15:57:10 +0700 Subject: [PATCH 16/36] feat: force update added. --- src/SinricPro.h | 11 ++++++++++- src/SinricProModuleCommandHandler.h | 6 ++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/SinricPro.h b/src/SinricPro.h index 8a044b8f..487caae6 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -48,9 +48,10 @@ using DisconnectedCallbackHandler = std::function; * @param major The major version number * @param minor The minor version number * @param patch The patch version number + * @param forceUpdate Skip the version check and apply the update. * @return bool True if the update process started successful, false otherwise. */ -using OTAUpdateCallbackHandler = std::function; +using OTAUpdateCallbackHandler = std::function; /** * @brief Function signature for setting a module setting. @@ -325,6 +326,14 @@ void SinricProClass::handleModuleRequest(JsonDocument& requestMessage, interface responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_success] = success; responseMessage[FSTR_SINRICPRO_payload].remove(FSTR_SINRICPRO_deviceId); + if (!success) { + if (responseMessageStr.length() > 0) { + responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = responseMessageStr; + responseMessageStr = ""; + } else { + responseMessage[FSTR_SINRICPRO_payload][FSTR_SINRICPRO_message] = "Module did not handle \"" + action + "\""; + } + } String responseString; serializeJson(responseMessage, responseString); diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index ece1bbeb..3600f8d0 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -18,11 +18,12 @@ FSTR(OTA, version); // "version" FSTR(OTA, major); // "major" FSTR(OTA, minor); // "minor" FSTR(OTA, patch); // "patch" +FSTR(OTA, forceUpdate); // "forceUpdate" FSTR(SETTINGS, setSetting); // "setSetting" FSTR(SETTINGS, id); // "id" FSTR(SETTINGS, value); // "value" -using OTAUpdateCallbackHandler = std::function; +using OTAUpdateCallbackHandler = std::function; using SetSettingCallbackHandler = std::function; class SinricProModuleCommandHandler { @@ -59,7 +60,8 @@ bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { int major = request.request_value[FSTR_OTA_version][FSTR_OTA_major]; int minor = request.request_value[FSTR_OTA_version][FSTR_OTA_minor]; int patch = request.request_value[FSTR_OTA_version][FSTR_OTA_patch]; - return _otaUpdateCallbackHandler(url, major, minor, patch); + bool forceUpdate = request.request_value[FSTR_OTA_version][FSTR_OTA_forceUpdate] | false; + return _otaUpdateCallbackHandler(url, major, minor, patch, forceUpdate); } else if (strcmp(FSTR_SETTINGS_setSetting, request.action.c_str()) == 0 && _setSettingCallbackHandler) { String id = request.request_value[FSTR_SETTINGS_id]; From ace2b7178b2fff364170bf27d19b65bb84b7742c Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 15:57:48 +0700 Subject: [PATCH 17/36] chore: refactor ota, setting examples --- examples/OTAUpdate/ESP32OTAHelper.h | 83 ++++--- examples/OTAUpdate/OTAUpdate.ino | 34 +-- examples/Settings/MultiWiFi/MultiWiFi.ino | 206 ++++++++++++++++++ .../MultiWiFi/SinricProWiFiSettings.cpp | 97 +++++++++ .../MultiWiFi/SinricProWiFiSettings.h | 123 +++++++++++ examples/Settings/{ => Settings}/Settings.ino | 11 +- 6 files changed, 496 insertions(+), 58 deletions(-) create mode 100644 examples/Settings/MultiWiFi/MultiWiFi.ino create mode 100644 examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp create mode 100644 examples/Settings/MultiWiFi/SinricProWiFiSettings.h rename examples/Settings/{ => Settings}/Settings.ino (92%) diff --git a/examples/OTAUpdate/ESP32OTAHelper.h b/examples/OTAUpdate/ESP32OTAHelper.h index 2b10ed9e..50080e4e 100644 --- a/examples/OTAUpdate/ESP32OTAHelper.h +++ b/examples/OTAUpdate/ESP32OTAHelper.h @@ -6,79 +6,96 @@ #include #include "Cert.h" -// Function to perform the OTA update -bool startOTAUpdate(const String& url) { +struct OTAResult { + bool success; + String errorMessage; +}; + +OTAResult startOTAUpdate(const String &url) { + OTAResult result = { false, "" }; WiFiClientSecure *client = new WiFiClientSecure; - - if(client) { + + if (client) { client->setCACert(rootCACertificate); { - // Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is - HTTPClient https; - - Serial.print("[HTTPS] begin...\n"); + // Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is + HTTPClient https; + + Serial.print("[startOTAUpdate()] begin...\n"); if (https.begin(*client, url)) { - Serial.print("[HTTPS] GET...\n"); + Serial.print("[startOTAUpdate()] GET...\n"); // start connection and send HTTP header int httpCode = https.GET(); - + // httpCode will be negative on error if (httpCode > 0) { - // HTTP header has been send and Server response header has been handled - Serial.printf("[HTTPS] GET... code: %d\n", httpCode); - + // HTTP header has been sent and Server response header has been handled + Serial.printf("[startOTAUpdate()] GET... code: %d\n", httpCode); + // file found at server if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { // get length of document (is -1 when Server sends no Content-Length header) int contentLength = https.getSize(); Serial.printf("Content-Length: %d\n", contentLength); - + if (contentLength > 0) { bool canBegin = Update.begin(contentLength); if (canBegin) { - WiFiClient * stream = https.getStreamPtr(); + WiFiClient *stream = https.getStreamPtr(); size_t written = Update.writeStream(*stream); - + if (written == contentLength) { - Serial.println("Written : " + String(written) + " successfully"); + Serial.println("[startOTAUpdate()] Written : " + String(written) + " successfully"); } else { - Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"); + result.errorMessage = "Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"; + Serial.println("[startOTAUpdate()] " + result.errorMessage); } - + if (Update.end()) { - Serial.println("OTA done!"); + Serial.println("[startOTAUpdate()] OTA done!"); if (Update.isFinished()) { - Serial.println("Update successfully completed. Rebooting."); + Serial.println("[startOTAUpdate()] Update successfully completed. Rebooting."); ESP.restart(); - return true; + result.success = true; + return result; } else { - Serial.println("Update not finished? Something went wrong!"); + result.errorMessage = "Update not finished? Something went wrong!"; + Serial.println("[startOTAUpdate()] " + result.errorMessage); } } else { - Serial.println("Error Occurred. Error #: " + String(Update.getError())); + result.errorMessage = "Error Occurred. Error #: " + String(Update.getError()); + Serial.println("[startOTAUpdate()] " + result.errorMessage); } } else { - Serial.println("Not enough space to begin OTA"); + result.errorMessage = "Not enough space to begin OTA"; + Serial.println("[startOTAUpdate()] " + result.errorMessage); } } else { - Serial.println("There was no content length in the response"); + result.errorMessage = "There was no content length in the response"; + Serial.println("[startOTAUpdate()] " + result.errorMessage); } + } else { + result.errorMessage = "HTTP response code: " + String(httpCode); + Serial.println("[startOTAUpdate()] " + result.errorMessage); } } else { - Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str()); + result.errorMessage = "GET... failed, error: " + https.errorToString(httpCode); + Serial.println("[startOTAUpdate()] " + result.errorMessage); } - + https.end(); } else { - Serial.printf("[HTTPS] Unable to connect\n"); + result.errorMessage = "Unable to connect"; + Serial.println("[startOTAUpdate()] " + result.errorMessage); } } delete client; } else { - Serial.println("Unable to create client"); + result.errorMessage = "Unable to create client"; + Serial.println("[startOTAUpdate()] " + result.errorMessage); } - - return false; -} + + return result; +} #endif \ No newline at end of file diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index 5a8d6e5f..f8f69390 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -12,7 +12,8 @@ */ // Uncomment the following line to enable serial debug output -//#define ENABLE_DEBUG +// #define SINRICPRO_NOSSL // Uncomment if you have memory limitation issues. +#define ENABLE_DEBUG // Your firmware version. Must be above SinricPro.h. Do not rename this. #define FIRMWARE_VERSION "1.1.1" @@ -50,27 +51,35 @@ #define BAUD_RATE 115200 // Change baudrate to your need -bool handleOTAUpdate(const String& url, int major, int minor, int patch) { +bool handleOTAUpdate(const String& url, int major, int minor, int patch, bool forceUpdate) { Version currentVersion = parseVersion(FIRMWARE_VERSION); Version newVersion = parseVersion(String(major) + "." + String(minor) + "." + String(patch)); + bool updateAvailable = isNewerVersion(currentVersion, newVersion); - bool updateAvailable = isNewerVersion(currentVersion, newVersion); Serial.print("URL: "); Serial.println(url.c_str()); Serial.print("Current version: "); Serial.println(currentVersion.toString()); Serial.print("New version: "); Serial.println(newVersion.toString()); - - if (updateAvailable) { - Serial.println("Update available!"); - return startOTAUpdate(url); + if (forceUpdate) Serial.println("Enforcing OTA update!"); + + // Handle OTA update based on forceUpdate flag and update availability + if (forceUpdate || updateAvailable) { + if (updateAvailable) { + Serial.println("Update available!"); + } + + OTAResult result = startOTAUpdate(url); + if (!result.success) { + SinricPro.setResponseMessage(std::move(result.errorMessage)); + } + return result.success; } else { + SinricPro.setResponseMessage("Current version is up to date."); Serial.println("Current version is up to date."); + return false; } - - // Does not reach here. Gets rebooted above. - return true; } // setup function for WiFi connection @@ -92,11 +101,6 @@ void setupWiFi() { delay(250); } - // Enable to fix random DNS lookup errors - // #if defined(ESP32) - // WiFi.config(WiFi.localIP(), IPAddress(8, 8, 8, 8)); - // #endif - Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); } diff --git a/examples/Settings/MultiWiFi/MultiWiFi.ino b/examples/Settings/MultiWiFi/MultiWiFi.ino new file mode 100644 index 00000000..09074eb0 --- /dev/null +++ b/examples/Settings/MultiWiFi/MultiWiFi.ino @@ -0,0 +1,206 @@ +/* + * Example for how to use SinricPro Primary and Secondary WiFi settings with ESP buit-in WiFiMulti. + * + * If you encounter any issues: + * - check the readme.md at https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md + * - ensure all dependent libraries are installed + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#arduinoide + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#dependencies + * - open serial monitor and check whats happening + * - check full user documentation at https://sinricpro.github.io/esp8266-esp32-sdk + * - visit https://github.com/sinricpro/esp8266-esp32-sdk/issues and check for existing issues or open a new one + */ + +// Uncomment the following line to enable serial debug output +#define ENABLE_DEBUG + +#ifdef ENABLE_DEBUG +#define DEBUG_ESP_PORT Serial +#define NODEBUG_WEBSOCKETS +#define NDEBUG +#endif + +#include + +#if defined(ESP8266) +#include +#include +ESP8266WiFiMulti wifiMulti; ///< ESP8266 WiFi multi instance. +#elif defined(ESP32) +#include +#include +WiFiMulti wifiMulti; ///< ESP32 WiFi multi instance. +#endif + +#include "FS.h" +#include "LittleFS.h" +#include "ArduinoJson.h" + +#include "SinricPro.h" +#include "SinricProSwitch.h" +#include "SinricProWiFiSettings.h" + +#define APP_KEY "" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "" // Should look like "5dc1564130xxxxxxxxxxxxxx" + +#define BAUD_RATE 115200 // Change baudrate to your need + +#define SET_WIFI_PRIMARY "pro.sinric::set.wifi.primary" +#define SET_WIFI_SECONDARY "pro.sinric::set.wifi.secondary" + +const bool formatLittleFSIfFailed = true; +const unsigned long NO_WIFI_REBOOT_TIMEOUT = 300000; // 5 minutes in milliseconds +unsigned long wifiStartTime; + +const char* primary_ssid = ""; // Set to your primary wifi's ssid +const char* primary_password = ""; // Set to your primary wifi's password +const char* secondary_ssid = ""; // Set to your secondary wifi's ssid +const char* secondary_password = ""; // Set to your secondary wifi's password + +SinricProWiFiSettings spws(primary_ssid, primary_password, secondary_ssid, secondary_password, "/wificonfig.dat"); + +bool onSetModuleSetting(const String& id, const String& value) { + // Handle module settings. + + JsonDocument doc; + DeserializationError error = deserializeJson(doc, value); + + if (error) { + Serial.print(F("onSetModuleSetting::deserializeJson() failed: ")); + Serial.println(error.f_str()); + return false; + } + + const char* password = doc["password"].as(); + const char* ssid = doc["ssid"].as(); + + if (id == SET_WIFI_PRIMARY) { // Set primary WiFi + spws.updatePrimarySettings(ssid, password); + } else if (id == SET_WIFI_SECONDARY) { // Set secondary WiFi + spws.updateSecondarySettings(ssid, password); + } + + bool connect = doc["connectNow"] | false; + if (connect) { + wifiMulti.APlistClean(); + wifiMulti.addAP(ssid, password); + return waitForConnectResult(); + } + + return true; +} + +bool setupLittleFS() { + // Sets up the LittleFS. + if (!LittleFS.begin(true)) { + Serial.println("An Error has occurred while mounting LittleFS"); + + if (formatLittleFSIfFailed) { + Serial.println("Formatting LittleFS..."); + if (LittleFS.format()) { + Serial.println("LittleFS formatted successfully!"); + return true; + } else { + Serial.println("LittleFS formatting error!"); + return false; + } + } else { + Serial.println("LittleFS error!"); + return false; + } + } else { + Serial.println("LittleFS Mount successful"); + return true; + } + + return true; +} + +bool waitForConnectResult() { + unsigned long startTime = millis(); + constexpr unsigned int connectTimeout = 10000; + + Serial.println("Connecting Wifi..."); + while (wifiMulti.run() != WL_CONNECTED) { + Serial.print("."); + delay(500); + if (millis() - startTime >= connectTimeout) { + Serial.println("WIFI not connected"); + return false; + } + } + + Serial.printf("\nWiFi connected\nIP address: %s\n", WiFi.localIP().toString().c_str()); + return true; +} + +// setup function for WiFi connection +void setupWiFi() { + Serial.printf("\r\n[Wifi]: Connecting"); + + WiFi.mode(WIFI_STA); +#if defined(ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); +#elif defined(ESP32) + WiFi.setSleep(false); +#endif + WiFi.setAutoReconnect(true); + + // Load settings from file or using defaults if loading fails. + spws.begin(); + + const SinricProWiFiSettings::wifi_settings_t& settings = spws.getWiFiSettings(); + + if (spws.isValidSetting(settings.primarySSID, settings.primaryPassword)) { + wifiMulti.addAP(settings.primarySSID, settings.primaryPassword); + } + + if (spws.isValidSetting(settings.secondarySSID, settings.secondaryPassword)) { + wifiMulti.addAP(settings.secondarySSID, settings.secondaryPassword); + } + + waitForConnectResult(); +} + +// setup function for SinricPro +void setupSinricPro() { + SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; + + // setup SinricPro + SinricPro.onConnected([]() { + Serial.printf("Connected to SinricPro\r\n"); + }); + SinricPro.onDisconnected([]() { + Serial.printf("Disconnected from SinricPro\r\n"); + }); + + SinricPro.onSetSetting(onSetModuleSetting); + SinricPro.begin(APP_KEY, APP_SECRET); +} + +// main setup function +void setup() { + Serial.begin(BAUD_RATE); + Serial.printf("\r\n\r\n"); + setupLittleFS(); + setupWiFi(); + setupSinricPro(); +} + +void rebootIfNoWiFi() { + // If no WiFI connection for 5 mins reboot the ESP. ESP will connect to either primary or secondary based on availability + if (WiFi.status() != WL_CONNECTED && (millis() - wifiStartTime >= NO_WIFI_REBOOT_TIMEOUT)) { + Serial.println("WiFi connection timed out. Rebooting..."); + ESP.restart(); + } else { + // Reset the start time if WiFi is connected + wifiStartTime = millis(); + } +} + +void loop() { + wifiMulti.run(); + SinricPro.handle(); + rebootIfNoWiFi(); +} diff --git a/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp b/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp new file mode 100644 index 00000000..205dc4db --- /dev/null +++ b/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp @@ -0,0 +1,97 @@ +#include "SinricProWiFiSettings.h" + +SinricProWiFiSettings::SinricProWiFiSettings(const char* defaultPrimarySSID, const char* defaultPrimaryPassword, + const char* defaultSecondarySSID, const char* defaultSecondaryPassword, + const char* configFileName) + : defaultPrimarySSID(defaultPrimarySSID), defaultPrimaryPassword(defaultPrimaryPassword), + defaultSecondarySSID(defaultSecondarySSID), defaultSecondaryPassword(defaultSecondaryPassword), + configFileName(configFileName) { + memset(&wifiSettings, 0, sizeof(wifiSettings)); +} + +void SinricProWiFiSettings::begin() { + if (!loadFromFile()) { + saveDefaultSettings(); + } + printSettings(); +} + +void SinricProWiFiSettings::updatePrimarySettings(const char* newSSID, const char* newPassword) { + if (isValidSetting(newSSID, newPassword)) { + strncpy(wifiSettings.primarySSID, newSSID, sizeof(wifiSettings.primarySSID)); + strncpy(wifiSettings.primaryPassword, newPassword, sizeof(wifiSettings.primaryPassword)); + saveToFile(); + } else { + Serial.println("Invalid Primary SSID or Password"); + } +} + +void SinricProWiFiSettings::updateSecondarySettings(const char* newSSID, const char* newPassword) { + if (isValidSetting(newSSID, newPassword)) { + strncpy(wifiSettings.secondarySSID, newSSID, sizeof(wifiSettings.secondarySSID)); + strncpy(wifiSettings.secondaryPassword, newPassword, sizeof(wifiSettings.secondaryPassword)); + saveToFile(); + } else { + Serial.println("Invalid Secondary SSID or Password"); + } +} + +void SinricProWiFiSettings::printSettings() const { + Serial.printf("Primary SSID: %s\n", wifiSettings.primarySSID); + Serial.printf("Primary Password: %s\n", wifiSettings.primaryPassword); + Serial.printf("Secondary SSID: %s\n", wifiSettings.secondarySSID); + Serial.printf("Secondary Password: %s\n", wifiSettings.secondaryPassword); +} + +void SinricProWiFiSettings::saveToFile() { + File file = LittleFS.open(configFileName, FILE_WRITE); + if (file) { + file.write(reinterpret_cast(&wifiSettings), sizeof(wifiSettings)); + file.close(); + } +} + +bool SinricProWiFiSettings::loadFromFile() { + File file = LittleFS.open(configFileName, FILE_READ); + if (file && file.size() == sizeof(wifiSettings)) { + file.read(reinterpret_cast(&wifiSettings), sizeof(wifiSettings)); + file.close(); + return true; + } + return false; +} + +void SinricProWiFiSettings::saveDefaultSettings() { + Serial.println("Saving default WiFi login!"); + + strncpy(wifiSettings.primarySSID, defaultPrimarySSID, sizeof(wifiSettings.primarySSID)); + strncpy(wifiSettings.primaryPassword, defaultPrimaryPassword, sizeof(wifiSettings.primaryPassword)); + strncpy(wifiSettings.secondarySSID, defaultSecondarySSID, sizeof(wifiSettings.secondarySSID)); + strncpy(wifiSettings.secondaryPassword, defaultSecondaryPassword, sizeof(wifiSettings.secondaryPassword)); + + saveToFile(); +} + +void SinricProWiFiSettings::deleteAllSettings() { + memset(&wifiSettings, 0, sizeof(wifiSettings)); + if (LittleFS.exists(configFileName)) { + LittleFS.remove(configFileName); + } + Serial.println("All WiFi settings have been deleted."); +} + +bool SinricProWiFiSettings::isValidSetting(const char* ssid, const char* password) const { + return validateSSID(ssid) && validatePassword(password); +} + +bool SinricProWiFiSettings::validateSSID(const char* ssid) const { + return ssid && strlen(ssid) > 0 && strlen(ssid) < sizeof(wifi_settings_t::primarySSID); +} + +bool SinricProWiFiSettings::validatePassword(const char* password) const { + return password && strlen(password) < sizeof(wifi_settings_t::primaryPassword); +} + +const SinricProWiFiSettings::wifi_settings_t& SinricProWiFiSettings::getWiFiSettings() const { + return wifiSettings; +} diff --git a/examples/Settings/MultiWiFi/SinricProWiFiSettings.h b/examples/Settings/MultiWiFi/SinricProWiFiSettings.h new file mode 100644 index 00000000..93e34c7e --- /dev/null +++ b/examples/Settings/MultiWiFi/SinricProWiFiSettings.h @@ -0,0 +1,123 @@ +#ifndef SinricProWiFiSettings_H +#define SinricProWiFiSettings_H + +#include +#include "FS.h" +#include "LittleFS.h" + + +/** + * @brief Manages SinricPro using primary and secondary SSID configurations. + */ +class SinricProWiFiSettings { +public: + struct wifi_settings_t { + char primarySSID[32]; ///< Primary SSID of the WiFi network. + char primaryPassword[64]; ///< Primary password of the WiFi network. + char secondarySSID[32]; ///< Secondary SSID of the WiFi network. + char secondaryPassword[64]; ///< Secondary password of the WiFi network. + }; + + /** + * @brief Construct a new SinricProWiFiSettings object with default WiFi settings. + * + * @param defaultPrimarySSID Default primary SSID. + * @param defaultPrimaryPassword Default primary password. + * @param defaultSecondarySSID Default secondary SSID. + * @param defaultSecondaryPassword Default secondary password. + * @param configFileName File name for storing WiFi settings. + */ + SinricProWiFiSettings(const char* defaultPrimarySSID, const char* defaultPrimaryPassword, + const char* defaultSecondarySSID, const char* defaultSecondaryPassword, + const char* configFileName); + + /** + * @brief Initializes the WiFi manager, loading settings from file or using defaults if loading fails. + */ + void begin(); + + /** + * @brief Updates the primary WiFi settings. + * + * @param newSSID New primary SSID. + * @param newPassword New primary password. + */ + void updatePrimarySettings(const char* newSSID, const char* newPassword); + + /** + * @brief Updates the secondary WiFi settings. + * + * @param newSSID New secondary SSID. + * @param newPassword New secondary password. + */ + void updateSecondarySettings(const char* newSSID, const char* newPassword); + + /** + * @brief Prints the current WiFi settings to the Serial console. + */ + void printSettings() const; + + /** + * @brief Checks if the provided SSID and password are valid. + * + * @param ssid SSID to check. + * @param password Password to check. + * @return true if both SSID and password are valid, false otherwise. + */ + bool isValidSetting(const char* ssid, const char* password) const; + + /** + * @brief Returns WiFi settings. + */ + const wifi_settings_t& getWiFiSettings() const; + +private: + const char* defaultPrimarySSID; ///< Default primary SSID. + const char* defaultPrimaryPassword; ///< Default primary password. + const char* defaultSecondarySSID; ///< Default secondary SSID. + const char* defaultSecondaryPassword; ///< Default secondary password. + const char* configFileName; ///< File name to store WiFi settings. + + wifi_settings_t wifiSettings; + + /** + * @brief Saves the current WiFi settings to a file. + */ + void saveToFile(); + + /** + * @brief Loads WiFi settings from a file. + * + * @return true if loaded successfully, false otherwise. + */ + bool loadFromFile(); + + /** + * @brief Saves the default WiFi settings to a file and updates the current settings. + */ + void saveDefaultSettings(); + + + /** + * @brief Validates the given SSID. + * + * @param ssid SSID to validate. + * @return true if the SSID is valid, false otherwise. + */ + bool validateSSID(const char* ssid) const; + + /** + * @brief Validates the given password. + * + * @param password Password to validate. + * @return true if the password is valid, false otherwise. + */ + bool validatePassword(const char* password) const; + + /** + * @brief Deletes all the stored WiFi settings. + */ + void deleteAllSettings(); +}; + +#endif // SinricProWiFiSettings_H \ No newline at end of file diff --git a/examples/Settings/Settings.ino b/examples/Settings/Settings/Settings.ino similarity index 92% rename from examples/Settings/Settings.ino rename to examples/Settings/Settings/Settings.ino index 6676be5f..f4ecfebb 100644 --- a/examples/Settings/Settings.ino +++ b/examples/Settings/Settings/Settings.ino @@ -36,10 +36,8 @@ #define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" #define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" #define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" -#define BAUD_RATE 115200 // Change baudrate to your need -#define SET_WIFI_PRIMARY "pro.sinric::set.wifi.primary" -#define SET_WIFI_SECONDARY "pro.sinric::set.wifi.secondary" +#define BAUD_RATE 115200 // Change baudrate to your need bool onSetDeviceSetting(const String& deviceId, const String& settingId, const String& settingValue) { // Handle device settings. @@ -48,13 +46,6 @@ bool onSetDeviceSetting(const String& deviceId, const String& settingId, const S bool onSetModuleSetting(const String& id, const String& value) { // Handle module settings. - - if (id == SET_WIFI_PRIMARY) { - // Set primary WiFi - } else if (id == SET_WIFI_SECONDARY) { - // Set secondary WiFi - } - return true; } From be1cdf3cb0df0a27298078570a134d2e5c359de0 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 16:09:16 +0700 Subject: [PATCH 18/36] fix: esp8266 build errors --- examples/Settings/MultiWiFi/MultiWiFi.ino | 14 ++++++++++-- .../MultiWiFi/SinricProWiFiSettings.cpp | 15 +++++++++++-- examples/Settings/Settings/Settings.ino | 22 +++++++++---------- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/examples/Settings/MultiWiFi/MultiWiFi.ino b/examples/Settings/MultiWiFi/MultiWiFi.ino index 09074eb0..b6059015 100644 --- a/examples/Settings/MultiWiFi/MultiWiFi.ino +++ b/examples/Settings/MultiWiFi/MultiWiFi.ino @@ -83,7 +83,12 @@ bool onSetModuleSetting(const String& id, const String& value) { bool connect = doc["connectNow"] | false; if (connect) { - wifiMulti.APlistClean(); + #if defined(ESP8266) + wifiMulti.cleanAPlist(); + #elif defined(ESP32) + wifiMulti.APlistClean(); + #endif + wifiMulti.addAP(ssid, password); return waitForConnectResult(); } @@ -93,7 +98,12 @@ bool onSetModuleSetting(const String& id, const String& value) { bool setupLittleFS() { // Sets up the LittleFS. - if (!LittleFS.begin(true)) { + #if defined(ESP8266) + if (!LittleFS.begin()) { + #elif defined(ESP32) + if (!LittleFS.begin(true)) { + #endif + Serial.println("An Error has occurred while mounting LittleFS"); if (formatLittleFSIfFailed) { diff --git a/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp b/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp index 205dc4db..8896ff32 100644 --- a/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp +++ b/examples/Settings/MultiWiFi/SinricProWiFiSettings.cpp @@ -44,7 +44,13 @@ void SinricProWiFiSettings::printSettings() const { } void SinricProWiFiSettings::saveToFile() { - File file = LittleFS.open(configFileName, FILE_WRITE); + + #if defined(ESP8266) + File file = LittleFS.open(configFileName, "w"); + #elif defined(ESP32) + File file = LittleFS.open(configFileName, FILE_WRITE); + #endif + if (file) { file.write(reinterpret_cast(&wifiSettings), sizeof(wifiSettings)); file.close(); @@ -52,7 +58,12 @@ void SinricProWiFiSettings::saveToFile() { } bool SinricProWiFiSettings::loadFromFile() { - File file = LittleFS.open(configFileName, FILE_READ); + #if defined(ESP8266) + File file = LittleFS.open(configFileName, "r"); + #elif defined(ESP32) + File file = LittleFS.open(configFileName, FILE_READ); + #endif + if (file && file.size() == sizeof(wifiSettings)) { file.read(reinterpret_cast(&wifiSettings), sizeof(wifiSettings)); file.close(); diff --git a/examples/Settings/Settings/Settings.ino b/examples/Settings/Settings/Settings.ino index f4ecfebb..53a61ad3 100644 --- a/examples/Settings/Settings/Settings.ino +++ b/examples/Settings/Settings/Settings.ino @@ -15,16 +15,16 @@ //#define ENABLE_DEBUG #ifdef ENABLE_DEBUG - #define DEBUG_ESP_PORT Serial - #define NODEBUG_WEBSOCKETS - #define NDEBUG +#define DEBUG_ESP_PORT Serial +#define NODEBUG_WEBSOCKETS +#define NDEBUG #endif #include #if defined(ESP8266) - #include +#include #elif defined(ESP32) || defined(ARDUINO_ARCH_RP2040) - #include +#include #endif #include "SinricPro.h" @@ -37,18 +37,18 @@ #define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" #define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" -#define BAUD_RATE 115200 // Change baudrate to your need +#define BAUD_RATE 115200 // Change baudrate to your need bool onSetDeviceSetting(const String& deviceId, const String& settingId, const String& settingValue) { - // Handle device settings. - return true; + // Handle device settings. + return true; } bool onSetModuleSetting(const String& id, const String& value) { // Handle module settings. return true; } - + // setup function for WiFi connection void setupWiFi() { Serial.printf("\r\n[Wifi]: Connecting"); @@ -74,7 +74,7 @@ void setupWiFi() { void setupSinricPro() { SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; mySwitch.onSetSetting(onSetDeviceSetting); - + // setup SinricPro SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); @@ -82,7 +82,7 @@ void setupSinricPro() { SinricPro.onDisconnected([]() { Serial.printf("Disconnected from SinricPro\r\n"); }); - + SinricPro.onSetSetting(onSetModuleSetting); SinricPro.begin(APP_KEY, APP_SECRET); } From 78a252e9b45988e90908e0cbfad4fc8252461236 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 16:09:51 +0700 Subject: [PATCH 19/36] fix: exclude ota --- .github/workflows/build-rpipicow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-rpipicow.yml b/.github/workflows/build-rpipicow.yml index 8c3b3dca..0b726452 100644 --- a/.github/workflows/build-rpipicow.yml +++ b/.github/workflows/build-rpipicow.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate, examples/Settings] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/Settings] steps: - name: Step 1 - Checkout Repo From 432fbfccaaa2deca617f4301f6dd5ddfe0c3d40d Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 16:19:59 +0700 Subject: [PATCH 20/36] fix: build errors --- .github/workflows/build-rpipicow.yml | 2 +- examples/OTAUpdate/ESP32OTAHelper.h | 7 +------ examples/OTAUpdate/ESP8266OTAHelper.h | 13 +++++++++---- examples/OTAUpdate/OTAResult.h | 4 ++++ examples/OTAUpdate/OTAUpdate.ino | 2 ++ 5 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 examples/OTAUpdate/OTAResult.h diff --git a/.github/workflows/build-rpipicow.yml b/.github/workflows/build-rpipicow.yml index 0b726452..e02d2a4b 100644 --- a/.github/workflows/build-rpipicow.yml +++ b/.github/workflows/build-rpipicow.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/Settings] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV] steps: - name: Step 1 - Checkout Repo diff --git a/examples/OTAUpdate/ESP32OTAHelper.h b/examples/OTAUpdate/ESP32OTAHelper.h index 50080e4e..8d8e91ec 100644 --- a/examples/OTAUpdate/ESP32OTAHelper.h +++ b/examples/OTAUpdate/ESP32OTAHelper.h @@ -4,12 +4,7 @@ #include #include #include -#include "Cert.h" - -struct OTAResult { - bool success; - String errorMessage; -}; +#include "Cert.h" OTAResult startOTAUpdate(const String &url) { OTAResult result = { false, "" }; diff --git a/examples/OTAUpdate/ESP8266OTAHelper.h b/examples/OTAUpdate/ESP8266OTAHelper.h index 0d589e31..f2e70b95 100644 --- a/examples/OTAUpdate/ESP8266OTAHelper.h +++ b/examples/OTAUpdate/ESP8266OTAHelper.h @@ -2,7 +2,7 @@ #include #include -#include "Cert.h" +#include "Cert.h" #if defined(ARDUINO_ARCH_RP2040) #include @@ -34,8 +34,9 @@ String extractOTAHostname(const String& url) { } // Function to perform the OTA update -bool startOTAUpdate(const String& url) { - +OTAResult startOTAUpdate(const String& url) { + OTAResult result = { false, "" }; + #if defined(ARDUINO_ARCH_RP2040) WiFiClientSecure client; client.setBufferSizes(4096, 4096); // For OTA to work on limited RAM @@ -65,19 +66,23 @@ bool startOTAUpdate(const String& url) { switch (http_ret) { case HTTP_UPDATE_OK: + result.success = true; Serial.printf("HTTP_UPDATE_OK\n"); break; case HTTP_UPDATE_FAILED: + result.success = false; Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", OTA_CLASS.getLastError(), OTA_CLASS.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: + result.success = false; Serial.println("HTTP_UPDATE_NO_UPDATES"); break; } - return false; + + return result; } #endif \ No newline at end of file diff --git a/examples/OTAUpdate/OTAResult.h b/examples/OTAUpdate/OTAResult.h new file mode 100644 index 00000000..bdff598f --- /dev/null +++ b/examples/OTAUpdate/OTAResult.h @@ -0,0 +1,4 @@ +struct OTAResult { + bool success; + String errorMessage; +}; diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index f8f69390..aaa30596 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -27,6 +27,7 @@ #endif #include +#include "OTAResult.h" #if defined(ESP8266) #include @@ -43,6 +44,7 @@ #include "SinricPro.h" #include "SinricProSwitch.h" + #define WIFI_SSID "YOUR-WIFI-SSID" #define WIFI_PASS "YOUR-WIFI-PASSWORD" #define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" From 77700a39f8e039ba40415cd7ae7baf1081ec6bc2 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 16:37:08 +0700 Subject: [PATCH 21/36] fix: build errors --- examples/OTAUpdate/Cert.h | 1 + examples/OTAUpdate/OTAResult.h | 3 +++ examples/OTAUpdate/OTAUpdate.ino | 2 +- examples/OTAUpdate/SemVer.h | 2 ++ examples/Settings/MultiWiFi/MultiWiFi.ino | 2 +- examples/Settings/MultiWiFi/SinricProWiFiSettings.h | 5 +---- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/examples/OTAUpdate/Cert.h b/examples/OTAUpdate/Cert.h index 498da1bc..65bcd526 100644 --- a/examples/OTAUpdate/Cert.h +++ b/examples/OTAUpdate/Cert.h @@ -1,3 +1,4 @@ +#pragma once // Ref: https://projects.petrucci.ch/esp32/?page=ssl.php&url=otaupdates.sinric.pro diff --git a/examples/OTAUpdate/OTAResult.h b/examples/OTAUpdate/OTAResult.h index bdff598f..9f890dfa 100644 --- a/examples/OTAUpdate/OTAResult.h +++ b/examples/OTAUpdate/OTAResult.h @@ -1,3 +1,6 @@ +#pragma once +#include + struct OTAResult { bool success; String errorMessage; diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index aaa30596..fdd2cf12 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -13,7 +13,7 @@ // Uncomment the following line to enable serial debug output // #define SINRICPRO_NOSSL // Uncomment if you have memory limitation issues. -#define ENABLE_DEBUG +// #define ENABLE_DEBUG // Your firmware version. Must be above SinricPro.h. Do not rename this. #define FIRMWARE_VERSION "1.1.1" diff --git a/examples/OTAUpdate/SemVer.h b/examples/OTAUpdate/SemVer.h index f936ff0e..1d1776e0 100644 --- a/examples/OTAUpdate/SemVer.h +++ b/examples/OTAUpdate/SemVer.h @@ -1,3 +1,5 @@ +#pragma once + #include struct Version { diff --git a/examples/Settings/MultiWiFi/MultiWiFi.ino b/examples/Settings/MultiWiFi/MultiWiFi.ino index b6059015..16381c91 100644 --- a/examples/Settings/MultiWiFi/MultiWiFi.ino +++ b/examples/Settings/MultiWiFi/MultiWiFi.ino @@ -12,7 +12,7 @@ */ // Uncomment the following line to enable serial debug output -#define ENABLE_DEBUG +//#define ENABLE_DEBUG #ifdef ENABLE_DEBUG #define DEBUG_ESP_PORT Serial diff --git a/examples/Settings/MultiWiFi/SinricProWiFiSettings.h b/examples/Settings/MultiWiFi/SinricProWiFiSettings.h index 93e34c7e..b3352db6 100644 --- a/examples/Settings/MultiWiFi/SinricProWiFiSettings.h +++ b/examples/Settings/MultiWiFi/SinricProWiFiSettings.h @@ -1,5 +1,4 @@ -#ifndef SinricProWiFiSettings_H -#define SinricProWiFiSettings_H +#pragma once #include #include "FS.h" @@ -119,5 +118,3 @@ class SinricProWiFiSettings { */ void deleteAllSettings(); }; - -#endif // SinricProWiFiSettings_H \ No newline at end of file From 6d1a699309a6906e97fac64cd52633a37de35d99 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Mon, 15 Jul 2024 17:19:33 +0700 Subject: [PATCH 22/36] fix: excluding settings due to PIO missing APlistClean in WiFi.h --- .github/workflows/build-esp8266-esp32.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-esp8266-esp32.yml b/.github/workflows/build-esp8266-esp32.yml index b4c71517..3418102e 100644 --- a/.github/workflows/build-esp8266-esp32.yml +++ b/.github/workflows/build-esp8266-esp32.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate, examples/Settings] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate] steps: From c469c89622040b3baeb3d03893f172a692265217 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:14:09 +0700 Subject: [PATCH 23/36] fix: version --- changelog.md | 4 ++-- library.json | 2 +- library.properties | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 0ddc8eb3..97b863ee 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,8 @@ # Changelog -## Version 3.2.1 +## Version 3.2.0 ### New - Support OTA Updates - - Module level command support for Settings + - Module level command support for WiFi settings, ESP Health ## Version 3.1.0 Upgrade: diff --git a/library.json b/library.json index d6440f57..74cbc874 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "maintainer": true } ], - "version": "3.2.1", + "version": "3.2.0", "frameworks": "arduino", "platforms": [ "espressif8266", diff --git a/library.properties b/library.properties index fa0787c3..ce300266 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SinricPro -version=3.2.1 +version=3.2.0 author=Boris Jaeger maintainer=Boris Jaeger sentence=Library for https://sinric.pro - simple way to connect your device to alexa From c9b728e61517be7b4ede2aab5e06cc26254127f5 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:14:29 +0700 Subject: [PATCH 24/36] feat: health --- examples/Health/Health.ino | 98 +++++++++++++++++++ examples/Health/HealthDiagnostics.cpp | 130 ++++++++++++++++++++++++++ examples/Health/HealthDiagnostics.h | 45 +++++++++ src/SinricPro.h | 34 ++++++- src/SinricProModuleCommandHandler.h | 27 +++++- 5 files changed, 329 insertions(+), 5 deletions(-) create mode 100644 examples/Health/Health.ino create mode 100644 examples/Health/HealthDiagnostics.cpp create mode 100644 examples/Health/HealthDiagnostics.h diff --git a/examples/Health/Health.ino b/examples/Health/Health.ino new file mode 100644 index 00000000..a986522c --- /dev/null +++ b/examples/Health/Health.ino @@ -0,0 +1,98 @@ +/* + * Example for how to monitor ESP health. + * + * If you encounter any issues: + * - check the readme.md at https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md + * - ensure all dependent libraries are installed + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#arduinoide + * - see https://github.com/sinricpro/esp8266-esp32-sdk/blob/master/README.md#dependencies + * - open serial monitor and check whats happening + * - check full user documentation at https://sinricpro.github.io/esp8266-esp32-sdk + * - visit https://github.com/sinricpro/esp8266-esp32-sdk/issues and check for existing issues or open a new one + */ + +// Uncomment the following line to enable serial debug output +#define ENABLE_DEBUG + +#ifdef ENABLE_DEBUG + #define DEBUG_ESP_PORT Serial + #define NODEBUG_WEBSOCKETS + #define NDEBUG +#endif + +#include +#include + +#if defined(ESP8266) + #include +#elif defined(ESP32) + #include +#endif + +#include "SinricPro.h" +#include "SinricProSwitch.h" +#include "HealthDiagnostics.h" + +#define WIFI_SSID "June" +#define WIFI_PASS "wifipassword" +#define APP_KEY "6c2eb41b-5f3b-457c-9b71-e227f6c7d7c4" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "62c10d6c-889a-459f-94a3-0e577cef5920-17bbf3e2-1eff-4ad7-b8aa-1348a07977cd" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "668824bc433af2075035036f" // Should look like "5dc1564130xxxxxxxxxxxxxx" + +#define BAUD_RATE 115200 // Change baudrate to your need + +HealthDiagnostics healthDiagnostics; + +bool handleReportHelath(String& healthReport) { + return healthDiagnostics.reportHealth(healthReport); +} + +// setup function for WiFi connection +void setupWiFi() { + Serial.printf("\r\n[Wifi]: Connecting"); + +#if defined(ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); + WiFi.setAutoReconnect(true); +#elif defined(ESP32) + WiFi.setSleep(false); + WiFi.setAutoReconnect(true); +#endif + + WiFi.begin(WIFI_SSID, WIFI_PASS); + + while (WiFi.status() != WL_CONNECTED) { + Serial.printf("."); + delay(250); + } + + Serial.printf("connected!\r\n[WiFi]: IP-Address is %s\r\n", WiFi.localIP().toString().c_str()); +} + +// setup function for SinricPro +void setupSinricPro() { + SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; + + // setup SinricPro + SinricPro.onConnected([]() { + Serial.printf("Connected to SinricPro\r\n"); + }); + + SinricPro.onDisconnected([]() { + Serial.printf("Disconnected from SinricPro\r\n"); + }); + SinricPro.onReportHelath(handleReportHelath); + SinricPro.begin(APP_KEY, APP_SECRET); +} + +// main setup function +void setup() { + Serial.begin(BAUD_RATE); + Serial.printf("\r\n\r\n"); + setupWiFi(); + setupSinricPro(); +} + +void loop() { + SinricPro.handle(); +} diff --git a/examples/Health/HealthDiagnostics.cpp b/examples/Health/HealthDiagnostics.cpp new file mode 100644 index 00000000..0fc4562b --- /dev/null +++ b/examples/Health/HealthDiagnostics.cpp @@ -0,0 +1,130 @@ +#include "HealthDiagnostics.h" + +String HealthDiagnostics::getChipId() { +#ifdef ESP32 + return String((uint32_t)ESP.getEfuseMac(), HEX); +#else + return String(ESP.getChipId(), HEX); +#endif +} + +void HealthDiagnostics::addHeapInfo(JsonObject& doc) { +#ifdef ESP32 + doc["freeHeap"] = ESP.getFreeHeap(); + doc["totalHeap"] = ESP.getHeapSize(); + doc["minFreeHeap"] = ESP.getMinFreeHeap(); + doc["maxAllocHeap"] = ESP.getMaxAllocHeap(); + + multi_heap_info_t heap_info; + heap_caps_get_info(&heap_info, MALLOC_CAP_INTERNAL); + + JsonObject internalHeap = doc["internalHeap"].to(); + internalHeap["totalFreeBytes"] = heap_info.total_free_bytes; + internalHeap["totalAllocatedBytes"] = heap_info.total_allocated_bytes; + internalHeap["largestFreeBlock"] = heap_info.largest_free_block; + internalHeap["minimumFreeBytes"] = heap_info.minimum_free_bytes; + internalHeap["allocatedBlocks"] = heap_info.allocated_blocks; + internalHeap["freeBlocks"] = heap_info.free_blocks; + internalHeap["totalBlocks"] = heap_info.total_blocks; + +#ifdef CONFIG_SPIRAM_SUPPORT + heap_caps_get_info(&heap_info, MALLOC_CAP_SPIRAM); + + JsonObject psram = doc["psram"].to(); + psram["totalFreeBytes"] = heap_info.total_free_bytes; + psram["totalAllocatedBytes"] = heap_info.total_allocated_bytes; + psram["largestFreeBlock"] = heap_info.largest_free_block; + psram["minimumFreeBytes"] = heap_info.minimum_free_bytes; + psram["allocatedBlocks"] = heap_info.allocated_blocks; + psram["freeBlocks"] = heap_info.free_blocks; + psram["totalBlocks"] = heap_info.total_blocks; +#endif +#else + doc["freeHeap"] = ESP.getFreeHeap(); + doc["heapFragmentation"] = ESP.getHeapFragmentation(); + doc["maxFreeBlockSize"] = ESP.getMaxFreeBlockSize(); + + // Get detailed heap information. + JsonObject heapInfo = doc["heapInfo"].to(); + + UMM_HEAP_INFO ummHeapInfo; + umm_info(&ummHeapInfo, 0); + heapInfo["freeBlocks"] = ummHeapInfo.freeBlocks; + heapInfo["usedBlocks"] = ummHeapInfo.usedBlocks; + heapInfo["totalBlocks"] = ummHeapInfo.totalBlocks; + heapInfo["totalEntries"] = ummHeapInfo.totalEntries; + heapInfo["usedEntries"] = ummHeapInfo.usedEntries; + heapInfo["freeEntries"] = ummHeapInfo.freeEntries; + heapInfo["maxFreeContiguousBytes"] = umm_max_block_size(); +#endif +} + +void HealthDiagnostics::addWiFiInfo(JsonObject& doc) { + doc["ssid"] = WiFi.SSID(); + doc["bssid"] = WiFi.BSSIDstr(); + doc["rssi"] = WiFi.RSSI(); + doc["ipAddress"] = WiFi.localIP().toString(); + doc["subnetMask"] = WiFi.subnetMask().toString(); + doc["gateway"] = WiFi.gatewayIP().toString(); + doc["dns"] = WiFi.dnsIP().toString(); + doc["macAddress"] = WiFi.macAddress(); + doc["channel"] = WiFi.channel(); +} + +void HealthDiagnostics::addSketchInfo(JsonObject& doc) { + doc["cpuFreq"] = ESP.getCpuFreqMHz(); + doc["sketchSize"] = ESP.getSketchSize(); + doc["freeSketchSpace"] = ESP.getFreeSketchSpace(); + doc["flashChipSize"] = ESP.getFlashChipSize(); + doc["flashChipSpeed"] = ESP.getFlashChipSpeed(); +} + +String HealthDiagnostics::getResetReasonESP32(esp_reset_reason_t resetReason) { + switch (resetReason) { + case ESP_RST_POWERON: return "Power-on event"; + case ESP_RST_EXT: return "External pin reset"; + case ESP_RST_SW: return "Software reset via esp_restart"; + case ESP_RST_PANIC: return "Software reset due to exception/panic"; + case ESP_RST_INT_WDT: return "Reset (software or hardware) due to interrupt watchdog"; + case ESP_RST_TASK_WDT: return "Reset due to task watchdog"; + case ESP_RST_WDT: return "Reset due to other watchdogs"; + case ESP_RST_DEEPSLEEP: return "Deep sleep reset"; + case ESP_RST_BROWNOUT: return "Brownout reset"; + case ESP_RST_SDIO: return "Reset over SDIO"; + default: return "Unknown reset reason"; + } +} + +void HealthDiagnostics::addResetCause(JsonObject& doc) { + #ifdef ESP32 + doc["reason"] = getResetReasonESP32(esp_reset_reason()); + #else + doc["reason"] = ESP.getResetReason(); + #endif + //doc["crashDetails"] = "" // Use something like https://github.com/krzychb/EspSaveCrash +} + +bool HealthDiagnostics::reportHealth(String& healthReport) { + JsonDocument doc; + doc["chipId"] = getChipId(); + doc["uptime"] = millis() / 1000; // seconds + + // Add detailed heap information. + JsonObject heap = doc["heap"].to(); + addHeapInfo(heap); + + // Detailed Sketch information. + JsonObject sketch = doc["sketch"].to(); + addSketchInfo(sketch); + + // Detailed WiFi information. + JsonObject wifi = doc["wifi"].to(); + addWiFiInfo(wifi); + + // Add last reset reson + JsonObject resetInfo = doc["reset"].to(); + addResetCause(resetInfo); + + serializeJson(doc, healthReport); + return true; +} \ No newline at end of file diff --git a/examples/Health/HealthDiagnostics.h b/examples/Health/HealthDiagnostics.h new file mode 100644 index 00000000..76969cb9 --- /dev/null +++ b/examples/Health/HealthDiagnostics.h @@ -0,0 +1,45 @@ +#ifndef HEALTH_DIAGNOSTICS_H +#define HEALTH_DIAGNOSTICS_H + +#include +#include +#ifdef ESP32 + #include + #include +#else + extern "C" { + #include "umm_malloc/umm_heap_select.h" + #include "umm_malloc/umm_malloc.h" + } +#endif +#if defined(ESP8266) + #include + #include +#elif defined(ESP32) + #include + #include "esp_system.h" +#endif + +/** + * @brief Class to handle health diagnostics + */ +class HealthDiagnostics { +public: + /** + * @brief Report the health diagnostic information. + * + * @param healthReport A reference to a String to store the health report in JSON format. + * @return True on success, otherwise false. + */ + bool reportHealth(String& healthReport); + +private: + String getChipId(); + void addHeapInfo(JsonObject& doc); + void addWiFiInfo(JsonObject& doc); + void addSketchInfo(JsonObject& doc); + void addResetCause(JsonObject& doc); + String getResetReasonESP32(esp_reset_reason_t resetReason); +}; + +#endif // HEALTH_DIAGNOSTICS_H \ No newline at end of file diff --git a/src/SinricPro.h b/src/SinricPro.h index 487caae6..6b8db5fc 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -64,6 +64,21 @@ using OTAUpdateCallbackHandler = std::function; +/** + * @typedef ReportHealthCallbackHandler + * @brief Defines a function type for reporting device health status. + * + * This typedef creates an alias for a std::function that takes a reference to a String + * and returns a boolean value. It's designed to be used as a callback for health reporting + * + * @param healthReport A reference to a String that will contain the health status information. + * The callback function should populate this string with relevant health data. + * + * @return bool Returns true if the health report was successfully handled + * false otherwise. + */ +using ReportHealthCallbackHandler = std::function; + using PongCallback = std::function; /** @@ -91,6 +106,7 @@ class SinricProClass : public SinricProInterface { Proxy operator[](const String deviceId); void onOTAUpdate(OTAUpdateCallbackHandler cb); void onSetSetting(SetSettingCallbackHandler cb); + void onReportHelath(ReportHealthCallbackHandler cb); protected: template @@ -506,7 +522,7 @@ void SinricProClass::onOTAUpdate(OTAUpdateCallbackHandler cb) { * * This method registers a callback function that will be called when a request to change * a module setting is received. - * + * @return void * @param cb A function pointer or lambda of type SetSettingCallbackHandler. * The callback should return a boolean indicating whether the setting was successfully updated. * @@ -515,6 +531,22 @@ void SinricProClass::onSetSetting(SetSettingCallbackHandler cb) { _moduleCommandHandler.onSetSetting(cb); } +/** + * @brief Sets the callback function for reporting device health status. + * + * This method allows the user to set a custom callback function that will be called + * when the SinricPro system needs to report the device's health status. + * + * @param cb A function pointer of type ReportHealthCallbackHandler. + * This callback should populate a String with health information and return a boolean + * indicating success or failure of the health reporting process. + * @return void + * @see ReportHealthCallbackHandler for the definition of the callback function type. + */ +void SinricProClass::onReportHelath(ReportHealthCallbackHandler cb) { + _moduleCommandHandler.onReportHelath(cb); +} + /** * @brief Set callback function for websocket connected event * diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index 3600f8d0..81be95f5 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -22,9 +22,12 @@ FSTR(OTA, forceUpdate); // "forceUpdate" FSTR(SETTINGS, setSetting); // "setSetting" FSTR(SETTINGS, id); // "id" FSTR(SETTINGS, value); // "value" +FSTR(INSIGHTS, health); // "health" +FSTR(INSIGHTS, report); // "report" using OTAUpdateCallbackHandler = std::function; using SetSettingCallbackHandler = std::function; +using ReportHealthCallbackHandler = std::function; class SinricProModuleCommandHandler { public: @@ -34,15 +37,18 @@ class SinricProModuleCommandHandler { bool handleRequest(SinricProRequest &request); void onOTAUpdate(OTAUpdateCallbackHandler callback); void onSetSetting(SetSettingCallbackHandler callback); + void onReportHelath(ReportHealthCallbackHandler callback); private: - OTAUpdateCallbackHandler _otaUpdateCallbackHandler; + OTAUpdateCallbackHandler _otaUpdateCallbackHandler; SetSettingCallbackHandler _setSettingCallbackHandler; + ReportHealthCallbackHandler _reportHealthCallbackHandler; }; SinricProModuleCommandHandler::SinricProModuleCommandHandler() : _otaUpdateCallbackHandler(nullptr), - _setSettingCallbackHandler(nullptr) {} + _setSettingCallbackHandler(nullptr), + _reportHealthCallbackHandler(nullptr) {} SinricProModuleCommandHandler::~SinricProModuleCommandHandler() {} @@ -53,7 +59,11 @@ void SinricProModuleCommandHandler::onOTAUpdate(OTAUpdateCallbackHandler callbac void SinricProModuleCommandHandler::onSetSetting(SetSettingCallbackHandler callback) { _setSettingCallbackHandler = callback; } - + +void SinricProModuleCommandHandler::onReportHelath(ReportHealthCallbackHandler callback) { + _reportHealthCallbackHandler = callback; +} + bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { if (strcmp(FSTR_OTA_otaUpdateAvailable, request.action.c_str()) == 0 && _otaUpdateCallbackHandler) { String url = request.request_value[FSTR_OTA_url]; @@ -67,7 +77,16 @@ bool SinricProModuleCommandHandler::handleRequest(SinricProRequest &request) { String id = request.request_value[FSTR_SETTINGS_id]; String value = request.request_value[FSTR_SETTINGS_value]; return _setSettingCallbackHandler(id, value); - } else { + } + else if (strcmp(FSTR_INSIGHTS_health, request.action.c_str()) == 0 && _reportHealthCallbackHandler) { + String healthReport = ""; + bool success = _reportHealthCallbackHandler(healthReport); + if (success) { + request.response_value[FSTR_INSIGHTS_report] = healthReport; + } + return success; + } + else { DEBUG_SINRIC("[SinricProModuleCommandHandler:handleRequest]: action: %s not supported!\r\n", request.action.c_str()); } return false; From 5127881bed4588c383727cca66a2b670e2bb5c21 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:16:05 +0700 Subject: [PATCH 25/36] fix: ids --- examples/Health/Health.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/Health/Health.ino b/examples/Health/Health.ino index a986522c..e8d7c6c8 100644 --- a/examples/Health/Health.ino +++ b/examples/Health/Health.ino @@ -33,11 +33,11 @@ #include "SinricProSwitch.h" #include "HealthDiagnostics.h" -#define WIFI_SSID "June" -#define WIFI_PASS "wifipassword" -#define APP_KEY "6c2eb41b-5f3b-457c-9b71-e227f6c7d7c4" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" -#define APP_SECRET "62c10d6c-889a-459f-94a3-0e577cef5920-17bbf3e2-1eff-4ad7-b8aa-1348a07977cd" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" -#define SWITCH_ID "668824bc433af2075035036f" // Should look like "5dc1564130xxxxxxxxxxxxxx" +#define WIFI_SSID "YOUR-WIFI-SSID" +#define WIFI_PASS "YOUR-WIFI-PASSWORD" +#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" #define BAUD_RATE 115200 // Change baudrate to your need From 6c589831b2c86524662c5b275d0f79ff3c433363 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:16:24 +0700 Subject: [PATCH 26/36] fix: add health --- .github/workflows/build-esp8266-esp32.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-esp8266-esp32.yml b/.github/workflows/build-esp8266-esp32.yml index 3418102e..59aa68dc 100644 --- a/.github/workflows/build-esp8266-esp32.yml +++ b/.github/workflows/build-esp8266-esp32.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/OTAUpdate, examples/Health] steps: From e0aee69e991a78bba6c96aca99f1f70c62ec9448 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:45:10 +0700 Subject: [PATCH 27/36] fix: support picow --- examples/Health/Health.ino | 32 +++++----- examples/Health/HealthDiagnostics.cpp | 84 +++++++++++++++------------ examples/Health/HealthDiagnostics.h | 33 +++++------ 3 files changed, 78 insertions(+), 71 deletions(-) diff --git a/examples/Health/Health.ino b/examples/Health/Health.ino index e8d7c6c8..b184d1f7 100644 --- a/examples/Health/Health.ino +++ b/examples/Health/Health.ino @@ -15,35 +15,35 @@ #define ENABLE_DEBUG #ifdef ENABLE_DEBUG - #define DEBUG_ESP_PORT Serial - #define NODEBUG_WEBSOCKETS - #define NDEBUG +#define DEBUG_ESP_PORT Serial +#define NODEBUG_WEBSOCKETS +#define NDEBUG #endif #include #include #if defined(ESP8266) - #include -#elif defined(ESP32) - #include +#include +#elif defined(ESP32) +#include #endif #include "SinricPro.h" #include "SinricProSwitch.h" #include "HealthDiagnostics.h" -#define WIFI_SSID "YOUR-WIFI-SSID" -#define WIFI_PASS "YOUR-WIFI-PASSWORD" -#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" -#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" -#define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" +#define WIFI_SSID "YOUR-WIFI-SSID" +#define WIFI_PASS "YOUR-WIFI-PASSWORD" +#define APP_KEY "YOUR-APP-KEY" // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx" +#define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" +#define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" + +#define BAUD_RATE 115200 // Change baudrate to your need -#define BAUD_RATE 115200 // Change baudrate to your need - HealthDiagnostics healthDiagnostics; -bool handleReportHelath(String& healthReport) { +bool handleReportHelath(String& healthReport) { return healthDiagnostics.reportHealth(healthReport); } @@ -71,13 +71,13 @@ void setupWiFi() { // setup function for SinricPro void setupSinricPro() { - SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; + SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; // setup SinricPro SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); }); - + SinricPro.onDisconnected([]() { Serial.printf("Disconnected from SinricPro\r\n"); }); diff --git a/examples/Health/HealthDiagnostics.cpp b/examples/Health/HealthDiagnostics.cpp index 0fc4562b..4c3dda7c 100644 --- a/examples/Health/HealthDiagnostics.cpp +++ b/examples/Health/HealthDiagnostics.cpp @@ -1,15 +1,17 @@ #include "HealthDiagnostics.h" String HealthDiagnostics::getChipId() { -#ifdef ESP32 +#if defined(ESP32) return String((uint32_t)ESP.getEfuseMac(), HEX); -#else +#elif defined(ESP8266) return String(ESP.getChipId(), HEX); +#elif defined(ARDUINO_ARCH_RP2040) + return String(rp2040.getChipID(), HEX); #endif } void HealthDiagnostics::addHeapInfo(JsonObject& doc) { -#ifdef ESP32 +#if defined(ESP32) doc["freeHeap"] = ESP.getFreeHeap(); doc["totalHeap"] = ESP.getHeapSize(); doc["minFreeHeap"] = ESP.getMinFreeHeap(); @@ -27,26 +29,25 @@ void HealthDiagnostics::addHeapInfo(JsonObject& doc) { internalHeap["freeBlocks"] = heap_info.free_blocks; internalHeap["totalBlocks"] = heap_info.total_blocks; -#ifdef CONFIG_SPIRAM_SUPPORT - heap_caps_get_info(&heap_info, MALLOC_CAP_SPIRAM); - - JsonObject psram = doc["psram"].to(); - psram["totalFreeBytes"] = heap_info.total_free_bytes; - psram["totalAllocatedBytes"] = heap_info.total_allocated_bytes; - psram["largestFreeBlock"] = heap_info.largest_free_block; - psram["minimumFreeBytes"] = heap_info.minimum_free_bytes; - psram["allocatedBlocks"] = heap_info.allocated_blocks; - psram["freeBlocks"] = heap_info.free_blocks; - psram["totalBlocks"] = heap_info.total_blocks; -#endif -#else + #ifdef CONFIG_SPIRAM_SUPPORT + heap_caps_get_info(&heap_info, MALLOC_CAP_SPIRAM); + + JsonObject psram = doc["psram"].to(); + psram["totalFreeBytes"] = heap_info.total_free_bytes; + psram["totalAllocatedBytes"] = heap_info.total_allocated_bytes; + psram["largestFreeBlock"] = heap_info.largest_free_block; + psram["minimumFreeBytes"] = heap_info.minimum_free_bytes; + psram["allocatedBlocks"] = heap_info.allocated_blocks; + psram["freeBlocks"] = heap_info.free_blocks; + psram["totalBlocks"] = heap_info.total_blocks; + #endif +#elif defined(ESP8266) doc["freeHeap"] = ESP.getFreeHeap(); doc["heapFragmentation"] = ESP.getHeapFragmentation(); doc["maxFreeBlockSize"] = ESP.getMaxFreeBlockSize(); // Get detailed heap information. JsonObject heapInfo = doc["heapInfo"].to(); - UMM_HEAP_INFO ummHeapInfo; umm_info(&ummHeapInfo, 0); heapInfo["freeBlocks"] = ummHeapInfo.freeBlocks; @@ -56,12 +57,20 @@ void HealthDiagnostics::addHeapInfo(JsonObject& doc) { heapInfo["usedEntries"] = ummHeapInfo.usedEntries; heapInfo["freeEntries"] = ummHeapInfo.freeEntries; heapInfo["maxFreeContiguousBytes"] = umm_max_block_size(); + +#elif defined(ARDUINO_ARCH_RP2040) + doc["freeHeap"] = rp2040.getFreeHeap(); + doc["totalHeap"] = rp2040.getTotalHeap(); #endif } void HealthDiagnostics::addWiFiInfo(JsonObject& doc) { doc["ssid"] = WiFi.SSID(); + +#if defined(ESP32) || defined(ESP8266) doc["bssid"] = WiFi.BSSIDstr(); +#endif + doc["rssi"] = WiFi.RSSI(); doc["ipAddress"] = WiFi.localIP().toString(); doc["subnetMask"] = WiFi.subnetMask().toString(); @@ -72,42 +81,41 @@ void HealthDiagnostics::addWiFiInfo(JsonObject& doc) { } void HealthDiagnostics::addSketchInfo(JsonObject& doc) { +#if defined(ESP32) || defined(ESP8266) doc["cpuFreq"] = ESP.getCpuFreqMHz(); doc["sketchSize"] = ESP.getSketchSize(); doc["freeSketchSpace"] = ESP.getFreeSketchSpace(); doc["flashChipSize"] = ESP.getFlashChipSize(); doc["flashChipSpeed"] = ESP.getFlashChipSpeed(); +#endif } -String HealthDiagnostics::getResetReasonESP32(esp_reset_reason_t resetReason) { - switch (resetReason) { - case ESP_RST_POWERON: return "Power-on event"; - case ESP_RST_EXT: return "External pin reset"; - case ESP_RST_SW: return "Software reset via esp_restart"; - case ESP_RST_PANIC: return "Software reset due to exception/panic"; - case ESP_RST_INT_WDT: return "Reset (software or hardware) due to interrupt watchdog"; - case ESP_RST_TASK_WDT: return "Reset due to task watchdog"; - case ESP_RST_WDT: return "Reset due to other watchdogs"; - case ESP_RST_DEEPSLEEP: return "Deep sleep reset"; - case ESP_RST_BROWNOUT: return "Brownout reset"; - case ESP_RST_SDIO: return "Reset over SDIO"; - default: return "Unknown reset reason"; +void HealthDiagnostics::addResetCause(JsonObject& doc) { +#if defined(ESP32) + switch (esp_reset_reason()) { + case ESP_RST_POWERON: doc["reason"] = "Power-on event"; break; + case ESP_RST_EXT: doc["reason"] = "External pin reset"; break; + case ESP_RST_SW: doc["reason"] = "Software reset via esp_restart"; break; + case ESP_RST_PANIC: doc["reason"] = "Software reset due to exception/panic"; break; + case ESP_RST_INT_WDT: doc["reason"] = "Reset (software or hardware) due to interrupt watchdog"; break; + case ESP_RST_TASK_WDT: doc["reason"] = "Reset due to task watchdog"; break; + case ESP_RST_WDT: doc["reason"] = "Reset due to other watchdogs"; break; + case ESP_RST_DEEPSLEEP: doc["reason"] = "Deep sleep reset"; break; + case ESP_RST_BROWNOUT: doc["reason"] = "Brownout reset"; break; + case ESP_RST_SDIO: doc["reason"] = "Reset over SDIO"; break; + default: doc["reason"] = "Unknown reset reason"; break; } -} +#elif defined(ESP8266) + doc["reason"] = ESP.getResetReason(); +#endif -void HealthDiagnostics::addResetCause(JsonObject& doc) { - #ifdef ESP32 - doc["reason"] = getResetReasonESP32(esp_reset_reason()); - #else - doc["reason"] = ESP.getResetReason(); - #endif //doc["crashDetails"] = "" // Use something like https://github.com/krzychb/EspSaveCrash } bool HealthDiagnostics::reportHealth(String& healthReport) { JsonDocument doc; doc["chipId"] = getChipId(); - doc["uptime"] = millis() / 1000; // seconds + doc["uptime"] = millis() / 1000; // seconds // Add detailed heap information. JsonObject heap = doc["heap"].to(); diff --git a/examples/Health/HealthDiagnostics.h b/examples/Health/HealthDiagnostics.h index 76969cb9..f64524b5 100644 --- a/examples/Health/HealthDiagnostics.h +++ b/examples/Health/HealthDiagnostics.h @@ -3,21 +3,21 @@ #include #include -#ifdef ESP32 - #include - #include -#else - extern "C" { - #include "umm_malloc/umm_heap_select.h" - #include "umm_malloc/umm_malloc.h" - } -#endif -#if defined(ESP8266) - #include - #include -#elif defined(ESP32) - #include - #include "esp_system.h" + +#if defined(ESP32) +#include +#include "esp_system.h" +#include +#include +#elif defined(ESP8266) +#include +#include +extern "C" { +#include "umm_malloc/umm_heap_select.h" +#include "umm_malloc/umm_malloc.h" +} +#elif defined(ARDUINO_ARCH_RP2040) +#include #endif /** @@ -33,13 +33,12 @@ class HealthDiagnostics { */ bool reportHealth(String& healthReport); -private: +private: String getChipId(); void addHeapInfo(JsonObject& doc); void addWiFiInfo(JsonObject& doc); void addSketchInfo(JsonObject& doc); void addResetCause(JsonObject& doc); - String getResetReasonESP32(esp_reset_reason_t resetReason); }; #endif // HEALTH_DIAGNOSTICS_H \ No newline at end of file From 14ffbbe100561d7bafc3dc5d2e31f9967859e4cc Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:45:27 +0700 Subject: [PATCH 28/36] fix: adding examples/Health --- .github/workflows/build-rpipicow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-rpipicow.yml b/.github/workflows/build-rpipicow.yml index e02d2a4b..db03a8db 100644 --- a/.github/workflows/build-rpipicow.yml +++ b/.github/workflows/build-rpipicow.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV] + example: [examples/ACUnit, examples/AirQualitySensor/AirQualitySensor, examples/Blinds, examples/ContactSensor, examples/DimSwitch, examples/doorbell, examples/Fan, examples/GarageDoor, examples/Light/Light, examples/Lock/Lock, examples/Lock/Lock_with_feedback, examples/MotionSensor, examples/PowerSensor, examples/Relay/MultiRelays_advance, examples/Relay/Relay, examples/Speaker, examples/Switch/MultiSwitch_advance, examples/Switch/MultiSwitch_beginner, examples/Switch/MultiSwitch_intermediate, examples/Switch/Switch, examples/Thermostat, examples/TV, examples/Health] steps: - name: Step 1 - Checkout Repo From bad84a2435ea131ccab236ab366b49c1a6670510 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 16:59:42 +0700 Subject: [PATCH 29/36] chore: update debug message. --- src/SinricPro.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SinricPro.h b/src/SinricPro.h index 6b8db5fc..72ccb3f5 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -194,7 +194,7 @@ DeviceType& SinricProClass::getDeviceInstance(String deviceId) { DeviceType* tmp_device = (DeviceType*)getDevice(deviceId); if (tmp_device) return *tmp_device; - DEBUG_SINRIC("[SinricPro]: Device \"%s\" does not exist. Creating new device\r\n", deviceId.c_str()); + DEBUG_SINRIC("[SinricPro]: Device \"%s\" does not exist in the internal device list. creating new device\r\n", deviceId.c_str()); DeviceType& tmp_deviceInstance = add(deviceId); if (isConnected()) { From 7ee99601b8f1795be1e69329c5ad630b7cb5ae63 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Wed, 17 Jul 2024 17:04:02 +0700 Subject: [PATCH 30/36] fix: typo --- examples/Health/Health.ino | 32 ++++++++++++++--------------- src/SinricPro.h | 6 +++--- src/SinricProModuleCommandHandler.h | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/Health/Health.ino b/examples/Health/Health.ino index b184d1f7..0dd9ac37 100644 --- a/examples/Health/Health.ino +++ b/examples/Health/Health.ino @@ -15,18 +15,18 @@ #define ENABLE_DEBUG #ifdef ENABLE_DEBUG -#define DEBUG_ESP_PORT Serial -#define NODEBUG_WEBSOCKETS -#define NDEBUG + #define DEBUG_ESP_PORT Serial + #define NODEBUG_WEBSOCKETS + #define NDEBUG #endif #include #include #if defined(ESP8266) -#include -#elif defined(ESP32) -#include + #include +#elif defined(ESP32) + #include #endif #include "SinricPro.h" @@ -39,14 +39,10 @@ #define APP_SECRET "YOUR-APP-SECRET" // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx" #define SWITCH_ID "YOUR-DEVICE-ID" // Should look like "5dc1564130xxxxxxxxxxxxxx" -#define BAUD_RATE 115200 // Change baudrate to your need - +#define BAUD_RATE 115200 // Change baudrate to your need + HealthDiagnostics healthDiagnostics; - -bool handleReportHelath(String& healthReport) { - return healthDiagnostics.reportHealth(healthReport); -} - + // setup function for WiFi connection void setupWiFi() { Serial.printf("\r\n[Wifi]: Connecting"); @@ -71,17 +67,21 @@ void setupWiFi() { // setup function for SinricPro void setupSinricPro() { - SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; + SinricProSwitch& mySwitch = SinricPro[SWITCH_ID]; // setup SinricPro SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); }); - + SinricPro.onDisconnected([]() { Serial.printf("Disconnected from SinricPro\r\n"); }); - SinricPro.onReportHelath(handleReportHelath); + + SinricPro.onReportHealth([&](String &healthReport) { + return healthDiagnostics.reportHealth(healthReport); + }); + SinricPro.begin(APP_KEY, APP_SECRET); } diff --git a/src/SinricPro.h b/src/SinricPro.h index 72ccb3f5..567da203 100644 --- a/src/SinricPro.h +++ b/src/SinricPro.h @@ -106,7 +106,7 @@ class SinricProClass : public SinricProInterface { Proxy operator[](const String deviceId); void onOTAUpdate(OTAUpdateCallbackHandler cb); void onSetSetting(SetSettingCallbackHandler cb); - void onReportHelath(ReportHealthCallbackHandler cb); + void onReportHealth(ReportHealthCallbackHandler cb); protected: template @@ -543,8 +543,8 @@ void SinricProClass::onSetSetting(SetSettingCallbackHandler cb) { * @return void * @see ReportHealthCallbackHandler for the definition of the callback function type. */ -void SinricProClass::onReportHelath(ReportHealthCallbackHandler cb) { - _moduleCommandHandler.onReportHelath(cb); +void SinricProClass::onReportHealth(ReportHealthCallbackHandler cb) { + _moduleCommandHandler.onReportHealth(cb); } /** diff --git a/src/SinricProModuleCommandHandler.h b/src/SinricProModuleCommandHandler.h index 81be95f5..fd0d3479 100644 --- a/src/SinricProModuleCommandHandler.h +++ b/src/SinricProModuleCommandHandler.h @@ -37,7 +37,7 @@ class SinricProModuleCommandHandler { bool handleRequest(SinricProRequest &request); void onOTAUpdate(OTAUpdateCallbackHandler callback); void onSetSetting(SetSettingCallbackHandler callback); - void onReportHelath(ReportHealthCallbackHandler callback); + void onReportHealth(ReportHealthCallbackHandler callback); private: OTAUpdateCallbackHandler _otaUpdateCallbackHandler; @@ -60,7 +60,7 @@ void SinricProModuleCommandHandler::onSetSetting(SetSettingCallbackHandler callb _setSettingCallbackHandler = callback; } -void SinricProModuleCommandHandler::onReportHelath(ReportHealthCallbackHandler callback) { +void SinricProModuleCommandHandler::onReportHealth(ReportHealthCallbackHandler callback) { _reportHealthCallbackHandler = callback; } From 99dfcee5c4a37e0b024fe3ecfa652c02fb8f96ac Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sat, 20 Jul 2024 23:36:43 +0700 Subject: [PATCH 31/36] fix: remove MALLOC_CAP_SPIRAM --- examples/Health/HealthDiagnostics.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/Health/HealthDiagnostics.cpp b/examples/Health/HealthDiagnostics.cpp index 4c3dda7c..c8ac06c3 100644 --- a/examples/Health/HealthDiagnostics.cpp +++ b/examples/Health/HealthDiagnostics.cpp @@ -29,18 +29,17 @@ void HealthDiagnostics::addHeapInfo(JsonObject& doc) { internalHeap["freeBlocks"] = heap_info.free_blocks; internalHeap["totalBlocks"] = heap_info.total_blocks; - #ifdef CONFIG_SPIRAM_SUPPORT - heap_caps_get_info(&heap_info, MALLOC_CAP_SPIRAM); - - JsonObject psram = doc["psram"].to(); - psram["totalFreeBytes"] = heap_info.total_free_bytes; - psram["totalAllocatedBytes"] = heap_info.total_allocated_bytes; - psram["largestFreeBlock"] = heap_info.largest_free_block; - psram["minimumFreeBytes"] = heap_info.minimum_free_bytes; - psram["allocatedBlocks"] = heap_info.allocated_blocks; - psram["freeBlocks"] = heap_info.free_blocks; - psram["totalBlocks"] = heap_info.total_blocks; - #endif + heap_caps_get_info(&heap_info, MALLOC_CAP_SPIRAM); + + JsonObject psram = doc["psram"].to(); + psram["totalFreeBytes"] = heap_info.total_free_bytes; + psram["totalAllocatedBytes"] = heap_info.total_allocated_bytes; + psram["largestFreeBlock"] = heap_info.largest_free_block; + psram["minimumFreeBytes"] = heap_info.minimum_free_bytes; + psram["allocatedBlocks"] = heap_info.allocated_blocks; + psram["freeBlocks"] = heap_info.free_blocks; + psram["totalBlocks"] = heap_info.total_blocks; + #elif defined(ESP8266) doc["freeHeap"] = ESP.getFreeHeap(); doc["heapFragmentation"] = ESP.getHeapFragmentation(); From 31db09ec83d9bd1002e97ea1726b65b188892eb5 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sat, 20 Jul 2024 23:59:28 +0700 Subject: [PATCH 32/36] fix: split Cert to .cpp and .h --- examples/OTAUpdate/Cert.cpp | 33 +++++++++++++++++++++++++++++++++ examples/OTAUpdate/Cert.h | 32 +------------------------------- 2 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 examples/OTAUpdate/Cert.cpp diff --git a/examples/OTAUpdate/Cert.cpp b/examples/OTAUpdate/Cert.cpp new file mode 100644 index 00000000..58084f91 --- /dev/null +++ b/examples/OTAUpdate/Cert.cpp @@ -0,0 +1,33 @@ +#include "Cert.h" + +// Ref: https://projects.petrucci.ch/esp32/?page=ssl.php&url=otaupdates.sinric.pro + + +const char* rootCACertificate = R"EOF( +-----BEGIN CERTIFICATE----- +MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw +WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK +a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO +VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw +gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD +ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw +i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB +AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g +BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu +Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C +2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+ +bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG +6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV +XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO +koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq +cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI +E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e +K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX +GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL +sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd +VQD9F6Na/+zmXCc= +-----END CERTIFICATE----- +)EOF"; \ No newline at end of file diff --git a/examples/OTAUpdate/Cert.h b/examples/OTAUpdate/Cert.h index 65bcd526..56b76eb9 100644 --- a/examples/OTAUpdate/Cert.h +++ b/examples/OTAUpdate/Cert.h @@ -1,33 +1,3 @@ #pragma once -// Ref: https://projects.petrucci.ch/esp32/?page=ssl.php&url=otaupdates.sinric.pro - - -const char rootCACertificate[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIEVzCCAj+gAwIBAgIRAIOPbGPOsTmMYgZigxXJ/d4wDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw -WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg -RW5jcnlwdDELMAkGA1UEAxMCRTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNCzqK -a2GOtu/cX1jnxkJFVKtj9mZhSAouWXW0gQI3ULc/FnncmOyhKJdyIBwsz9V8UiBO -VHhbhBRrwJCuhezAUUE8Wod/Bk3U/mDR+mwt4X2VEIiiCFQPmRpM5uoKrNijgfgw -gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD -ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfK1/PPCFPnQS37SssxMZw -i9LXDTAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB -AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g -BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu -Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAH3KdNEVCQdqk0LKyuNImTKdRJY1C -2uw2SJajuhqkyGPY8C+zzsufZ+mgnhnq1A2KVQOSykOEnUbx1cy637rBAihx97r+ -bcwbZM6sTDIaEriR/PLk6LKs9Be0uoVxgOKDcpG9svD33J+G9Lcfv1K9luDmSTgG -6XNFIN5vfI5gs/lMPyojEMdIzK9blcl2/1vKxO8WGCcjvsQ1nJ/Pwt8LQZBfOFyV -XP8ubAp/au3dc4EKWG9MO5zcx1qT9+NXRGdVWxGvmBFRAajciMfXME1ZuGmk3/GO -koAM7ZkjZmleyokP1LGzmfJcUd9s7eeu1/9/eg5XlXd/55GtYjAM+C4DG5i7eaNq -cm2F+yxYIPt6cbbtYVNJCGfHWqHEQ4FYStUyFnv8sjyqU8ypgZaNJ9aVcWSICLOI -E1/Qv/7oKsnZCWJ926wU6RqG1OYPGOi1zuABhLw61cuPVDT28nQS/e6z95cJXq0e -K1BcaJ6fJZsmbjRgD5p3mvEf5vdQM7MCEvU0tHbsx2I5mHHJoABHb8KVBgWp/lcX -GWiWaeOyB7RP+OfDtvi2OsapxXiV7vNVs7fMlrRjY1joKaqmmycnBvAq14AEbtyL -sVfOS66B8apkeFX2NY4XPEYV4ZSCe8VHPrdrERk2wILG3T/EGmSIkCYVUMSnjmJd -VQD9F6Na/+zmXCc= ------END CERTIFICATE----- -)EOF"; \ No newline at end of file +extern const char* rootCACertificate; \ No newline at end of file From e7fd5d4820fe11c3bd57e158cd70438e4cc1f0d3 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 21 Jul 2024 00:00:12 +0700 Subject: [PATCH 33/36] fix: Update version comparison and OTA update response --- examples/OTAUpdate/ESP32OTAHelper.h | 104 +++++++------------------- examples/OTAUpdate/ESP8266OTAHelper.h | 23 +++--- examples/OTAUpdate/OTAUpdate.ino | 22 +++--- examples/OTAUpdate/SemVer.h | 65 ++++++++++------ 4 files changed, 94 insertions(+), 120 deletions(-) diff --git a/examples/OTAUpdate/ESP32OTAHelper.h b/examples/OTAUpdate/ESP32OTAHelper.h index 8d8e91ec..43f59b4d 100644 --- a/examples/OTAUpdate/ESP32OTAHelper.h +++ b/examples/OTAUpdate/ESP32OTAHelper.h @@ -6,91 +6,43 @@ #include #include "Cert.h" -OTAResult startOTAUpdate(const String &url) { - OTAResult result = { false, "" }; - WiFiClientSecure *client = new WiFiClientSecure; +String startOtaUpdate(const String &url) { + WiFiClientSecure client; + client.setCACert(rootCACertificate); - if (client) { - client->setCACert(rootCACertificate); - { - // Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is - HTTPClient https; + HTTPClient https; + Serial.print("[startOtaUpdate()] begin...\n"); + if (!https.begin(client, url)) return "Unable to connect"; - Serial.print("[startOTAUpdate()] begin...\n"); - if (https.begin(*client, url)) { - Serial.print("[startOTAUpdate()] GET...\n"); - // start connection and send HTTP header - int httpCode = https.GET(); + Serial.print("[startOtaUpdate()] GET...\n"); + // start connection and send HTTP header + int httpCode = https.GET(); + if (httpCode < 0) return "GET... failed, error: " + https.errorToString(httpCode); + if (httpCode != HTTP_CODE_OK || httpCode != HTTP_CODE_MOVED_PERMANENTLY) return "HTTP response code: " + String(httpCode); - // httpCode will be negative on error - if (httpCode > 0) { - // HTTP header has been sent and Server response header has been handled - Serial.printf("[startOTAUpdate()] GET... code: %d\n", httpCode); + int contentLength = https.getSize(); + Serial.printf("Content-Length: %d\n", contentLength); - // file found at server - if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { - // get length of document (is -1 when Server sends no Content-Length header) - int contentLength = https.getSize(); - Serial.printf("Content-Length: %d\n", contentLength); + if (contentLength == 0) return "There was no content length in the response"; - if (contentLength > 0) { - bool canBegin = Update.begin(contentLength); - if (canBegin) { - WiFiClient *stream = https.getStreamPtr(); - size_t written = Update.writeStream(*stream); + bool canBegin = Update.begin(contentLength); - if (written == contentLength) { - Serial.println("[startOTAUpdate()] Written : " + String(written) + " successfully"); - } else { - result.errorMessage = "Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"; - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } + if (!canBegin) return "Not enough space to begin OTA"; - if (Update.end()) { - Serial.println("[startOTAUpdate()] OTA done!"); - if (Update.isFinished()) { - Serial.println("[startOTAUpdate()] Update successfully completed. Rebooting."); - ESP.restart(); - result.success = true; - return result; - } else { - result.errorMessage = "Update not finished? Something went wrong!"; - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } - } else { - result.errorMessage = "Error Occurred. Error #: " + String(Update.getError()); - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } - } else { - result.errorMessage = "Not enough space to begin OTA"; - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } - } else { - result.errorMessage = "There was no content length in the response"; - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } - } else { - result.errorMessage = "HTTP response code: " + String(httpCode); - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } - } else { - result.errorMessage = "GET... failed, error: " + https.errorToString(httpCode); - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } + WiFiClient *stream = https.getStreamPtr(); + size_t written = Update.writeStream(*stream); - https.end(); - } else { - result.errorMessage = "Unable to connect"; - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } - } - delete client; - } else { - result.errorMessage = "Unable to create client"; - Serial.println("[startOTAUpdate()] " + result.errorMessage); - } + if (written != contentLength) return "Written only : " + String(written) + "/" + String(contentLength) + ". Retry?"; + Serial.println("[startOtaUpdate()] Written : " + String(written) + " successfully"); + + if (!Update.end()) return "Error Occurred. Error #: " + String(Update.getError()); + Serial.println("[startOtaUpdate()] OTA done!"); - return result; + if (!Update.isFinished()) return "Update not finished? Something went wrong!"; + Serial.println("[startOtaUpdate()] Update successfully completed. Rebooting."); + ESP.restart(); + + return ""; } #endif \ No newline at end of file diff --git a/examples/OTAUpdate/ESP8266OTAHelper.h b/examples/OTAUpdate/ESP8266OTAHelper.h index f2e70b95..0cb223e8 100644 --- a/examples/OTAUpdate/ESP8266OTAHelper.h +++ b/examples/OTAUpdate/ESP8266OTAHelper.h @@ -34,9 +34,7 @@ String extractOTAHostname(const String& url) { } // Function to perform the OTA update -OTAResult startOTAUpdate(const String& url) { - OTAResult result = { false, "" }; - +String startOtaUpdate(const String& url) { #if defined(ARDUINO_ARCH_RP2040) WiFiClientSecure client; client.setBufferSizes(4096, 4096); // For OTA to work on limited RAM @@ -45,7 +43,7 @@ OTAResult startOTAUpdate(const String& url) { // Use MFLN to reduce the memory usage String host = extractOTAHostname(url); bool mfln = client.probeMaxFragmentLength(host, 443, 512); - Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); + Serial.printf("[startOtaUpdate()] MFLN supported: %s\n", mfln ? "yes" : "no"); if (mfln) { client.setBufferSizes(512, 512); } else client.setBufferSizes(4096, 4096); #endif @@ -59,30 +57,29 @@ OTAResult startOTAUpdate(const String& url) { // value is used to put the LED on. If the LED is on with HIGH, that value should be passed //ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); - Serial.printf("Starting the OTA update. This may take few mins to complete!\n"); + Serial.printf("[startOtaUpdate()] Starting the OTA update. This may take few mins to complete!\n"); auto http_ret = OTA_CLASS.update(client, url); //if success reboot will reboot! + String errorMsg = ""; switch (http_ret) { case HTTP_UPDATE_OK: - result.success = true; - Serial.printf("HTTP_UPDATE_OK\n"); + Serial.printf("[startOtaUpdate()] HTTP_UPDATE_OK\n"); break; case HTTP_UPDATE_FAILED: - result.success = false; - Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", OTA_CLASS.getLastError(), OTA_CLASS.getLastErrorString().c_str()); + errorMsg = String("HTTP_UPDATE_FAILED Error (") + OTA_CLASS.getLastError() + "): " + OTA_CLASS.getLastErrorString(); + Serial.printf("%s\n", errorMsg.c_str()); break; case HTTP_UPDATE_NO_UPDATES: - result.success = false; - Serial.println("HTTP_UPDATE_NO_UPDATES"); + errorMsg = "HTTP_UPDATE_NO_UPDATES"; + Serial.println(errorMsg); break; } - - return result; + return errorMsg; } #endif \ No newline at end of file diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index fdd2cf12..ea45aaf6 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -54,9 +54,9 @@ #define BAUD_RATE 115200 // Change baudrate to your need bool handleOTAUpdate(const String& url, int major, int minor, int patch, bool forceUpdate) { - Version currentVersion = parseVersion(FIRMWARE_VERSION); - Version newVersion = parseVersion(String(major) + "." + String(minor) + "." + String(patch)); - bool updateAvailable = isNewerVersion(currentVersion, newVersion); + Version currentVersion = Version(FIRMWARE_VERSION); + Version newVersion = Version(String(major) + "." + String(minor) + "." + String(patch)); + bool updateAvailable = newVersion > currentVersion; Serial.print("URL: "); Serial.println(url.c_str()); @@ -72,14 +72,16 @@ bool handleOTAUpdate(const String& url, int major, int minor, int patch, bool fo Serial.println("Update available!"); } - OTAResult result = startOTAUpdate(url); - if (!result.success) { - SinricPro.setResponseMessage(std::move(result.errorMessage)); - } - return result.success; + String result = startOtaUpdate(url); + if (!result.isEmpty()) { + SinricPro.setResponseMessage(std::move(result)); + return false; + } + return true; } else { - SinricPro.setResponseMessage("Current version is up to date."); - Serial.println("Current version is up to date."); + String result = "Current version is up to date."; + SinricPro.setResponseMessage(result); + Serial.println(result); return false; } } diff --git a/examples/OTAUpdate/SemVer.h b/examples/OTAUpdate/SemVer.h index 1d1776e0..a7684fd4 100644 --- a/examples/OTAUpdate/SemVer.h +++ b/examples/OTAUpdate/SemVer.h @@ -2,32 +2,55 @@ #include -struct Version { +class Version { + public: + Version(const String& versionStr); + String toString() const; + + bool operator > (const Version& other) const; + bool operator < (const Version& other) const; + bool operator == (const Version& other) const; + bool operator != (const Version& other) const; + + protected: int major; int minor; int patch; - - String toString() const { - return String(major) + "." + String(minor) + "." + String(patch); - } }; -Version parseVersion(const String& versionStr) { - Version v; - int firstDot = versionStr.indexOf('.'); - int secondDot = versionStr.lastIndexOf('.'); - v.major = versionStr.substring(0, firstDot).toInt(); - v.minor = versionStr.substring(firstDot + 1, secondDot).toInt(); - v.patch = versionStr.substring(secondDot + 1).toInt(); - return v; +Version::Version(const String& versionStr) { + int firstDot = versionStr.indexOf('.'); + int secondDot = versionStr.lastIndexOf('.'); + major = versionStr.substring(0, firstDot).toInt(); + minor = versionStr.substring(firstDot + 1, secondDot).toInt(); + patch = versionStr.substring(secondDot + 1).toInt(); +} + +String Version::toString() const { + return String(major) + "." + String(minor) + "." + String(patch); +} + +bool Version::operator>(const Version& other) const { + if (major > other.major) return true; + if (minor > other.minor) return true; + if (patch > other.patch) return true; + return false; +} + +bool Version::operator<(const Version& other) const { + if (major < other.major) return true; + if (minor < other.minor) return true; + if (patch < other.patch) return true; + return false; +} + +bool Version::operator==(const Version& other) const { + if (major != other.major) return false; + if (minor != other.minor) return false; + if (patch != other.patch) return false; + return true; } -bool isNewerVersion(const Version& currentVersion, const Version& newVersion) { - if (newVersion.major > currentVersion.major) return true; - if (newVersion.major < currentVersion.major) return false; - - if (newVersion.minor > currentVersion.minor) return true; - if (newVersion.minor < currentVersion.minor) return false; - - return newVersion.patch > currentVersion.patch; +bool Version::operator!=(const Version& other) const { + return !operator==(other); } \ No newline at end of file From ffdbe01814360f1fb2f3b79e66cf4d9981a89306 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 21 Jul 2024 00:02:43 +0700 Subject: [PATCH 34/36] fix: remove OTAResult --- examples/OTAUpdate/OTAResult.h | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 examples/OTAUpdate/OTAResult.h diff --git a/examples/OTAUpdate/OTAResult.h b/examples/OTAUpdate/OTAResult.h deleted file mode 100644 index 9f890dfa..00000000 --- a/examples/OTAUpdate/OTAResult.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include - -struct OTAResult { - bool success; - String errorMessage; -}; From 027b89732afa55456c110290a631313af269f9ff Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 21 Jul 2024 00:02:51 +0700 Subject: [PATCH 35/36] fix: remove OTAResult --- examples/OTAUpdate/OTAUpdate.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index ea45aaf6..98c8ce29 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -27,7 +27,6 @@ #endif #include -#include "OTAResult.h" #if defined(ESP8266) #include From 040e8af51f972eeae561ed3d12c84ae32546ff53 Mon Sep 17 00:00:00 2001 From: Aruna Tennakoon Date: Sun, 21 Jul 2024 00:37:14 +0700 Subject: [PATCH 36/36] fix: ota update example --- examples/OTAUpdate/OTAUpdate.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/OTAUpdate/OTAUpdate.ino b/examples/OTAUpdate/OTAUpdate.ino index 98c8ce29..1d3af1eb 100644 --- a/examples/OTAUpdate/OTAUpdate.ino +++ b/examples/OTAUpdate/OTAUpdate.ino @@ -79,7 +79,7 @@ bool handleOTAUpdate(const String& url, int major, int minor, int patch, bool fo return true; } else { String result = "Current version is up to date."; - SinricPro.setResponseMessage(result); + SinricPro.setResponseMessage(std::move(result)); Serial.println(result); return false; }