diff --git a/src/Clients/BasePrinterClient.h b/src/Clients/BasePrinterClient.h index 7f3b48c..4985ff5 100644 --- a/src/Clients/BasePrinterClient.h +++ b/src/Clients/BasePrinterClient.h @@ -3,6 +3,7 @@ #include #include #include "Debug.h" +#include "../Network/JsonRequestClient.h" class BasePrinterClient { public: diff --git a/src/Clients/BasePrinterClientImpl.cpp b/src/Clients/BasePrinterClientImpl.cpp index b110181..441e2bf 100644 --- a/src/Clients/BasePrinterClientImpl.cpp +++ b/src/Clients/BasePrinterClientImpl.cpp @@ -1,9 +1,15 @@ #include "BasePrinterClientImpl.h" -BasePrinterClientImpl::BasePrinterClientImpl(String printerType, GlobalDataController *globalDataController, DebugController *debugController) { +BasePrinterClientImpl::BasePrinterClientImpl( + String printerType, + GlobalDataController *globalDataController, + DebugController *debugController, + JsonRequestClient *jsonRequestClient +) { this->globalDataController = globalDataController; this->debugController = debugController; this->printerType = printerType; + this->jsonRequestClient = jsonRequestClient; } // Reset all PrinterData @@ -132,4 +138,16 @@ String BasePrinterClientImpl::getPrinterName() { void BasePrinterClientImpl::setPrinterName(String printer) { printerData.printerName = printer; +} + +String BasePrinterClientImpl::getInstanceServerTarget() { + String targetServer = this->globalDataController->getPrinterServer(); + if (this->globalDataController->getPrinterServer() == "") { + targetServer = this->globalDataController->getPrinterHostName(); + } + return targetServer; +} + +int BasePrinterClientImpl::getInstanceServerPort() { + return this->globalDataController->getPrinterPort(); } \ No newline at end of file diff --git a/src/Clients/BasePrinterClientImpl.h b/src/Clients/BasePrinterClientImpl.h index c1a3495..4c2ec9d 100644 --- a/src/Clients/BasePrinterClientImpl.h +++ b/src/Clients/BasePrinterClientImpl.h @@ -2,10 +2,12 @@ #include "BasePrinterClient.h" #include "../Global/GlobalDataController.h" + class BasePrinterClientImpl : public BasePrinterClient { protected: GlobalDataController *globalDataController; DebugController *debugController; + JsonRequestClient *jsonRequestClient; String printerType = "Octoprint"; typedef struct { @@ -31,9 +33,11 @@ protected: } PrinterStruct; PrinterStruct printerData; + String result; + String encodedAuth = ""; public: - BasePrinterClientImpl(String printerType, GlobalDataController *globalDataController, DebugController *debugController); + BasePrinterClientImpl(String printerType, GlobalDataController *globalDataController, DebugController *debugController, JsonRequestClient *jsonRequestClient); void getPrinterJobResults() {}; void getPrinterPsuState() {}; @@ -64,4 +68,8 @@ public: int getPrinterPort(); String getPrinterName(); void setPrinterName(String printer); + +protected: + String getInstanceServerTarget(); + int getInstanceServerPort(); }; \ No newline at end of file diff --git a/src/Clients/DuetClient.cpp b/src/Clients/DuetClient.cpp index 0263acd..aca5994 100644 --- a/src/Clients/DuetClient.cpp +++ b/src/Clients/DuetClient.cpp @@ -3,8 +3,8 @@ #include "DuetClient.h" -DuetClient::DuetClient(GlobalDataController *globalDataController, DebugController *debugController) -: BasePrinterClientImpl("Duet", globalDataController, debugController) { +DuetClient::DuetClient(GlobalDataController *globalDataController, DebugController *debugController, JsonRequestClient *jsonRequestClient) +: BasePrinterClientImpl("Duet", globalDataController, debugController, jsonRequestClient) { this->updatePrintClient(); } diff --git a/src/Clients/DuetClient.h b/src/Clients/DuetClient.h index e0b8115..bf76586 100644 --- a/src/Clients/DuetClient.h +++ b/src/Clients/DuetClient.h @@ -18,7 +18,7 @@ private: String result; public: - DuetClient(GlobalDataController *globalDataController, DebugController *debugController); + DuetClient(GlobalDataController *globalDataController, DebugController *debugController, JsonRequestClient *jsonRequestClient); void getPrinterJobResults() override; void getPrinterPsuState() override; diff --git a/src/Clients/KlipperClient.cpp b/src/Clients/KlipperClient.cpp index c7f55e1..e89857e 100644 --- a/src/Clients/KlipperClient.cpp +++ b/src/Clients/KlipperClient.cpp @@ -3,8 +3,8 @@ #include "KlipperClient.h" -KlipperClient::KlipperClient(GlobalDataController *globalDataController, DebugController *debugController) -: BasePrinterClientImpl("Klipper", globalDataController, debugController) { +KlipperClient::KlipperClient(GlobalDataController *globalDataController, DebugController *debugController, JsonRequestClient *jsonRequestClient) +: BasePrinterClientImpl("Klipper", globalDataController, debugController, jsonRequestClient) { this->updatePrintClient(); } @@ -31,6 +31,11 @@ boolean KlipperClient::validate() { } WiFiClient KlipperClient::getSubmitRequest(String apiGetData) { + + + + + WiFiClient printClient; printClient.setTimeout(5000); String targetServer = this->globalDataController->getPrinterServer(); @@ -151,16 +156,54 @@ WiFiClient KlipperClient::getPostRequest(String apiPostData, String apiPostBody) return printClient; } + + + + + + + + + + void KlipperClient::getPrinterJobResults() { if (!validate()) { return; } + + const size_t bufferSize = JSON_ARRAY_SIZE(4) + JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(9) + 426; + DynamicJsonDocument *jsonDoc = this->jsonRequestClient->requestJson( + PRINTER_REQUEST_GET, + this->getInstanceServerTarget(), + this->getInstanceServerPort(), + this->encodedAuth, + "/printer/objects/query?heater_bed&extruder&webhooks&virtual_sdcard&print_stats&toolhead&display_status", + "", + bufferSize, + true + ); + if (this->jsonRequestClient->getLastError() != "") { + this->debugController->printLn("Get Klipper Data: " + this->getInstanceServerTarget() + ":" + String(this->getInstanceServerPort())); + this->debugController->printLn(this->jsonRequestClient->getLastError()); + printerData.error = this->jsonRequestClient->getLastError(); + printerData.state = ""; + printerData.isPrinting = false; + printerData.toolTemp = ""; + printerData.toolTargetTemp = ""; + printerData.bedTemp = ""; + printerData.bedTargetTemp = ""; + return; + } + + + + /* String targetServer = this->globalDataController->getPrinterServer(); if (this->globalDataController->getPrinterServer() == "") { targetServer = this->globalDataController->getPrinterHostName(); } - //**** get the Printer Job status + // get the Printer Job status String apiGetData = "GET /printer/objects/query?heater_bed&extruder&webhooks&virtual_sdcard&print_stats&toolhead&display_status"; WiFiClient printClient = getSubmitRequest(apiGetData); if (printerData.error != "") { @@ -193,7 +236,7 @@ void KlipperClient::getPrinterJobResults() { printerData.progressPrintTime = (const char*)jsonBuffer["result"]["status"]["print_stats"]["print_duration"]; printerData.progressPrintTimeLeft = (const char*)jsonBuffer["progress"]["printTimeLeft"]; printerData.filamentLength = (const char*)jsonBuffer["result"]["status"]["job"]["print_stats"]["filament_used"]; - printerData.state = (const char*)jsonBuffer["result"]["status"]["print_stats"]["state"]; + printerData.state = (const char*)jsonBuffer["result"]["status"]["print_stats"]["state"];*/ /** printerData.progressPrintTimeLeft : If no metadata is available, print duration and progress can be used to calculate the ETA: @@ -205,13 +248,13 @@ let total_time = pstats.print_duration / vsd.progress; let eta = total_time - pstats.print_duration; */ - if (BasePrinterClientImpl::isOperational()) { + /*if (BasePrinterClientImpl::isOperational()) { this->debugController->printLn("Status: " + printerData.state); } else { this->debugController->printLn("Printer Not Operational"); } - //**** get the fileseize + // get the fileseize apiGetData = "GET /server/files/metadata?filename=" + printerData.fileName; printClient = getSubmitRequest(apiGetData); if (printerData.error != "") { @@ -245,7 +288,7 @@ let eta = total_time - pstats.print_duration; */ if (BasePrinterClientImpl::isOperational()) { this->debugController->printLn("Status: " + printerData.state + " " + printerData.fileName + "(" + printerData.progressCompletion + "%)"); - } + }*/ } void KlipperClient::getPrinterPsuState() { diff --git a/src/Clients/KlipperClient.h b/src/Clients/KlipperClient.h index 378808f..d02625c 100644 --- a/src/Clients/KlipperClient.h +++ b/src/Clients/KlipperClient.h @@ -8,17 +8,14 @@ class KlipperClient : public BasePrinterClientImpl { private: - String encodedAuth = ""; boolean pollPsu; boolean validate(); WiFiClient getSubmitRequest(String apiGetData); WiFiClient getPostRequest(String apiPostData, String apiPostBody); - - String result; public: - KlipperClient(GlobalDataController *globalDataController, DebugController *debugController); + KlipperClient(GlobalDataController *globalDataController, DebugController *debugController, JsonRequestClient *jsonRequestClient); void getPrinterJobResults() override; void getPrinterPsuState() override; diff --git a/src/Includes.h b/src/Includes.h index 994197d..f53c1eb 100644 --- a/src/Includes.h +++ b/src/Includes.h @@ -9,6 +9,7 @@ #endif #include "Network/TimeClient.h" #include "Network/OpenWeatherMapClient.h" +#include "Network/JsonRequestClient.h" #if (PRINTERCLIENT == REPETIER_CLIENT) #include "Clients/RepetierClient.h" #elif (PRINTERCLIENT == KLIPPER_CLIENT) @@ -28,8 +29,9 @@ // Initilize all needed data DebugController debugController(DEBUG_MODE_ENABLE); +JsonRequestClient jsonRequestClient(&debugController); TimeClient timeClient(TIME_UTCOFFSET, &debugController); -OpenWeatherMapClient weatherClient(WEATHER_APIKEY, WEATHER_CITYID, 1, WEATHER_METRIC, WEATHER_LANGUAGE, &debugController); +OpenWeatherMapClient weatherClient(WEATHER_APIKEY, WEATHER_CITYID, 1, WEATHER_METRIC, WEATHER_LANGUAGE, &debugController, &jsonRequestClient); GlobalDataController globalDataController(&timeClient, &weatherClient, &debugController); WebServer webServer(&globalDataController, &debugController); @@ -37,7 +39,7 @@ WebServer webServer(&globalDataController, &debugController); #if (PRINTERCLIENT == REPETIER_CLIENT) //RepetierClient printerClient(&globalDataController); #elif (PRINTERCLIENT == KLIPPER_CLIENT) - KlipperClient printerClient(&globalDataController, &debugController); + KlipperClient printerClient(&globalDataController, &debugController, &jsonRequestClient); #elif (PRINTERCLIENT == DUET_CLIENT) //DuetClient printerClient(PrinterApiKey, PrinterServer, PrinterPort, PrinterAuthUser, PrinterAuthPass, HAS_PSU, debugHandle); #else diff --git a/src/Network/JsonRequestClient.cpp b/src/Network/JsonRequestClient.cpp new file mode 100644 index 0000000..0f65d16 --- /dev/null +++ b/src/Network/JsonRequestClient.cpp @@ -0,0 +1,132 @@ +#include "JsonRequestClient.h" + +DynamicJsonDocument *JsonRequestClient::lastJsonDocument = NULL; + +JsonRequestClient::JsonRequestClient(DebugController *debugController) { + this->debugController = debugController; +} + +WiFiClient JsonRequestClient::requestWifiClient( + int requestType, + String server, + int port, + String encodedAuth, + String httpPath, + String apiPostBody +) { + WiFiClient requestClient; + requestClient.setTimeout(5000); + httpPath = (requestType == PRINTER_REQUEST_POST ? "POST " : "GET ") + httpPath; + String fullTarget = server + ":" + String(port) + httpPath; + + this->debugController->print("Request data from "); + this->debugController->print(httpPath); + if (requestType == PRINTER_REQUEST_POST) { + this->debugController->print(" | " + apiPostBody); + } + this->debugController->printLn(""); + + if (requestClient.connect(server, port)) { //starts client connection, checks for connection + requestClient.println(httpPath); + requestClient.println("Host: " + server + ":" + String(port)); + if (encodedAuth != "") { + requestClient.print("Authorization: "); + requestClient.println("Basic " + encodedAuth); + } + requestClient.println("User-Agent: ArduinoWiFi/1.1"); + requestClient.println("Connection: close"); + if (requestType == PRINTER_REQUEST_POST) { + requestClient.println("Content-Type: application/json"); + requestClient.print("Content-Length: "); + requestClient.println(apiPostBody.length()); + requestClient.println(); + requestClient.println(apiPostBody); + } + + if (requestClient.println() == 0) { + this->debugController->printLn("Connection to " + fullTarget + " failed."); + this->debugController->printLn(""); + this->lastError = "Connection to " + fullTarget + " failed."; + return requestClient; + } + } + else { + // error message if no client connect + this->debugController->printLn("Connection failed: " + fullTarget); + this->debugController->printLn(""); + this->lastError = "Connection failed: " + fullTarget; + return requestClient; + } + + while(requestClient.connected() && !requestClient.available()) delay(1); + + // Check HTTP status + char status[32] = {0}; + requestClient.readBytesUntil('\r', status, sizeof(status)); + if (strcmp(status, "HTTP/1.1 200 OK") != 0 && strcmp(status, "HTTP/1.1 409 CONFLICT") != 0) { + this->debugController->print("Unexpected response: "); + this->debugController->printLn(status); + this->lastError = "Response: " + String(status); + return requestClient; + } + + // Skip HTTP headers + char endOfHeaders[] = "\r\n\r\n"; + if (!requestClient.find(endOfHeaders)) { + this->debugController->printLn("Invalid response"); + this->lastError = "Invalid response from " + fullTarget; + } + + return requestClient; +} + +DynamicJsonDocument *JsonRequestClient::requestJson( + int requestType, + String server, + int port, + String encodedAuth, + String httpPath, + String apiPostBody, + size_t bufferSize, + bool withResponse = false +) { + // Request data + this->resetLastError(); + WiFiClient reqClient = this->requestWifiClient(requestType, server, port, encodedAuth, httpPath, apiPostBody); + if ((this->lastError != "") || !withResponse) { + return NULL; + } + + // Parse JSON object + DynamicJsonDocument *returnJson = this->createNewJsonDocument(bufferSize); + DeserializationError error = deserializeJson(*returnJson, reqClient); + if (error) { + this->debugController->printLn("Data Parsing failed: " + server + ":" + String(port)); + this->lastError = "Klipper Data Parsing failed: " + server + ":" + String(port); + return NULL; + } + return returnJson; +} + +String JsonRequestClient::getLastError() { + return this->lastError; +} + +void JsonRequestClient::resetLastError() { + this->lastError = ""; +} + +DynamicJsonDocument *JsonRequestClient::createNewJsonDocument(size_t bufferSize) { + if (JsonRequestClient::lastJsonDocument != NULL) { + this->freeLastJsonDocument(); + } + JsonRequestClient::lastJsonDocument = new DynamicJsonDocument(bufferSize); + return JsonRequestClient::lastJsonDocument; +} + +void JsonRequestClient::freeLastJsonDocument() { + if (JsonRequestClient::lastJsonDocument != NULL) { + free(JsonRequestClient::lastJsonDocument); + JsonRequestClient::lastJsonDocument = NULL; + } +} \ No newline at end of file diff --git a/src/Network/JsonRequestClient.h b/src/Network/JsonRequestClient.h new file mode 100644 index 0000000..a95fe6f --- /dev/null +++ b/src/Network/JsonRequestClient.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include +#include +#include "Debug.h" +#include "../Global/DebugController.h" + +#define PRINTER_REQUEST_GET 0 +#define PRINTER_REQUEST_POST 1 + +class JsonRequestClient { +private: + DebugController *debugController; + String lastError = ""; + + static DynamicJsonDocument* lastJsonDocument; +public: + JsonRequestClient(DebugController *debugController); + + DynamicJsonDocument *requestJson(int requestType, String server, int port, String encodedAuth, String httpPath, String apiPostBody, size_t bufferSize, bool withResponse); + String getLastError(); + void resetLastError(); + +private: + WiFiClient requestWifiClient(int requestType, String server, int port, String encodedAuth, String httpPath, String apiPostBody); + void freeLastJsonDocument(); + DynamicJsonDocument *createNewJsonDocument(size_t bufferSize); +}; diff --git a/src/Network/OpenWeatherMapClient.cpp b/src/Network/OpenWeatherMapClient.cpp index 0f8624d..fd7abc1 100644 --- a/src/Network/OpenWeatherMapClient.cpp +++ b/src/Network/OpenWeatherMapClient.cpp @@ -1,12 +1,13 @@ #include "OpenWeatherMapClient.h" -OpenWeatherMapClient::OpenWeatherMapClient(String ApiKey, int CityID, int cityCount, boolean isMetric, String language, DebugController *debugController) { +OpenWeatherMapClient::OpenWeatherMapClient(String ApiKey, int CityID, int cityCount, boolean isMetric, String language, DebugController *debugController, JsonRequestClient *jsonRequestClient) { this->debugController = debugController; - updateCityId(CityID); - updateLanguage(language); - myApiKey = ApiKey; + this->jsonRequestClient = jsonRequestClient; + this->updateCityId(CityID); + this->updateLanguage(language); + this->myApiKey = ApiKey; this->isMetric = false; - setMetric(isMetric); + this->setMetric(isMetric); } void OpenWeatherMapClient::updateWeatherApiKey(String ApiKey) { @@ -21,77 +22,46 @@ void OpenWeatherMapClient::updateLanguage(String language) { } void OpenWeatherMapClient::updateWeather() { - WiFiClient weatherClient; - String apiGetData = "GET /data/2.5/group?id=" + myCityIDs + "&units=" + units + "&cnt=1&APPID=" + myApiKey + "&lang=" + lang + " HTTP/1.1"; - + + String apiGetData = "/data/2.5/group?id=" + myCityIDs + "&units=" + units + "&cnt=1&APPID=" + myApiKey + "&lang=" + lang + " HTTP/1.1"; this->debugController->printLn("Getting Weather Data"); this->debugController->printLn(apiGetData); - result = ""; - if (weatherClient.connect(servername, 80)) { //starts client connection, checks for connection - weatherClient.println(apiGetData); - weatherClient.println("Host: " + String(servername)); - weatherClient.println("User-Agent: ArduinoWiFi/1.1"); - weatherClient.println("Connection: close"); - weatherClient.println(); - } - else { - this->debugController->printLn("connection for weather data failed"); //error message if no client connect - this->debugController->printLn(""); - return; - } - - while(weatherClient.connected() && !weatherClient.available()) delay(1); //waits for data - - this->debugController->printLn("Waiting for data"); - - // Check HTTP status - char status[32] = {0}; - weatherClient.readBytesUntil('\r', status, sizeof(status)); - this->debugController->printLn("Response Header: " + String(status)); - if (strcmp(status, "HTTP/1.1 200 OK") != 0) { - this->debugController->print("Unexpected response: "); - this->debugController->printLn(status); - return; - } - - // Skip HTTP headers - char endOfHeaders[] = "\r\n\r\n"; - if (!weatherClient.find(endOfHeaders)) { - this->debugController->printLn(F("Invalid response")); - return; - } - + const size_t bufferSize = 1024; weathers[0].cached = false; weathers[0].error = ""; - const size_t bufferSize = 1024; - DynamicJsonDocument jsonBuffer(bufferSize); - - // Parse JSON object - DeserializationError error = deserializeJson(jsonBuffer, weatherClient); - if (error) { + DynamicJsonDocument *jsonBuffer = this->jsonRequestClient->requestJson( + PRINTER_REQUEST_GET, + String(servername), + 80, + "", + apiGetData, + "", + bufferSize, + true + ); + if ((jsonBuffer == NULL) || (this->jsonRequestClient->getLastError() != "")) { this->debugController->printLn("Weather Data Parsing failed!"); - this->debugController->printLn(error.c_str()); - weathers[0].error = "Weather Data Parsing failed!"; + this->debugController->printLn(this->jsonRequestClient->getLastError()); + weathers[0].error = this->jsonRequestClient->getLastError(); return; } - weatherClient.stop(); //stop client - int count = jsonBuffer["cnt"]; + int count = (*jsonBuffer)["cnt"]; for (int inx = 0; inx < count; inx++) { - weathers[inx].lon = (float)jsonBuffer["list"][inx]["coord"]["lon"]; - weathers[inx].lat = (float)jsonBuffer["list"][inx]["coord"]["lat"]; - weathers[inx].dt = (long)jsonBuffer["list"][inx]["dt"]; - weathers[inx].city = (const char*)jsonBuffer["list"][inx]["name"]; - weathers[inx].country = (const char*)jsonBuffer["list"][inx]["sys"]["country"]; - weathers[inx].temp = (float)jsonBuffer["list"][inx]["main"]["temp"]; - weathers[inx].humidity = (int)jsonBuffer["list"][inx]["main"]["humidity"]; - weathers[inx].condition = (const char*)jsonBuffer["list"][inx]["weather"][0]["main"]; - weathers[inx].wind = (float)jsonBuffer["list"][inx]["wind"]["speed"]; - weathers[inx].weatherId = (int)jsonBuffer["list"][inx]["weather"][0]["id"]; - weathers[inx].description = (const char*)jsonBuffer["list"][inx]["weather"][0]["description"]; - weathers[inx].icon = (const char*)jsonBuffer["list"][inx]["weather"][0]["icon"]; + weathers[inx].lon = (float)(*jsonBuffer)["list"][inx]["coord"]["lon"]; + weathers[inx].lat = (float)(*jsonBuffer)["list"][inx]["coord"]["lat"]; + weathers[inx].dt = (long)(*jsonBuffer)["list"][inx]["dt"]; + weathers[inx].city = (const char*)(*jsonBuffer)["list"][inx]["name"]; + weathers[inx].country = (const char*)(*jsonBuffer)["list"][inx]["sys"]["country"]; + weathers[inx].temp = (float)(*jsonBuffer)["list"][inx]["main"]["temp"]; + weathers[inx].humidity = (int)(*jsonBuffer)["list"][inx]["main"]["humidity"]; + weathers[inx].condition = (const char*)(*jsonBuffer)["list"][inx]["weather"][0]["main"]; + weathers[inx].wind = (float)(*jsonBuffer)["list"][inx]["wind"]["speed"]; + weathers[inx].weatherId = (int)(*jsonBuffer)["list"][inx]["weather"][0]["id"]; + weathers[inx].description = (const char*)(*jsonBuffer)["list"][inx]["weather"][0]["description"]; + weathers[inx].icon = (const char*)(*jsonBuffer)["list"][inx]["weather"][0]["icon"]; this->debugController->printLn("lat: " + weathers[inx].lat); this->debugController->printLn("lon: " + weathers[inx].lon); diff --git a/src/Network/OpenWeatherMapClient.h b/src/Network/OpenWeatherMapClient.h index 11dad88..189535f 100644 --- a/src/Network/OpenWeatherMapClient.h +++ b/src/Network/OpenWeatherMapClient.h @@ -3,6 +3,7 @@ #include #include #include "../Global/DebugController.h" +#include "JsonRequestClient.h" class OpenWeatherMapClient { private: @@ -36,9 +37,10 @@ private: String roundValue(String value); DebugController *debugController; + JsonRequestClient *jsonRequestClient; public: - OpenWeatherMapClient(String ApiKey, int CityID, int cityCount, boolean isMetric, String language, DebugController *debugController); + OpenWeatherMapClient(String ApiKey, int CityID, int cityCount, boolean isMetric, String language, DebugController *debugController, JsonRequestClient *jsonRequestClient); void updateWeather(); void updateWeatherApiKey(String ApiKey); void updateCityId(int CityID);