Parcourir la source

add optional ImageUpload for Webhook (#3174)

* WIP add Webhook

* fix config html for webhook
add tooltips for webhook

* webhook: fix not enabling webhook

* send webhook as json

* Update ApiKey.md

* webhook: fix only sending last "Number"

* webhook JSON is now closer to the data log in CSV format

* webhook: add img upload

* webhoop added config for imgupload

* webhook html fixes

* webhook: drop timeStampTimeUTC and switch from timeStamp to lastvalue like lokal csv to fix no timestamp on error

* add checkbox for Webhook_UploadImg

* Update sd-card/html/edit_config_template.html

* Update edit_config_template.html

* Update edit_config_template.html

* Update edit_config_template.html

* added a long timestamp to both webhook requests

---------

Co-authored-by: CaCO3 <caco3@ruinelli.ch>
Raphael Hehl il y a 1 an
Parent
commit
cd29690b96

+ 30 - 2
code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp

@@ -8,6 +8,7 @@
 #include "interface_webhook.h"
 
 #include "ClassFlowPostProcessing.h"
+#include "ClassFlowAlignment.h"
 #include "esp_log.h"
 #include "../../include/defines.h"
 
@@ -20,11 +21,13 @@ static const char* TAG = "WEBHOOK";
 void ClassFlowWebhook::SetInitialParameter(void)
 {
     uri = "";
-    flowpostprocessing = NULL;  
+    flowpostprocessing = NULL;
+    flowAlignment = NULL;
     previousElement = NULL;
     ListFlowControll = NULL; 
     disabled = false;
     WebhookEnable = false;
+    WebhookUploadImg = 0;
 }       
 
 ClassFlowWebhook::ClassFlowWebhook()
@@ -43,6 +46,11 @@ ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow*>* lfc)
         {
             flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
         }
+        if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
+        {
+            flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
+        }
+
     }
 }
 
@@ -59,6 +67,10 @@ ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow*>* lfc, ClassFlow *_pre
         {
             flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
         }
+        if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
+        {
+            flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
+        }
     }
 }
 
@@ -93,6 +105,16 @@ bool ClassFlowWebhook::ReadParameter(FILE* pfile, string& aktparamgraph)
         {
             this->apikey = splitted[1];
         }
+        if (((toUpper(_param) == "UPLOADIMG")) && (splitted.size() > 1))
+        {
+            if (toUpper(splitted[1]) == "1")
+            {
+                this->WebhookUploadImg = 1;
+            } else if (toUpper(splitted[1]) == "2")
+            {
+                this->WebhookUploadImg = 2;
+            }
+        }
     }
     
     WebhookInit(uri,apikey);
@@ -135,7 +157,13 @@ bool ClassFlowWebhook::doFlow(string zwtime)
     if (flowpostprocessing)
     {
         printf("vor sende WebHook");
-        WebhookPublish(flowpostprocessing->GetNumbers());
+        bool numbersWithError = WebhookPublish(flowpostprocessing->GetNumbers());
+
+        #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
+            if ((WebhookUploadImg == 1 || (WebhookUploadImg != 0 && numbersWithError)) && flowAlignment && flowAlignment->AlgROI) {
+                WebhookUploadPic(flowAlignment->AlgROI);
+            }
+        #endif
     }
        
     return true;

+ 5 - 1
code/components/jomjol_flowcontroll/ClassFlowWebhook.h

@@ -8,6 +8,7 @@
 #include "ClassFlow.h"
 
 #include "ClassFlowPostProcessing.h"
+#include "ClassFlowAlignment.h"
 
 #include <string>
 
