diff --git a/printermonitor/RepetierClient.cpp b/printermonitor/RepetierClient.cpp new file mode 100644 index 0000000..37da3af --- /dev/null +++ b/printermonitor/RepetierClient.cpp @@ -0,0 +1,339 @@ +/** The MIT License (MIT) + +Copyright (c) 2018 David Payne + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Additional Contributions: +/* 15 Jan 2019 : Owen Carter : Add psucontrol query via POST api call */ +/* 07 April 2019 : Jon Smith : Redesigned this class for Repetier Server */ + +#include "RepetierClient.h" + +RepetierClient::RepetierClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { + updateOctoPrintClient(ApiKey, server, port, user, pass, psu); +} + +void RepetierClient::updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { + server.toCharArray(myServer, 100); + myApiKey = ApiKey; + myPort = port; + encodedAuth = ""; + if (user != "") { + String userpass = user + ":" + pass; + base64 b64; + encodedAuth = b64.encode(userpass, true); + } + pollPsu = psu; +} + +boolean RepetierClient::validate() { + boolean rtnValue = false; + printerData.error = ""; + if (String(myServer) == "") { + printerData.error += "Server address is required; "; + } + if (myApiKey == "") { + printerData.error += "ApiKey is required; "; + } + if (printerData.error == "") { + rtnValue = true; + } + return rtnValue; +} + +WiFiClient RepetierClient::getSubmitRequest(String apiGetData) { + WiFiClient printClient; + printClient.setTimeout(5000); + + Serial.println("Getting Repetier Data via GET"); + Serial.println(apiGetData); + result = ""; + if (printClient.connect(myServer, myPort)) { //starts client connection, checks for connection + printClient.println(apiGetData); + printClient.println("Host: " + String(myServer) + ":" + String(myPort)); + printClient.println("X-Api-Key: " + myApiKey); + if (encodedAuth != "") { + printClient.print("Authorization: "); + printClient.println("Basic " + encodedAuth); + } + printClient.println("User-Agent: ArduinoWiFi/1.1"); + printClient.println("Connection: close"); + if (printClient.println() == 0) { + Serial.println("Connection to " + String(myServer) + ":" + String(myPort) + " failed."); + Serial.println(); + resetPrintData(); + printerData.error = "Connection to " + String(myServer) + ":" + String(myPort) + " failed."; + return printClient; + } + } + else { + Serial.println("Connection to Repeteir failed: " + String(myServer) + ":" + String(myPort)); //error message if no client connect + Serial.println(); + resetPrintData(); + printerData.error = "Connection to Repetier failed: " + String(myServer) + ":" + String(myPort); + return printClient; + } + + // Check HTTP status + char status[32] = {0}; + printClient.readBytesUntil('\r', status, sizeof(status)); + if (strcmp(status, "Host: 200 OK") != 0) { + Serial.print(F("Unexpected response: ")); + Serial.println(status); + printerData.state = ""; + printerData.error = "Response: " + String(status); + return printClient; + } + + // Skip HTTP headers + char endOfHeaders[] = "\r\n\r\n"; + if (!printClient.find(endOfHeaders)) { + Serial.println(F("Invalid response")); + printerData.error = "Invalid response from " + String(myServer) + ":" + String(myPort); + printerData.state = ""; + } + + return printClient; +} + + +void RepetierClient::getPrinterJobResults() { + if (!validate()) { + return; + } + //**** get the Printer Job status + String apiGetData = "GET /printer/api/?a=listPrinter"; + WiFiClient printClient = getSubmitRequest(apiGetData); + if (printerData.error != "") { + return; + } + const size_t bufferSize = JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 2*JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 710; + DynamicJsonBuffer jsonBuffer(bufferSize); + + // Parse JSON object + JsonArray& root = jsonBuffer.parseArray(printClient); + + + if (!root.success()) { + Serial.println("Repetier Data Parsing failed: " + String(myServer) + ":" + String(myPort)); + printerData.error = "Repetier Data Parsing failed: " + String(myServer) + ":" + String(myPort); + printerData.state = ""; + return; + } + + ///Selecting First printer + JsonObject& pr = root[0]; + + //printerData.averagePrintTime = (const char*)pr[""]; + printerData.estimatedPrintTime = (const char*)pr["printTime"]; + printerData.fileName = (const char*) pr["job"]; + printerData.fileSize = (const char*) pr["totalLines"]; + //printerData.filamentLength = (const char*) pr[""]; + printerData.state = (const char*) pr["online"]; + //printerData.lastPrintTime = (const char*) pr[""]; + printerData.progressCompletion = (const char*) pr["done"]; + printerData.progressFilepos = (const char*) pr["linesSend"]; + printerData.progressPrintTime = (const char*) pr["printedTimeComp"]; + +//Figure out Time Left + long timeTot=0; + long timeElap=0; + long timeLeft=0; + if (printerData.estimatedPrintTime != "" ) + {timeTot = atol(pr["printTime"]);} + if (printerData.progressPrintTime != "") + {timeElap= atol(pr["printedTimeComp"]);} + timeLeft = timeTot-timeElap; + printerData.progressPrintTimeLeft =String(timeLeft); + + + + String printing = (const char*) pr["job"]; + if (printing != "none") { + printerData.isPrinting = true; + }else {printerData.isPrinting=false;} + +Serial.println("PT:"+printerData.progressPrintTime); +Serial.println("PTC:"+ printerData.estimatedPrintTime); +Serial.println("ST:"+ printerData.lastPrintTime); +Serial.println("TimeLeft: "+printerData.progressPrintTimeLeft); + + if (printerData.isPrinting) + { Serial.println("I think I am printing");} + + if (isOperational()) { + Serial.println("Status: " + printerData.state); + } else { + Serial.println("Printer Not Operational"); + } + + //**** get the Printer Temps and Stat + apiGetData = "GET /printer/api/?a=stateList"; + printClient = getSubmitRequest(apiGetData); + if (printerData.error != "") { + return; + } + const size_t bufferSize2 = 3*JSON_OBJECT_SIZE(2) + 2*JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(9) + 300; + DynamicJsonBuffer jsonBuffer2(bufferSize2); + + //Parse JSON object + JsonObject& root2 = jsonBuffer2.parseObject(printClient); + + //Select printer + String slug=(const char*) pr["slug"]; + JsonObject& pr2 = root2[slug]; + + if (!root2.success()) { + printerData.isPrinting = false; + printerData.toolTemp = ""; + printerData.toolTargetTemp = ""; + printerData.bedTemp = ""; + printerData.bedTargetTemp = (const char*) pr2["heatBeds"][0]["tempSet"]; + return; + } + + + printerData.toolTemp = (const char*) pr2["extruder"][0]["tempRead"]; + printerData.toolTargetTemp = (const char*) pr2["extruder"][0]["tempSet"]; + printerData.bedTemp = (const char*) pr2["heatedBeds"][0]["tempRead"]; + printerData.bedTargetTemp = (const char*) pr2["heatedBeds"][0]["tempSet"]; + + if (printerData.isPrinting) { + Serial.println("Status: " + printerData.state + " " + printerData.fileName + "(" + printerData.progressCompletion + "%)"); + } +} + +void RepetierClient::getPrinterPsuState() { + //**** get the PSU state (if enabled and printer operational) + //Not implemented in Repetier Server AFAIK +} + +// Reset all PrinterData +void RepetierClient::resetPrintData() { + printerData.averagePrintTime = ""; + printerData.estimatedPrintTime = ""; + printerData.fileName = ""; + printerData.fileSize = ""; + printerData.lastPrintTime = ""; + printerData.progressCompletion = ""; + printerData.progressFilepos = ""; + printerData.progressPrintTime = ""; + printerData.progressPrintTimeLeft = ""; + printerData.state = ""; + printerData.toolTemp = ""; + printerData.toolTargetTemp = ""; + printerData.filamentLength = ""; + printerData.bedTemp = ""; + printerData.bedTargetTemp = ""; + printerData.isPrinting = false; + printerData.isPSUoff = false; + printerData.error = ""; +} + +String RepetierClient::getAveragePrintTime(){ + return printerData.averagePrintTime; +} + +String RepetierClient::getEstimatedPrintTime() { + return printerData.estimatedPrintTime; +} + +String RepetierClient::getFileName() { + return printerData.fileName; +} + +String RepetierClient::getFileSize() { + return printerData.fileSize; +} + +String RepetierClient::getLastPrintTime(){ + return printerData.lastPrintTime; +} + +String RepetierClient::getProgressCompletion() { + return String(printerData.progressCompletion.toInt()); +} + +String RepetierClient::getProgressFilepos() { + return printerData.progressFilepos; +} + +String RepetierClient::getProgressPrintTime() { + return printerData.progressPrintTime; +} + +String RepetierClient::getProgressPrintTimeLeft() { + String rtnValue = printerData.progressPrintTimeLeft; + if (getProgressCompletion() == "100") { + rtnValue = "0"; // Print is done so this should be 0 this is a fix for OctoPrint + } + return rtnValue; +} + +String RepetierClient::getState() { + return printerData.state; +} + +boolean RepetierClient::isPrinting() { + return printerData.isPrinting; +} + +boolean RepetierClient::isPSUoff() { + return printerData.isPSUoff; +} + +boolean RepetierClient::isOperational() { + boolean operational = false; + if (printerData.state == "Operational" || isPrinting()) { + operational = true; + } + return operational; +} + +String RepetierClient::getTempBedActual() { + return printerData.bedTemp; +} + +String RepetierClient::getTempBedTarget() { + return printerData.bedTargetTemp; +} + +String RepetierClient::getTempToolActual() { + return printerData.toolTemp; +} + +String RepetierClient::getTempToolTarget() { + return printerData.toolTargetTemp; +} + +String RepetierClient::getFilamentLength() { + return printerData.filamentLength; +} + +String RepetierClient::getError() { + return printerData.error; +} + +String RepetierClient::getValueRounded(String value) { + float f = value.toFloat(); + int rounded = (int)(f+0.5f); + return String(rounded); +} diff --git a/printermonitor/RepetierClient.h b/printermonitor/RepetierClient.h new file mode 100644 index 0000000..b615506 --- /dev/null +++ b/printermonitor/RepetierClient.h @@ -0,0 +1,98 @@ +/** The MIT License (MIT) + +Copyright (c) 2018 David Payne + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Additional Contributions: +/* 15 Jan 2019 : Owen Carter : Add psucontrol query via POST api call */ + +#pragma once +#include +#include "libs/ArduinoJson/ArduinoJson.h" +#include + +class RepetierClient { + +private: + char myServer[100]; + int myPort = 3344; + String myApiKey = ""; + String encodedAuth = ""; + boolean pollPsu; + + void resetPrintData(); + boolean validate(); + WiFiClient getSubmitRequest(String apiGetData); + WiFiClient getPostRequest(String apiPostData, String apiPostBody); + + String result; + + typedef struct { + String averagePrintTime; + String estimatedPrintTime; + String fileName; + String fileSize; + String lastPrintTime; + String progressCompletion; + String progressFilepos; + String progressPrintTime; + String progressPrintTimeLeft; + String state; + String toolTemp; + String toolTargetTemp; + String filamentLength; + String bedTemp; + String bedTargetTemp; + boolean isPrinting; + boolean isPSUoff; + String error; + } PrinterStruct; + + PrinterStruct printerData; + + +public: + RepetierClient(String ApiKey, String server, int port, String user, String pass, boolean psu); + void getPrinterJobResults(); + void getPrinterPsuState(); + void updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); + + String getAveragePrintTime(); + String getEstimatedPrintTime(); + String getFileName(); + String getFileSize(); + String getLastPrintTime(); + String getProgressCompletion(); + String getProgressFilepos(); + String getProgressPrintTime(); + String getProgressPrintTimeLeft(); + String getState(); + boolean isPrinting(); + boolean isOperational(); + boolean isPSUoff(); + String getTempBedActual(); + String getTempBedTarget(); + String getTempToolActual(); + String getTempToolTarget(); + String getFilamentLength(); + String getValueRounded(String value); + String getError(); +};