|
@@ -13,6 +13,7 @@ import json
|
13
|
13
|
import sys
|
14
|
14
|
import os
|
15
|
15
|
import time
|
|
16
|
+import string
|
16
|
17
|
import urllib.parse
|
17
|
18
|
import urllib.request
|
18
|
19
|
from os import path
|
|
@@ -22,6 +23,9 @@ from PyQt5.QtGui import QIcon, QPixmap, QImageReader, QDesktopServices
|
22
|
23
|
from PyQt5.QtCore import QCoreApplication, QSettings, QUrl, QTimer, QSize, Qt, QSettings
|
23
|
24
|
|
24
|
25
|
class SettingsWindow(QWidget):
|
|
26
|
+ columns = [ "Hostname", "API Key", "Tool Preheat", "Bed Preheat" ]
|
|
27
|
+ presets = [ "octopi.local", "000000000_API_KEY_HERE_000000000", "0", "0" ]
|
|
28
|
+
|
25
|
29
|
def __init__(self, parent, *args, **kwargs):
|
26
|
30
|
super(SettingsWindow, self).__init__(*args, **kwargs)
|
27
|
31
|
self.parent = parent
|
|
@@ -32,6 +36,15 @@ class SettingsWindow(QWidget):
|
32
|
36
|
box = QVBoxLayout()
|
33
|
37
|
self.setLayout(box)
|
34
|
38
|
|
|
39
|
+ helpText = "Usage:\n"
|
|
40
|
+ helpText += "1st Column: Printer Hostname or IP address\n"
|
|
41
|
+ helpText += "2nd Column: OctoPrint API Key (32 char hexadecimal)\n"
|
|
42
|
+ helpText += "3rd Column: Tool Preheat Temperature (0 to disable)\n"
|
|
43
|
+ helpText += "4th Column: Bed Preheat Temperature (0 to disable)"
|
|
44
|
+ self.helpText = QLabel(helpText)
|
|
45
|
+ box.addWidget(self.helpText, 0)
|
|
46
|
+ box.setAlignment(self.helpText, Qt.AlignHCenter)
|
|
47
|
+
|
35
|
48
|
buttons = QHBoxLayout()
|
36
|
49
|
box.addLayout(buttons, 0)
|
37
|
50
|
|
|
@@ -45,13 +58,16 @@ class SettingsWindow(QWidget):
|
45
|
58
|
|
46
|
59
|
printers = self.parent.readSettings()
|
47
|
60
|
self.rows = len(printers)
|
48
|
|
- self.table = QTableWidget(self.rows, 2)
|
|
61
|
+ self.table = QTableWidget(self.rows, len(self.columns))
|
49
|
62
|
box.addWidget(self.table, 1)
|
50
|
63
|
|
51
|
64
|
for i in range(0, self.rows):
|
52
|
65
|
p = printers[i]
|
53
|
|
- for j in range(0, 2):
|
54
|
|
- item = QTableWidgetItem(p[j])
|
|
66
|
+ for j in range(0, len(self.columns)):
|
|
67
|
+ text = p[j]
|
|
68
|
+ if (j >= 2) and (j <= 3) and (text == None):
|
|
69
|
+ text = "0"
|
|
70
|
+ item = QTableWidgetItem(text)
|
55
|
71
|
self.table.setItem(i, j, item)
|
56
|
72
|
|
57
|
73
|
buttons2 = QHBoxLayout()
|
|
@@ -65,7 +81,7 @@ class SettingsWindow(QWidget):
|
65
|
81
|
self.down.clicked.connect(self.moveDown)
|
66
|
82
|
buttons2.addWidget(self.down)
|
67
|
83
|
|
68
|
|
- self.table.setHorizontalHeaderLabels(["Hostname", "API Key"])
|
|
84
|
+ self.table.setHorizontalHeaderLabels(self.columns)
|
69
|
85
|
self.table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
70
|
86
|
self.table.resizeColumnsToContents()
|
71
|
87
|
|
|
@@ -75,13 +91,46 @@ class SettingsWindow(QWidget):
|
75
|
91
|
def tableToList(self):
|
76
|
92
|
printers = []
|
77
|
93
|
for i in range(0, self.rows):
|
78
|
|
- p = [self.table.item(i, 0).text(), self.table.item(i, 1).text()]
|
|
94
|
+ p = []
|
|
95
|
+ for j in range(0, len(self.columns)):
|
|
96
|
+ text = self.table.item(i, j).text()
|
|
97
|
+ if (j >= 2) and (j <= 3) and (text == "0"):
|
|
98
|
+ text = None
|
|
99
|
+ p.append(text)
|
79
|
100
|
printers.append(p)
|
80
|
101
|
return printers
|
81
|
102
|
|
|
103
|
+ def settingsValid(self, printers):
|
|
104
|
+ for p in printers:
|
|
105
|
+ # p[0] needs to be valid hostname or IP
|
|
106
|
+ # TODO
|
|
107
|
+
|
|
108
|
+ # p[1] needs to be valid API key (hexadecimal, 32 chars)
|
|
109
|
+ if (len(p[1]) != 32) or not all(c in string.hexdigits for c in p[1]):
|
|
110
|
+ return (False, "API Key not 32-digit hexadecimal")
|
|
111
|
+
|
|
112
|
+ # p[2] and p[3] need to be integer temperatures (0...999)
|
|
113
|
+ for s in [ p[2], p[3] ]:
|
|
114
|
+ if s == None:
|
|
115
|
+ s = "0"
|
|
116
|
+ if (len(s) < 1) or (len(s) > 3) or not all(c in string.digits for c in s):
|
|
117
|
+ return (False, "Temperature not a number from 0...999")
|
|
118
|
+ return (True, "")
|
|
119
|
+
|
82
|
120
|
def closeEvent(self, event):
|
83
|
|
- oldPrinters = [item[0:2] for item in self.parent.printers]
|
|
121
|
+ oldPrinters = [item[0:len(self.columns)] for item in self.parent.printers]
|
84
|
122
|
newPrinters = self.tableToList()
|
|
123
|
+
|
|
124
|
+ valid, errorText = self.settingsValid(newPrinters)
|
|
125
|
+ if valid == False:
|
|
126
|
+ r = self.parent.showDialog(self.parent.name + " Settings Invalid", errorText + "!", "Do you want to edit it again?", True, True, False)
|
|
127
|
+ if r == True:
|
|
128
|
+ event.ignore()
|
|
129
|
+ return
|
|
130
|
+ else:
|
|
131
|
+ self.parent.removeSettingsWindow()
|
|
132
|
+ return
|
|
133
|
+
|
85
|
134
|
if oldPrinters != newPrinters:
|
86
|
135
|
r = self.parent.showDialog(self.parent.name + " Settings Changed", "Do you want to save the new list of printers?", "This will restart the application!", True, False, False)
|
87
|
136
|
if r == True:
|
|
@@ -92,8 +141,9 @@ class SettingsWindow(QWidget):
|
92
|
141
|
def addPrinter(self):
|
93
|
142
|
self.rows += 1
|
94
|
143
|
self.table.setRowCount(self.rows)
|
95
|
|
- self.table.setItem(self.rows - 1, 0, QTableWidgetItem("HOSTNAME"))
|
96
|
|
- self.table.setItem(self.rows - 1, 1, QTableWidgetItem("API_KEY"))
|
|
144
|
+ for i in range(0, len(self.columns)):
|
|
145
|
+ item = QTableWidgetItem(self.presets[i])
|
|
146
|
+ self.table.setItem(self.rows - 1, i, item)
|
97
|
147
|
self.table.resizeColumnsToContents()
|
98
|
148
|
|
99
|
149
|
def removePrinter(self):
|
|
@@ -283,9 +333,11 @@ class OctoTray():
|
283
|
333
|
networkTimeout = 2.0 # in s
|
284
|
334
|
|
285
|
335
|
# list of lists, inner lists contain printer data:
|
286
|
|
- # 0=host 1=key (2=system-commands 3=menu 4+=actions)
|
|
336
|
+ # first elements as in SettingsWindow.columns
|
|
337
|
+ # 0=host 1=key 2=tool-preheat 3=bed-preheat
|
|
338
|
+ # rest used for system-commands, menu, actions
|
287
|
339
|
printers = []
|
288
|
|
-
|
|
340
|
+
|
289
|
341
|
statesWithWarning = [
|
290
|
342
|
"Printing", "Pausing", "Paused"
|
291
|
343
|
]
|
|
@@ -343,6 +395,27 @@ class OctoTray():
|
343
|
395
|
p.append(action)
|
344
|
396
|
menu.addAction(action)
|
345
|
397
|
|
|
398
|
+ if (p[2] != None) or (p[3] != None):
|
|
399
|
+ menu.addSeparator()
|
|
400
|
+
|
|
401
|
+ if p[2] != None:
|
|
402
|
+ action = QAction("Preheat Tool")
|
|
403
|
+ action.triggered.connect(lambda chk, x=p: self.printerHeatTool(x))
|
|
404
|
+ p.append(action)
|
|
405
|
+ menu.addAction(action)
|
|
406
|
+
|
|
407
|
+ if p[3] != None:
|
|
408
|
+ action = QAction("Preheat Bed")
|
|
409
|
+ action.triggered.connect(lambda chk, x=p: self.printerHeatBed(x))
|
|
410
|
+ p.append(action)
|
|
411
|
+ menu.addAction(action)
|
|
412
|
+
|
|
413
|
+ if (p[2] != None) or (p[3] != None):
|
|
414
|
+ action = QAction("Cooldown")
|
|
415
|
+ action.triggered.connect(lambda chk, x=p: self.printerCooldown(x))
|
|
416
|
+ p.append(action)
|
|
417
|
+ menu.addAction(action)
|
|
418
|
+
|
346
|
419
|
menu.addSeparator()
|
347
|
420
|
|
348
|
421
|
action = QAction("Get Status")
|
|
@@ -402,6 +475,8 @@ class OctoTray():
|
402
|
475
|
p = []
|
403
|
476
|
p.append(settings.value("host"))
|
404
|
477
|
p.append(settings.value("key"))
|
|
478
|
+ p.append(settings.value("tool_preheat"))
|
|
479
|
+ p.append(settings.value("bed_preheat"))
|
405
|
480
|
printers.append(p)
|
406
|
481
|
settings.endArray()
|
407
|
482
|
return printers
|
|
@@ -415,6 +490,8 @@ class OctoTray():
|
415
|
490
|
settings.setArrayIndex(i)
|
416
|
491
|
settings.setValue("host", p[0])
|
417
|
492
|
settings.setValue("key", p[1])
|
|
493
|
+ settings.setValue("tool_preheat", p[2])
|
|
494
|
+ settings.setValue("bed_preheat", p[3])
|
418
|
495
|
settings.endArray()
|
419
|
496
|
del settings
|
420
|
497
|
|
|
@@ -683,6 +760,34 @@ class OctoTray():
|
683
|
760
|
s += "\n" + t
|
684
|
761
|
self.showDialog("OctoTray Status", s, None, False, warning)
|
685
|
762
|
|
|
763
|
+ def setTemperature(self, host, key, what, temp):
|
|
764
|
+ path = "printer/bed"
|
|
765
|
+ s = "{\"command\": \"target\", \"target\": " + temp + "}"
|
|
766
|
+
|
|
767
|
+ if "tool" in what:
|
|
768
|
+ path = "printer/tool"
|
|
769
|
+ s = "{\"command\": \"target\", \"targets\": {\"" + what + "\": " + temp + "}}"
|
|
770
|
+
|
|
771
|
+ if temp == None:
|
|
772
|
+ temp = 0
|
|
773
|
+
|
|
774
|
+ self.sendPostRequest(host, key, path, s)
|
|
775
|
+
|
|
776
|
+ def printerHeatTool(self, p):
|
|
777
|
+ self.setTemperature(p[0], p[1], "tool0", p[2])
|
|
778
|
+
|
|
779
|
+ def printerHeatBed(self, p):
|
|
780
|
+ self.setTemperature(p[0], p[1], "bed", p[3])
|
|
781
|
+
|
|
782
|
+ def printerCooldown(self, p):
|
|
783
|
+ state = self.getState(p[0], p[1])
|
|
784
|
+ if state in self.statesWithWarning:
|
|
785
|
+ if self.showDialog("OctoTray Warning", "The printer seems to be running currently!", "Do you really want to turn it off?", True, True) == False:
|
|
786
|
+ return
|
|
787
|
+
|
|
788
|
+ self.setTemperature(p[0], p[1], "tool0", 0)
|
|
789
|
+ self.setTemperature(p[0], p[1], "bed", 0)
|
|
790
|
+
|
686
|
791
|
def printerWebcamAction(self, item):
|
687
|
792
|
for cw in self.camWindows:
|
688
|
793
|
if cw.getHost() == item[0]:
|