@@ -16,8 +17,11 @@ class ClassFlowWebhook :
 {
 protected:
     std::string uri, apikey;
-	ClassFlowPostProcessing* flowpostprocessing;  
+	ClassFlowPostProcessing* flowpostprocessing;
+    ClassFlowAlignment* flowAlignment;
+
     bool WebhookEnable;
+    int WebhookUploadImg;
 
     void SetInitialParameter(void); 
 

+ 51 - 3
code/components/jomjol_webhook/interface_webhook.cpp

@@ -15,6 +15,7 @@ static const char *TAG = "WEBHOOK";
 
 std::string _webhookURI;
 std::string _webhookApiKey;
+long _lastTimestamp;
 
 static esp_err_t http_event_handler(esp_http_client_event_t *evt);
 
@@ -22,22 +23,27 @@ void WebhookInit(std::string _uri, std::string _apiKey)
 {
     _webhookURI = _uri;
     _webhookApiKey = _apiKey;
+    _lastTimestamp = 0L;
 }
 
-void WebhookPublish(std::vector<NumberPost*>* numbers)
+bool WebhookPublish(std::vector<NumberPost*>* numbers)
 {
+    bool numbersWithError = false;
     cJSON *jsonArray = cJSON_CreateArray();
 
     for (int i = 0; i < (*numbers).size(); ++i)
     {
         string timezw = "";
         char buffer[80];
-        struct tm* timeinfo = localtime(&(*numbers)[i]->timeStampLastPreValue);
+        time_t &lastPreValue = (*numbers)[i]->timeStampLastPreValue;
+        struct tm* timeinfo = localtime(&lastPreValue);
+        _lastTimestamp = static_cast<long>(lastPreValue);
         strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo);
         timezw = std::string(buffer);
 
         cJSON *json = cJSON_CreateObject();
         cJSON_AddStringToObject(json, "timestamp", timezw.c_str());
+        cJSON_AddStringToObject(json, "timestampLong", std::to_string(_lastTimestamp).c_str());
         cJSON_AddStringToObject(json, "name", (*numbers)[i]->name.c_str());
         cJSON_AddStringToObject(json, "rawValue", (*numbers)[i]->ReturnRawValue.c_str());
         cJSON_AddStringToObject(json, "value", (*numbers)[i]->ReturnValue.c_str());
@@ -47,6 +53,10 @@ void WebhookPublish(std::vector<NumberPost*>* numbers)
         cJSON_AddStringToObject(json, "error", (*numbers)[i]->ErrorMessageText.c_str());
         
         cJSON_AddItemToArray(jsonArray, json);
+
+        if ((*numbers)[i]->ErrorMessage) {
+            numbersWithError = true;
+        }
     }
 
     char *jsonString = cJSON_PrintUnformatted(jsonArray);
@@ -79,13 +89,51 @@ void WebhookPublish(std::vector<NumberPost*>* numbers)
         LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code: " + std::to_string(status_code));
     } else {
         LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "HTTP request failed");
-    }
+    } 
 
     esp_http_client_cleanup(http_client);
     cJSON_Delete(jsonArray);
     free(jsonString);
+    return numbersWithError;    
 }
 
