From b28f49a19503f2bffe617f75842e5de27a7ae757 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 15 Jan 2019 21:14:09 +0100 Subject: [PATCH 1/2] Add a function + option to request PSU status and enter clock/blank mode depending on the PSU status --- README.md | 1 + printermonitor/OctoPrintClient.cpp | 120 ++++++++++++++++++++++++++--- printermonitor/OctoPrintClient.h | 15 +++- printermonitor/Settings.h | 10 +++ printermonitor/printermonitor.ino | 54 ++++++++++--- 5 files changed, 175 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 3e7f920..d41dc61 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ SOFTWARE. * Supports OTA (loading firmware over WiFi connection on same LAN) * Basic Authentication to protect your settings * Version 2.2 added the ability to update firmware through web interface from a compiled binary +* Can query the Octoprint [https://plugins.octoprint.org/plugins/psucontrol/](PSU Control plugin) to enter clock or blank mode when PSU is off * Video: https://youtu.be/niRv9SCgAPk * Detailed build video by Chris Riley: https://youtu.be/Rm-l1FSuJpI diff --git a/printermonitor/OctoPrintClient.cpp b/printermonitor/OctoPrintClient.cpp index b2879ca..4f669ad 100644 --- a/printermonitor/OctoPrintClient.cpp +++ b/printermonitor/OctoPrintClient.cpp @@ -21,13 +21,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/* 15 Jan 2019 : Owen Carter : Add psucontrol query via POST api call */ + #include "OctoPrintClient.h" -OctoPrintClient::OctoPrintClient(String ApiKey, String server, int port, String user, String pass) { - updateOctoPrintClient(ApiKey, server, port, user, pass); +OctoPrintClient::OctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { + updateOctoPrintClient(ApiKey, server, port, user, pass, psu); } -void OctoPrintClient::updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass) { +void OctoPrintClient::updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu) { server.toCharArray(myServer, 100); myApiKey = ApiKey; myPort = port; @@ -37,6 +39,7 @@ void OctoPrintClient::updateOctoPrintClient(String ApiKey, String server, int po base64 b64; encodedAuth = b64.encode(userpass, true); } + pollPsu = psu; } boolean OctoPrintClient::validate() { @@ -58,7 +61,7 @@ WiFiClient OctoPrintClient::getSubmitRequest(String apiGetData) { WiFiClient printClient; printClient.setTimeout(5000); - Serial.println("Getting Octoprint Data"); + Serial.println("Getting Octoprint Data via GET"); Serial.println(apiGetData); result = ""; if (printClient.connect(myServer, myPort)) { //starts client connection, checks for connection @@ -109,18 +112,76 @@ WiFiClient OctoPrintClient::getSubmitRequest(String apiGetData) { return printClient; } +WiFiClient OctoPrintClient::getPostRequest(String apiPostData, String apiPostBody) { + WiFiClient printClient; + printClient.setTimeout(5000); + + Serial.println("Getting Octoprint Data via POST"); + Serial.println(apiPostData + " | " + apiPostBody); + result = ""; + if (printClient.connect(myServer, myPort)) { //starts client connection, checks for connection + printClient.println(apiPostData); + printClient.println("Host: " + String(myServer) + ":" + String(myPort)); + printClient.println("Connection: close"); + printClient.println("X-Api-Key: " + myApiKey); + if (encodedAuth != "") { + printClient.print("Authorization: "); + printClient.println("Basic " + encodedAuth); + } + printClient.println("User-Agent: ArduinoWiFi/1.1"); + printClient.println("Content-Type: application/json"); + printClient.print("Content-Length: "); + printClient.println(apiPostBody.length()); + printClient.println(); + printClient.println(apiPostBody); + 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 OctoPrint failed: " + String(myServer) + ":" + String(myPort)); //error message if no client connect + Serial.println(); + resetPrintData(); + printerData.error = "Connection to OctoPrint failed: " + String(myServer) + ":" + String(myPort); + return printClient; + } + + // Check HTTP status + char status[32] = {0}; + printClient.readBytesUntil('\r', status, sizeof(status)); + if (strcmp(status, "HTTP/1.1 200 OK") != 0 && strcmp(status, "HTTP/1.1 409 CONFLICT") != 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 OctoPrintClient::getPrinterJobResults() { if (!validate()) { return; } + //**** get the Printer Job status String apiGetData = "GET /api/job HTTP/1.1"; - 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); @@ -148,7 +209,7 @@ void OctoPrintClient::getPrinterJobResults() { if (isOperational()) { Serial.println("Status: " + printerData.state); } else { - Serial.println("Printer Not Opperational"); + Serial.println("Printer Not Operational"); } //**** get the Printer Temps and Stat @@ -183,8 +244,42 @@ void OctoPrintClient::getPrinterJobResults() { if (isPrinting()) { Serial.println("Status: " + printerData.state + " " + printerData.fileName + "(" + printerData.progressCompletion + "%)"); } +} + +void OctoPrintClient::getPrinterPsuState() { + //**** get the PSU state (if enabled and printer operational) + if (pollPsu && isOperational()) { + if (!validate()) { + printerData.isPSUoff = false; // we do not know PSU state, so assume on. + return; + } + String apiPostData = "POST /api/plugin/psucontrol HTTP/1.1"; + String apiPostBody = "{\"command\":\"getPSUState\"}"; + WiFiClient printClient = getPostRequest(apiPostData,apiPostBody); + if (printerData.error != "") { + printerData.isPSUoff = false; // we do not know PSU state, so assume on. + return; + } + const size_t bufferSize3 = JSON_OBJECT_SIZE(2) + 300; + DynamicJsonBuffer jsonBuffer3(bufferSize3); - printClient.stop(); //stop client + // Parse JSON object + JsonObject& root3 = jsonBuffer3.parseObject(printClient); + if (!root3.success()) { + printerData.isPSUoff = false; // we do not know PSU state, so assume on + return; + } + + String psu = (const char*)root3["isPSUOn"]; + if (psu == "true") { + printerData.isPSUoff = false; // PSU checked and is on + } else { + printerData.isPSUoff = true; // PSU checked and is off, set flag + } + printClient.stop(); //stop client + } else { + printerData.isPSUoff = false; // we are not checking PSU state, so assume on + } } // Reset all PrinterData @@ -205,6 +300,7 @@ void OctoPrintClient::resetPrintData() { printerData.bedTemp = ""; printerData.bedTargetTemp = ""; printerData.isPrinting = false; + printerData.isPSUoff = false; printerData.error = ""; } @@ -256,6 +352,10 @@ boolean OctoPrintClient::isPrinting() { return printerData.isPrinting; } +boolean OctoPrintClient::isPSUoff() { + return printerData.isPSUoff; +} + boolean OctoPrintClient::isOperational() { boolean operational = false; if (printerData.state == "Operational" || isPrinting()) { @@ -292,4 +392,4 @@ String OctoPrintClient::getValueRounded(String value) { float f = value.toFloat(); int rounded = (int)(f+0.5f); return String(rounded); -} +} diff --git a/printermonitor/OctoPrintClient.h b/printermonitor/OctoPrintClient.h index 1d02836..3335536 100644 --- a/printermonitor/OctoPrintClient.h +++ b/printermonitor/OctoPrintClient.h @@ -21,6 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/* 15 Jan 2019 : Owen Carter : Add psucontrol query via POST api call */ + #pragma once #include #include @@ -33,11 +35,13 @@ private: int myPort = 80; String myApiKey = ""; String encodedAuth = ""; + boolean pollPsu; void resetPrintData(); boolean validate(); WiFiClient getSubmitRequest(String apiGetData); - + WiFiClient getPostRequest(String apiPostData, String apiPostBody); + String result; typedef struct { @@ -57,6 +61,7 @@ private: String bedTemp; String bedTargetTemp; boolean isPrinting; + boolean isPSUoff; String error; } PrinterStruct; @@ -64,9 +69,10 @@ private: public: - OctoPrintClient(String ApiKey, String server, int port, String user, String pass); + OctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); void getPrinterJobResults(); - void updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass); + void getPrinterPsuState(); + void updateOctoPrintClient(String ApiKey, String server, int port, String user, String pass, boolean psu); String getAveragePrintTime(); String getEstimatedPrintTime(); @@ -80,6 +86,7 @@ public: String getState(); boolean isPrinting(); boolean isOperational(); + boolean isPSUoff(); String getTempBedActual(); String getTempBedTarget(); String getTempToolActual(); @@ -88,4 +95,4 @@ public: String getValueRounded(String value); String getError(); }; - + diff --git a/printermonitor/Settings.h b/printermonitor/Settings.h index c064400..27f1fc2 100644 --- a/printermonitor/Settings.h +++ b/printermonitor/Settings.h @@ -21,6 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/* 15 Jan 2019 : Owen Carter : Add psucontrol setting */ + /****************************************************************************** * Printer Monitor is designed for the Wemos D1 ESP8266 * Wemos D1 Mini: https://amzn.to/2qLyKJd @@ -71,11 +73,14 @@ boolean IS_METRIC = false; // false = Imperial and true = Metric // Languages: ar, bg, ca, cz, de, el, en, fa, fi, fr, gl, hr, hu, it, ja, kr, la, lt, mk, nl, pl, pt, ro, ru, se, sk, sl, es, tr, ua, vi, zh_cn, zh_tw String WeatherLanguage = "en"; //Default (en) English +// Webserver const int WEBSERVER_PORT = 80; // The port you can access this device on over HTTP const boolean WEBSERVER_ENABLED = true; // Device will provide a web interface via http://[ip]:[port]/ boolean IS_BASIC_AUTH = true; // true = require athentication to change configuration settings / false = no auth char* www_username = "admin"; // User account for the Web Interface char* www_password = "password"; // Password for the Web Interface + +// Date and Time float UtcOffset = -7; // Hour offset from GMT for your timezone boolean IS_24HOUR = false; // 23:00 millitary 24 hour clock int minutesBetweenDataRefresh = 15; @@ -91,8 +96,13 @@ boolean INVERT_DISPLAY = false; // true = pins at top | false = pins at the bott // LED Settings const int externalLight = LED_BUILTIN; // Set to unused pin, like D1, to disable use of built-in LED (LED_BUILTIN) +// PSU Control +boolean HAS_PSU = false; // Set to true if https://github.com/kantlivelong/OctoPrint-PSUControl/ in use + +// OTA Updates boolean ENABLE_OTA = true; // this will allow you to load firmware to the device over WiFi (see OTA for ESP8266) String OTA_Password = ""; // Set an OTA password here -- leave blank if you don't want to be prompted for password + //****************************** // End Settings //****************************** diff --git a/printermonitor/printermonitor.ino b/printermonitor/printermonitor.ino index 21e201b..b5af388 100644 --- a/printermonitor/printermonitor.ino +++ b/printermonitor/printermonitor.ino @@ -21,6 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/* 15 Jan 2019 : Owen Carter : Add psucontrol option and processing */ + /********************************************** * Edit Settings.h for personalization ***********************************************/ @@ -82,7 +84,7 @@ String lastReportStatus = ""; boolean displayOn = true; // OctoPrint Client -OctoPrintClient printerClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort, OctoAuthUser, OctoAuthPass); +OctoPrintClient printerClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort, OctoAuthUser, OctoAuthPass, HAS_PSU); int printerCount = 0; // Weather Client @@ -113,6 +115,7 @@ String CHANGE_FORM = "
Use 24 Hour Clock (military time)

" "

Flip display orientation

" + "

Use OctoPrint PSU control plugin for clock/blank

" "

Clock Sync / Weather Refresh (minutes)

" "

Theme Color

" "


" @@ -205,7 +208,7 @@ void setup() { readSettings(); - // initialize dispaly + // initialize display display.init(); if (INVERT_DISPLAY) { display.flipScreenVertically(); // connections at top of OLED display @@ -367,6 +370,7 @@ void loop() { digitalWrite(externalLight, LOW); lastMinute = timeClient.getMinutes(); // reset the check value printerClient.getPrinterJobResults(); + printerClient.getPrinterPsuState(); digitalWrite(externalLight, HIGH); } else if (printerClient.isPrinting()) { if (lastSecond != timeClient.getSeconds() && timeClient.getSeconds().endsWith("0")) { @@ -374,6 +378,7 @@ void loop() { // every 10 seconds while printing get an update digitalWrite(externalLight, LOW); printerClient.getPrinterJobResults(); + printerClient.getPrinterPsuState(); digitalWrite(externalLight, HIGH); } } @@ -456,6 +461,7 @@ void handleUpdateConfig() { DISPLAYCLOCK = server.hasArg("isClockEnabled"); IS_24HOUR = server.hasArg("is24hour"); INVERT_DISPLAY = server.hasArg("invDisp"); + HAS_PSU = server.hasArg("hasPSU"); minutesBetweenDataRefresh = server.arg("refresh").toInt(); themeColor = server.arg("theme"); UtcOffset = server.arg("utcoffset").toFloat(); @@ -466,6 +472,7 @@ void handleUpdateConfig() { writeSettings(); findMDNS(); printerClient.getPrinterJobResults(); + printerClient.getPrinterPsuState(); if (INVERT_DISPLAY != flipOld) { ui.init(); if(INVERT_DISPLAY) @@ -570,6 +577,12 @@ void handleConfigure() { isInvDisp = "checked='checked'"; } form.replace("%IS_INVDISP_CHECKED%", isInvDisp); + String hasPSUchecked = ""; + if (HAS_PSU) { + hasPSUchecked = "checked='checked'"; + } + form.replace("%HAS_PSU_CHECKED%", hasPSUchecked); + String options = ""; options.replace(">"+String(minutesBetweenDataRefresh)+"<", " selected>"+String(minutesBetweenDataRefresh)+"<"); form.replace("%OPTIONS%", options); @@ -695,10 +708,16 @@ void displayPrinterStatus() { html += "

"; html += "Host Name: " + OctoPrintHostName + "
"; if (printerClient.getError() != "") { - html += "Error: " + printerClient.getError() + "
"; + html += "Status: Offline
"; + html += "Reason: " + printerClient.getError() + "
"; + } else { + html += "Status: " + printerClient.getState(); + if (printerClient.isPSUoff() && HAS_PSU) { + html += ", PSU off"; + } + html += "
"; } - html += "Status: " + printerClient.getState() + "
"; - + if (printerClient.isPrinting()) { html += "File: " + printerClient.getFileName() + "
"; float fileSize = printerClient.getFileSize().toFloat(); @@ -941,9 +960,17 @@ void drawClockHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { if (!IS_24HOUR) { display->drawString(0, 48, timeClient.getAmPm()); display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(64, 48, "offline"); + if (printerClient.isPSUoff()) { + display->drawString(64, 47, "psu off"); + } else { + display->drawString(64, 47, "offline"); + } } else { - display->drawString(0,48, "offline"); + if (printerClient.isPSUoff()) { + display->drawString(0, 47, "psu off"); + } else { + display->drawString(0, 47, "offline"); + } } display->setTextAlignment(TEXT_ALIGN_LEFT); display->drawRect(0, 43, 128, 2); @@ -1004,6 +1031,7 @@ void writeSettings() { f.println("CityID=" + String(CityIDs[0])); f.println("isMetric=" + String(IS_METRIC)); f.println("language=" + String(WeatherLanguage)); + f.println("hasPSU=" + String(HAS_PSU)); } f.close(); readSettings(); @@ -1091,6 +1119,10 @@ void readSettings() { INVERT_DISPLAY = line.substring(line.lastIndexOf("invertDisp=") + 11).toInt(); Serial.println("INVERT_DISPLAY=" + String(INVERT_DISPLAY)); } + if (line.indexOf("hasPSU=") >= 0) { + HAS_PSU = line.substring(line.lastIndexOf("hasPSU=") + 7).toInt(); + Serial.println("HAS_PSU=" + String(HAS_PSU)); + } if (line.indexOf("isWeather=") >= 0) { DISPLAYWEATHER = line.substring(line.lastIndexOf("isWeather=") + 10).toInt(); Serial.println("DISPLAYWEATHER=" + String(DISPLAYWEATHER)); @@ -1115,7 +1147,7 @@ void readSettings() { } } fr.close(); - printerClient.updateOctoPrintClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort, OctoAuthUser, OctoAuthPass); + printerClient.updateOctoPrintClient(OctoPrintApiKey, OctoPrintServer, OctoPrintPort, OctoAuthUser, OctoAuthPass, HAS_PSU); weatherClient.updateWeatherApiKey(WeatherApiKey); weatherClient.updateLanguage(WeatherLanguage); weatherClient.setMetric(IS_METRIC); @@ -1167,7 +1199,7 @@ void checkDisplay() { return; } } else if (DISPLAYCLOCK) { - if (!printerClient.isOperational() && !isClockOn) { + if ((!printerClient.isOperational() || printerClient.isPSUoff()) && !isClockOn) { Serial.println("Clock Mode is turned on."); if (!DISPLAYWEATHER) { ui.disableAutoTransition(); @@ -1181,7 +1213,7 @@ void checkDisplay() { } ui.setOverlays(clockOverlay, numberOfOverlays); isClockOn = true; - } else if (printerClient.isOperational() && isClockOn) { + } else if (printerClient.isOperational() && !printerClient.isPSUoff() && isClockOn) { Serial.println("Printer Monitor is active."); ui.setFrames(frames, numberOfFrames); ui.setOverlays(overlays, numberOfOverlays); @@ -1206,4 +1238,4 @@ void enableDisplay(boolean enable) { Serial.println("Display was turned OFF: " + timeClient.getFormattedTime()); displayOffEpoch = lastEpoch; } -} +} From 8283e0ca85cd8de239a6c880455925ae61fe4535 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 15 Jan 2019 22:03:40 +0100 Subject: [PATCH 2/2] Correct the readme link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d41dc61..d3e464d 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ SOFTWARE. * Supports OTA (loading firmware over WiFi connection on same LAN) * Basic Authentication to protect your settings * Version 2.2 added the ability to update firmware through web interface from a compiled binary -* Can query the Octoprint [https://plugins.octoprint.org/plugins/psucontrol/](PSU Control plugin) to enter clock or blank mode when PSU is off +* Can query the Octoprint [PSU Control plugin](https://plugins.octoprint.org/plugins/psucontrol/) to enter clock or blank mode when PSU is off * Video: https://youtu.be/niRv9SCgAPk * Detailed build video by Chris Riley: https://youtu.be/Rm-l1FSuJpI