From b28f49a19503f2bffe617f75842e5de27a7ae757 Mon Sep 17 00:00:00 2001
From: Owen
Date: Tue, 15 Jan 2019 21:14:09 +0100
Subject: [PATCH] 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 = "
"
" 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;
}
-}
+}