+void WebhookUploadPic(ImageData *Img) {
+    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Starting WebhookUploadPic");
+
+    std::string fullURI = _webhookURI + "?timestamp=" + std::to_string(_lastTimestamp);
+    char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
+    esp_http_client_config_t http_config = {
+        .url = fullURI.c_str(),
+        .user_agent = "ESP32 Meter reader",
+        .method = HTTP_METHOD_PUT,
+        .event_handler = http_event_handler,
+        .buffer_size = MAX_HTTP_OUTPUT_BUFFER,
+        .user_data = response_buffer
+    };
+
+    esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
+
+    esp_http_client_set_header(http_client, "Content-Type", "image/jpeg");
+    esp_http_client_set_header(http_client, "APIKEY", _webhookApiKey.c_str());
+
+    esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_set_post_field(http_client, (const char *)Img->data, Img->size));
+
+    err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
+
+    if (err == ESP_OK) {
+        LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP PUT request was performed successfully");
+        int status_code = esp_http_client_get_status_code(http_client);
+        LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code: " + std::to_string(status_code));
+    } else {
+        LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "HTTP PUT request failed");
+    }
+
+    esp_http_client_cleanup(http_client);
+
+    LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WebhookUploadPic finished");
+}
+
+
 static esp_err_t http_event_handler(esp_http_client_event_t *evt)
 {
     switch(evt->event_id)

+ 2 - 1
code/components/jomjol_webhook/interface_webhook.h

@@ -10,7 +10,8 @@
 #include <ClassFlowDefineTypes.h>
 
 void WebhookInit(std::string _webhookURI, std::string _apiKey);
-void WebhookPublish(std::vector<NumberPost*>* numbers);
+bool WebhookPublish(std::vector<NumberPost*>* numbers);
+void WebhookUploadPic(ImageData *Img);
 
 #endif //INTERFACE_WEBHOOK_H
 #endif //ENABLE_WEBHOOK

+ 8 - 0
param-docs/parameter-pages/Webhook/UploadImg.md

@@ -0,0 +1,8 @@
+# Parameter `UploadImg`
+Default Value: `0` (`NEVER`)
+
+Available options:
+
+- `0`: `NEVER`
+- `1`: `ALWAYS`
+- `2`: `ON_ERROR`

+ 1 - 0
sd-card/config/config.ini

@@ -108,6 +108,7 @@ HomeassistantDiscovery = false
 ;[Webhook]
 ;Uri = undefined
 ;ApiKey = undefined
+;UploadImg = 0
 
 ;[GPIO]
 ;MainTopicMQTT = wasserzaehler/GPIO

+ 19 - 1
sd-card/html/edit_config_template.html

@@ -1353,7 +1353,8 @@
 			<td colspan="3" style="padding-left: 0px; padding-bottom: 3px;">
 			    <h4>
 				<input type="checkbox" id="Category_Webhook_enabled" value="1"  onclick = 'UpdateAfterCategoryCheck()' unchecked >
-				<label for=Category_Webhook_enabled>Webhook</label></h4>		
+				<label for=Category_Webhook_enabled>Webhook</label>
+				</h4>		
 			</td>
 		</tr>
 
@@ -1379,6 +1380,20 @@
 			<td>$TOOLTIP_Webhook_ApiKey</td>
 		</tr>
 
+		<tr class="WebhookItem">
+			<td class="indent1">
+				<input type="checkbox" id="Webhook_UploadImg_enabled" value="1"  onclick = 'InvertEnableItem("Webhook", "UploadImg")' unchecked>
+				<label for=Webhook_UploadImg_enabled><class id="Webhook_UploadImg_text" style="color:black;">Upload Image</class></label>
+			</td>
+			<td>
+				<select id="Webhook_UploadImg_value1">
+					<option value="0" selected>NEVER</option>
+					<option value="1">ALWAYS</option>
+					<option value="2">ON_ERROR</option>
+				</select>
+			</td>
+			<td>$TOOLTIP_Webhook_UploadImg</td>
+		</tr>
 
 		<!------------- GPIO ------------------>
 		<tr style="border-bottom: 2px solid lightgray;">
@@ -2310,6 +2325,7 @@ function UpdateInput() {
 
 	WriteParameter(param, category, "Webhook", "Uri", true);	
     WriteParameter(param, category, "Webhook", "ApiKey", true);
+	WriteParameter(param, category, "Webhook", "UploadImg", true);
 
     WriteParameter(param, category, "GPIO", "IO0", true);
     WriteParameter(param, category, "GPIO", "IO1", true);
@@ -2391,6 +2407,7 @@ function ReadParameterAll() {
     category["MQTT"]["enabled"] = document.getElementById("Category_MQTT_enabled").checked;
     category["InfluxDB"]["enabled"] = document.getElementById("Category_InfluxDB_enabled").checked;
     category["InfluxDBv2"]["enabled"] = document.getElementById("Category_InfluxDBv2_enabled").checked;
+	category["Webhook"]["enabled"] = document.getElementById("Category_Webhook_enabled").checked;
     category["GPIO"]["enabled"] = document.getElementById("Category_GPIO_enabled").checked;
 
     ReadParameter(param, "TakeImage", "RawImagesLocation", true);
@@ -2474,6 +2491,7 @@ function ReadParameterAll() {
 
 	ReadParameter(param, "Webhook", "Uri", true);	
     ReadParameter(param, "Webhook", "ApiKey", true);
+	ReadParameter(param, "Webhook", "UploadImg", true);
 
     ReadParameter(param, "GPIO", "IO0", true);
     ReadParameter(param, "GPIO", "IO1", true);

+ 1 - 0
sd-card/html/readconfigparam.js

@@ -237,6 +237,7 @@ function ParseConfig() {
     param[catname] = new Object();
     ParamAddValue(param, catname, "Uri");
     ParamAddValue(param, catname, "ApiKey");
+    ParamAddValue(param, catname, "UploadImg");
 
     var catname = "GPIO";
     category[catname] = new Object();