Project opens websocket and reconnects if ping replies from the printer stop
This commit is contained in:
parent
ab849a721b
commit
d084ebb1ac
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */; };
|
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */; };
|
||||||
|
E16378B229A43CE1002F05E9 /* KlipperMonScratchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16378B129A43CE1002F05E9 /* KlipperMonScratchTests.swift */; };
|
||||||
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */; };
|
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */; };
|
||||||
E180B5ED2992CD9200425DB0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E180B5EC2992CD9200425DB0 /* Assets.xcassets */; };
|
E180B5ED2992CD9200425DB0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E180B5EC2992CD9200425DB0 /* Assets.xcassets */; };
|
||||||
E180B5F02992CD9200425DB0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E180B5EF2992CD9200425DB0 /* Preview Assets.xcassets */; };
|
E180B5F02992CD9200425DB0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E180B5EF2992CD9200425DB0 /* Preview Assets.xcassets */; };
|
||||||
@ -20,6 +21,7 @@
|
|||||||
E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */; };
|
E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */; };
|
||||||
E180B6222993256E00425DB0 /* PrinterRequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6212993256E00425DB0 /* PrinterRequestManager.swift */; };
|
E180B6222993256E00425DB0 /* PrinterRequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6212993256E00425DB0 /* PrinterRequestManager.swift */; };
|
||||||
E1E8B07729949E2700BABE4B /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = E1E8B07629949E2700BABE4B /* Starscream */; };
|
E1E8B07729949E2700BABE4B /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = E1E8B07629949E2700BABE4B /* Starscream */; };
|
||||||
|
E1E8B07929955ABE00BABE4B /* PrinterStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E8B07829955ABE00BABE4B /* PrinterStats.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -42,6 +44,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
E124B9D72993FE5500C0D2D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
E124B9D72993FE5500C0D2D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterConfigView.swift; sourceTree = "<group>"; };
|
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterConfigView.swift; sourceTree = "<group>"; };
|
||||||
|
E16378B129A43CE1002F05E9 /* KlipperMonScratchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonScratchTests.swift; sourceTree = "<group>"; };
|
||||||
E180B5E52992CD9100425DB0 /* KlipperMon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KlipperMon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
E180B5E52992CD9100425DB0 /* KlipperMon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KlipperMon.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonApp.swift; sourceTree = "<group>"; };
|
E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonApp.swift; sourceTree = "<group>"; };
|
||||||
E180B5EC2992CD9200425DB0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
E180B5EC2992CD9200425DB0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -57,6 +60,7 @@
|
|||||||
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterObjectsQuery.swift; sourceTree = "<group>"; };
|
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterObjectsQuery.swift; sourceTree = "<group>"; };
|
||||||
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonMenuBarExtraView.swift; sourceTree = "<group>"; };
|
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonMenuBarExtraView.swift; sourceTree = "<group>"; };
|
||||||
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterRequestManager.swift; sourceTree = "<group>"; };
|
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterRequestManager.swift; sourceTree = "<group>"; };
|
||||||
|
E1E8B07829955ABE00BABE4B /* PrinterStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterStats.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -119,6 +123,7 @@
|
|||||||
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */,
|
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */,
|
||||||
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */,
|
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */,
|
||||||
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */,
|
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */,
|
||||||
|
E1E8B07829955ABE00BABE4B /* PrinterStats.swift */,
|
||||||
);
|
);
|
||||||
path = KlipperMon;
|
path = KlipperMon;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -135,6 +140,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */,
|
E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */,
|
||||||
|
E16378B129A43CE1002F05E9 /* KlipperMonScratchTests.swift */,
|
||||||
);
|
);
|
||||||
path = KlipperMonTests;
|
path = KlipperMonTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -284,6 +290,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
E1E8B07929955ABE00BABE4B /* PrinterStats.swift in Sources */,
|
||||||
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */,
|
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */,
|
||||||
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
||||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */,
|
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */,
|
||||||
@ -299,6 +306,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E180B6002992CD9300425DB0 /* KlipperMonTests.swift in Sources */,
|
E180B6002992CD9300425DB0 /* KlipperMonTests.swift in Sources */,
|
||||||
|
E16378B229A43CE1002F05E9 /* KlipperMonScratchTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<false/>
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string>_moonraker._tcp.</string>
|
<string>_moonraker._tcp.</string>
|
||||||
|
@ -14,16 +14,16 @@ struct KlipperMonMenuBarApp: App {
|
|||||||
@State var currentIcon = "move.3d"
|
@State var currentIcon = "move.3d"
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup(id: "floating-stats") {
|
// WindowGroup(id: "floating-stats") {
|
||||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
// KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
// .environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||||
//.frame(width: 300, height: 140)
|
// }
|
||||||
}
|
|
||||||
//.windowResizability(.contentSize)
|
|
||||||
|
|
||||||
Window("Configuration", id: "soyuz_cfg", content: {
|
WindowGroup("Configuration", id: "soyuz_cfg", content: {
|
||||||
PrinterConfigView()
|
PrinterConfigView()
|
||||||
|
//.frame(minWidth: 300, maxWidth: 600, minHeight: 60, maxHeight: 100)
|
||||||
})
|
})
|
||||||
|
.windowResizability(.contentSize)
|
||||||
|
|
||||||
MenuBarExtra("Soyuz", systemImage: currentIcon) {
|
MenuBarExtra("Soyuz", systemImage: currentIcon) {
|
||||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||||
|
@ -7,129 +7,94 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import Network
|
||||||
|
|
||||||
struct KlipperMonMenuBarExtraView: View {
|
struct KlipperMonMenuBarExtraView: View {
|
||||||
|
// The threshhold considered a burn-risk, at which point certain UI elements turn red.
|
||||||
let DANGERTEMP = 40.0
|
let DANGERTEMP = 40.0
|
||||||
|
|
||||||
@Environment(\.openWindow) var openWindow
|
@Environment(\.openWindow) var openWindow
|
||||||
|
|
||||||
@ObservedObject var printerManager = PrinterRequestManager.shared
|
@ObservedObject var printerManager = PrinterRequestManager.shared
|
||||||
|
|
||||||
@State var printerObjectsQuery: PrinterObjectsQuery?
|
|
||||||
@State var printPercentage: Double = 0
|
@State var printPercentage: Double = 0
|
||||||
|
|
||||||
// TODO: Don't forget, create @State variable for printer status (i.e. "Printing", etc)
|
|
||||||
// and programmatically add a "connecting" section
|
|
||||||
@State var printerStatus: String = ""
|
|
||||||
|
|
||||||
@Binding var currentMenuBarIcon: String
|
@Binding var currentMenuBarIcon: String
|
||||||
|
|
||||||
@State var hotendHotTemp: Bool = false
|
@State var hotendHotTemp: Bool = false
|
||||||
@State var bedHotTemp: Bool = false
|
@State var bedHotTemp: Bool = false
|
||||||
|
|
||||||
//@State var nwBrowserDiscoveredItems: [NWEndpoint] = []
|
|
||||||
|
|
||||||
//var nwBrowser = NWBrowser(for: .bonjour(type: "_moonraker._tcp.", domain: "local."), using: .tcp)
|
|
||||||
|
|
||||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
|
||||||
|
|
||||||
// TODO: Use @published API data instead of instance state variable
|
// TODO: Use @published API data instead of instance state variable
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
// Printer Readouts
|
// Printer Readouts
|
||||||
if let queryResults = printerManager.printerObjectsQuery {
|
//if let printerStats = printerManager.printerStats {
|
||||||
Text(queryResults.result.status.print_stats.state.capitalized)
|
if(printerManager.isConnected) {
|
||||||
.font(.title)
|
VStack {
|
||||||
.padding(4)
|
Text(printerManager.state.capitalized)
|
||||||
// Print information
|
.font(.title)
|
||||||
HStack {
|
.padding(4)
|
||||||
Image(systemName: "pencil.tip")
|
// Print information
|
||||||
.rotationEffect(Angle(degrees: 180))
|
|
||||||
.offset(x: 5.5, y: 4)
|
|
||||||
.font(.system(size: 24))
|
|
||||||
ProgressView(value: queryResults.result.status.virtual_sdcard.progress, total: 1.0)
|
|
||||||
.progressViewStyle(.linear)
|
|
||||||
.offset(x: 10)
|
|
||||||
Text("\(Int(queryResults.result.status.virtual_sdcard.progress * 100))%")
|
|
||||||
.padding(2)
|
|
||||||
.padding([.leading], 8)
|
|
||||||
}
|
|
||||||
// Temperatures
|
|
||||||
HStack {
|
|
||||||
// Hot-end temperature
|
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "flame")
|
Image(systemName: "pencil.tip")
|
||||||
.foregroundColor( hotendHotTemp ? .red : .white )
|
.rotationEffect(Angle(degrees: 180))
|
||||||
.opacity( hotendHotTemp ? 1.0 : 0.3 )
|
.offset(x: 5.5, y: 4)
|
||||||
Text("Hotend")
|
.font(.system(size: 24))
|
||||||
.font(.headline)
|
ProgressView(value: printerManager.progress, total: 1.0)
|
||||||
Spacer()
|
.progressViewStyle(.linear)
|
||||||
Text("\(Int(queryResults.result.status.extruder.temperature))°C")
|
.offset(x: 10)
|
||||||
|
Text("\(Int(printerManager.progress * 100))%")
|
||||||
|
.padding(2)
|
||||||
|
.padding([.leading], 8)
|
||||||
}
|
}
|
||||||
// Bed temperature
|
// Temperatures
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "flame")
|
// Hot-end temperature
|
||||||
.foregroundColor( bedHotTemp ? .red : .white )
|
HStack {
|
||||||
.opacity( bedHotTemp ? 1.0 : 0.3 )
|
Image(systemName: "flame")
|
||||||
Text("Plate")
|
.foregroundColor( printerManager.extruderTemperature > DANGERTEMP ? .red : .white )
|
||||||
.font(.headline)
|
.opacity( printerManager.extruderTemperature > DANGERTEMP ? 1.0 : 0.3 )
|
||||||
Spacer()
|
Text("Hotend")
|
||||||
Text("\(Int(queryResults.result.status.heater_bed.temperature))°C")
|
.font(.headline)
|
||||||
|
Spacer()
|
||||||
|
Text("\(Int(printerManager.extruderTemperature))°C")
|
||||||
|
}
|
||||||
|
// Bed temperature
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "flame")
|
||||||
|
.foregroundColor( printerManager.bedTemperature > DANGERTEMP ? .red : .white )
|
||||||
|
.opacity( printerManager.bedTemperature > DANGERTEMP ? 1.0 : 0.3 )
|
||||||
|
Text("Plate")
|
||||||
|
.font(.headline)
|
||||||
|
Spacer()
|
||||||
|
Text("\(Int(printerManager.bedTemperature))°C")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Divider()
|
||||||
}
|
}
|
||||||
Divider()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minWidth: 220, minHeight: 100)
|
//.frame(minWidth: 220, minHeight: 100)
|
||||||
// .overlay {
|
|
||||||
// if !printerManager.printerCommsOkay {
|
|
||||||
// RoundedRectangle(cornerRadius: 10, style: .circular)
|
|
||||||
// .foregroundColor(.black)
|
|
||||||
// .frame(minWidth: 300, minHeight: 100)
|
|
||||||
// .opacity(0.6)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Footer information
|
// Footer information
|
||||||
HStack {
|
HStack {
|
||||||
Button {
|
Button {
|
||||||
print("Button pressed")
|
print("Button pressed")
|
||||||
openWindow(id: "soyuz_cfg")
|
openWindow(id: "soyuz_cfg")
|
||||||
} label: {
|
} label: {
|
||||||
Text("Server Config")
|
Text("Printers")
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
if(printerManager.printerCommsOkay) {
|
if(printerManager.isConnected) {
|
||||||
Image(systemName: "network")
|
Image(systemName: "network")
|
||||||
Text("Online")
|
Text("Online")
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "exclamationmark.triangle")
|
||||||
Text("Offline")
|
Text("Offline")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(4)
|
.padding(2)
|
||||||
.frame(minWidth: 220, maxWidth: 250)
|
.frame(minWidth: 220, maxWidth: 375)
|
||||||
.onReceive(timer) { input in
|
|
||||||
Task {
|
|
||||||
//await printerManager.queryPrinterStats()
|
|
||||||
|
|
||||||
if let query = printerManager.printerObjectsQuery {
|
|
||||||
hotendHotTemp = (query.result.status.extruder.temperature > DANGERTEMP) ? true : false
|
|
||||||
bedHotTemp = (query.result.status.heater_bed.temperature > DANGERTEMP) ? true : false
|
|
||||||
printerStatus = query.result.status.print_stats.state.capitalized
|
|
||||||
} else {
|
|
||||||
printerStatus = "Connecting..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Testing bonjour stuff
|
|
||||||
ForEach(printerManager.nwBrowserDiscoveredItems, id: \.hashValue) { endpoint in
|
|
||||||
Button {
|
|
||||||
printerManager.resolveBonjourHost(endpoint)
|
|
||||||
} label: {
|
|
||||||
Text(endpoint.debugDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,48 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Network
|
||||||
|
|
||||||
struct PrinterConfigView: View {
|
struct PrinterConfigView: View {
|
||||||
|
@ObservedObject var printerManager = PrinterRequestManager.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text("Config Printer In Here")
|
VStack {
|
||||||
|
if(printerManager.isConnected) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "network")
|
||||||
|
Text(printerManager.connection.endpoint.toFriendlyString())
|
||||||
|
Text("\(printerManager.socketHost):\(printerManager.socketPort)")
|
||||||
|
Button {
|
||||||
|
printerManager.socket?.disconnect()
|
||||||
|
} label: {
|
||||||
|
Text("Disconnect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: 500, height: 80)
|
||||||
|
} else {
|
||||||
|
VStack {
|
||||||
|
Text("Auto-detected Printers")
|
||||||
|
.font(.title)
|
||||||
|
ForEach(printerManager.nwBrowserDiscoveredItems, id: \.hashValue) { result in
|
||||||
|
HStack {
|
||||||
|
Text(result.endpoint.toFriendlyString())
|
||||||
|
Button {
|
||||||
|
printerManager.resolveBonjourHost(result.endpoint)
|
||||||
|
} label: {
|
||||||
|
Text("Connect")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: 500, height: 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,3 +56,12 @@ struct PrinterConfigView_Previews: PreviewProvider {
|
|||||||
PrinterConfigView()
|
PrinterConfigView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NWEndpoint {
|
||||||
|
func toFriendlyString() -> String {
|
||||||
|
let regex = /\.(.+)/
|
||||||
|
let match = self.debugDescription.firstMatch(of: regex)
|
||||||
|
return self.debugDescription.replacingOccurrences(of: match!.0, with: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
// Root struct to decode for REST response
|
||||||
struct PrinterObjectsQuery: Decodable {
|
struct PrinterObjectsQuery: Decodable {
|
||||||
let result: ResultsData
|
let result: ResultsData
|
||||||
}
|
}
|
||||||
@ -16,34 +17,76 @@ struct ResultsData: Decodable {
|
|||||||
let status: StatusData
|
let status: StatusData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Individual update replies for JSON-RPC
|
||||||
|
struct jsonRpcUpdate: Decodable {
|
||||||
|
let method: String?
|
||||||
|
let params: jsonRpcParams
|
||||||
|
}
|
||||||
|
|
||||||
|
struct jsonRpcParams: Decodable {
|
||||||
|
let status: StatusData?
|
||||||
|
let timestamp: Double?
|
||||||
|
|
||||||
|
init(from decoder: Decoder) throws {
|
||||||
|
var container = try decoder.unkeyedContainer()
|
||||||
|
self.status = try container.decode(StatusData.self)
|
||||||
|
self.timestamp = try container.decode(Double.self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// public init(from decoder: Decoder) throws {
|
||||||
|
// let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
//
|
||||||
|
// do {
|
||||||
|
// self = .statusData(try container.decode(StatusData.self, forKey: .statusData))
|
||||||
|
// } catch DecodingError.keyNotFound {
|
||||||
|
// print("Error")
|
||||||
|
// self = .double(try container.decode(Double.self))
|
||||||
|
// //self = .timestamp(try container.decode(Double.self, forKey: .timestamp))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Root structs to decode for JSON-RPC response
|
||||||
|
struct jsonRpcResponse: Decodable {
|
||||||
|
let result: jsonRpcResult
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct jsonRpcResult: Decodable {
|
||||||
|
let eventtime: Double
|
||||||
|
let status: StatusData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared data sub-structs
|
||||||
struct StatusData: Decodable {
|
struct StatusData: Decodable {
|
||||||
let virtual_sdcard: VirtualSDCardData
|
let virtual_sdcard: VirtualSDCardData?
|
||||||
let extruder: ExtruderData
|
let extruder: ExtruderData?
|
||||||
let print_stats: PrintStatsData
|
let print_stats: PrintStatsData?
|
||||||
let heater_bed: HeaterBedData
|
let heater_bed: HeaterBedData?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VirtualSDCardData: Decodable {
|
struct VirtualSDCardData: Decodable {
|
||||||
let file_path: String?
|
let file_path: String?
|
||||||
let progress: Double
|
let progress: Double?
|
||||||
let is_active: Bool
|
let is_active: Bool?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExtruderData: Decodable {
|
struct ExtruderData: Decodable {
|
||||||
let temperature: Double
|
let temperature: Double?
|
||||||
let target: Double
|
let target: Double?
|
||||||
let power: Double
|
let power: Double?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PrintStatsData: Decodable {
|
struct PrintStatsData: Decodable {
|
||||||
let filename: String
|
let filename: String?
|
||||||
let print_duration: Double
|
let print_duration: Double?
|
||||||
let filament_used: Double
|
let filament_used: Double?
|
||||||
let state: String
|
let state: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HeaterBedData: Decodable {
|
struct HeaterBedData: Decodable {
|
||||||
let temperature: Double
|
let temperature: Double?
|
||||||
let target: Double
|
let target: Double?
|
||||||
let power: Double
|
let power: Double?
|
||||||
}
|
}
|
||||||
|
@ -24,81 +24,117 @@ struct JsonRpcRequest: Codable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//@MainActor
|
||||||
class PrinterRequestManager: ObservableObject, WebSocketDelegate {
|
class PrinterRequestManager: ObservableObject, WebSocketDelegate {
|
||||||
func didReceive(event: Starscream.WebSocketEvent, client: Starscream.WebSocket) {
|
let WEBSOCKET_TIMEOUT_INTERVAL: TimeInterval = 60.0
|
||||||
switch event {
|
|
||||||
case .connected(let headers):
|
|
||||||
print("websocket is connected: \(headers)")
|
|
||||||
let jsonRpcRequest = JsonRpcRequest(method: "printer.objects.subscribe",
|
|
||||||
params: ["objects":
|
|
||||||
["extruder": nil,
|
|
||||||
"virtual_sdcard": nil,
|
|
||||||
"heater_bed": nil,
|
|
||||||
"print_stats": nil]
|
|
||||||
])
|
|
||||||
|
|
||||||
print(String(data: try! JSONEncoder().encode(jsonRpcRequest), encoding: .utf8)!)
|
|
||||||
socket.write(data: try! JSONEncoder().encode(jsonRpcRequest), completion: {
|
|
||||||
print("Data transferred.")
|
|
||||||
})
|
|
||||||
case .disconnected(let reason, let code):
|
|
||||||
print("websocket is disconnected: \(reason) with code: \(code)")
|
|
||||||
case .text(let string):
|
|
||||||
print("Received text: \(string)")
|
|
||||||
case .binary(let data):
|
|
||||||
print("Received data: \(data.count)")
|
|
||||||
case .ping(_):
|
|
||||||
break
|
|
||||||
case .pong(_):
|
|
||||||
break
|
|
||||||
case .viabilityChanged(_):
|
|
||||||
break
|
|
||||||
case .reconnectSuggested(_):
|
|
||||||
break
|
|
||||||
case .cancelled:
|
|
||||||
break
|
|
||||||
case .error(let error):
|
|
||||||
print("[error] Starscream: \(error.debugDescription)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// REST query results
|
|
||||||
@Published var printerObjectsQuery: PrinterObjectsQuery?
|
|
||||||
|
|
||||||
// Websocket RPC-JSON endpoints discovered via bonjour
|
|
||||||
@Published var nwBrowserDiscoveredItems: [NWEndpoint] = []
|
|
||||||
|
|
||||||
@Published var printerCommsOkay = false
|
|
||||||
|
|
||||||
var socket: WebSocket!
|
|
||||||
|
|
||||||
private var socketHost, socketPort: String?
|
|
||||||
|
|
||||||
//var nwBrowser: NWBrowser!
|
|
||||||
let nwBrowser = NWBrowser(for: .bonjour(type: "_moonraker._tcp", domain: "local."), using: .tcp)
|
|
||||||
var connection: NWConnection!
|
|
||||||
|
|
||||||
static let shared = PrinterRequestManager()
|
static let shared = PrinterRequestManager()
|
||||||
|
|
||||||
|
// Debug stuff
|
||||||
|
let startDate = Date()
|
||||||
|
let startDateString: String
|
||||||
|
let filename: URL
|
||||||
|
|
||||||
|
func writeToDebugLog(_ output: String) {
|
||||||
|
do {
|
||||||
|
let fileHandle = try FileHandle(forWritingTo: filename)
|
||||||
|
defer {
|
||||||
|
fileHandle.closeFile()
|
||||||
|
}
|
||||||
|
fileHandle.seekToEndOfFile()
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SS"
|
||||||
|
|
||||||
|
let debugOutput = String("\(dateFormatter.string(from: Date())) - \(output)\n")
|
||||||
|
fileHandle.write(debugOutput.data(using: .utf8)!)
|
||||||
|
} catch {
|
||||||
|
print("[error] writeToDebugLog - \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Websocket JSON-RPC endpoints discovered via bonjour
|
||||||
|
@Published var nwBrowserDiscoveredItems: [NWBrowser.Result] = []
|
||||||
|
|
||||||
|
|
||||||
|
// Websocket JSON-RPC published data
|
||||||
|
@Published var state: String
|
||||||
|
@Published var progress: Double
|
||||||
|
@Published var extruderTemperature: Double
|
||||||
|
@Published var bedTemperature: Double
|
||||||
|
|
||||||
|
// Active connection published data
|
||||||
|
@Published var isConnected = false
|
||||||
|
@Published var socketHost: String
|
||||||
|
@Published var socketPort: String
|
||||||
|
|
||||||
|
let nwBrowser = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)
|
||||||
|
var connection: NWConnection!
|
||||||
|
|
||||||
|
var socket: WebSocket?
|
||||||
|
var lastPingDate = Date()
|
||||||
|
|
||||||
|
// TODO: Set this up to actually reconnect
|
||||||
|
|
||||||
|
// Parse a JSON-RPC query-response message
|
||||||
|
func parse_response(_ response: jsonRpcResponse) {
|
||||||
|
state = response.result.status.print_stats?.state ?? ""
|
||||||
|
progress = response.result.status.virtual_sdcard?.progress ?? 0.0
|
||||||
|
extruderTemperature = response.result.status.extruder?.temperature ?? 0.0
|
||||||
|
bedTemperature = response.result.status.heater_bed?.temperature ?? 0.0
|
||||||
|
|
||||||
|
print(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a JSON-RPC update message
|
||||||
|
func parse_update(_ update: jsonRpcUpdate) {
|
||||||
|
if let newState = update.params.status?.print_stats?.state {
|
||||||
|
state = newState
|
||||||
|
}
|
||||||
|
if let newProgress = update.params.status?.virtual_sdcard?.progress {
|
||||||
|
progress = newProgress
|
||||||
|
}
|
||||||
|
if let newExtruderTemp = update.params.status?.extruder?.temperature {
|
||||||
|
extruderTemperature = newExtruderTemp
|
||||||
|
}
|
||||||
|
if let newBedTemp = update.params.status?.heater_bed?.temperature {
|
||||||
|
bedTemperature = newBedTemp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
|
state = ""
|
||||||
|
progress = 0.0
|
||||||
|
extruderTemperature = 0.0
|
||||||
|
bedTemperature = 0.0
|
||||||
|
socketHost = ""
|
||||||
|
socketPort = ""
|
||||||
|
//reconnectionTimer = nil
|
||||||
|
|
||||||
|
// MARK: Debug stuff
|
||||||
|
startDateString = "\(startDate)\n\n"
|
||||||
|
filename = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("klippermon-debug-\(startDateString).txt")
|
||||||
|
|
||||||
|
do {
|
||||||
|
try startDateString.write(to: filename, atomically: true, encoding: .utf8)
|
||||||
|
} catch {
|
||||||
|
print("[error] Couldn't write to \(filename) - \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Bonjour browser initialization at instantiation
|
// MARK: Bonjour browser initialization at instantiation
|
||||||
nwBrowser.browseResultsChangedHandler = { (newResults, changes) in
|
nwBrowser.browseResultsChangedHandler = { (newResults, changes) in
|
||||||
print("[update] Results changed.")
|
print("[update] Results changed.")
|
||||||
newResults.forEach { result in
|
newResults.forEach { result in
|
||||||
print(result)
|
print(result)
|
||||||
self.nwBrowserDiscoveredItems.append(result.endpoint)
|
self.nwBrowserDiscoveredItems.append(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// State update handler
|
// Bonjour browser state update handler
|
||||||
nwBrowser.stateUpdateHandler = { newState in
|
nwBrowser.stateUpdateHandler = { newState in
|
||||||
switch newState {
|
switch newState {
|
||||||
case .failed(let error):
|
case .failed(let error):
|
||||||
print("[error] nwbrowser: \(error)")
|
print("[error] nwbrowser: \(error)")
|
||||||
case .ready:
|
case .ready:
|
||||||
print("[ready] nwbrowser")
|
print("[ready] nwbrowser")
|
||||||
if let innerEndpoint = self.connection?.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
|
||||||
print("Connected to:", "\(host):\(port)")
|
|
||||||
}
|
|
||||||
case .setup:
|
case .setup:
|
||||||
print("[setup] nwbrowser")
|
print("[setup] nwbrowser")
|
||||||
default:
|
default:
|
||||||
@ -109,22 +145,32 @@ class PrinterRequestManager: ObservableObject, WebSocketDelegate {
|
|||||||
nwBrowser.start(queue: DispatchQueue.main)
|
nwBrowser.start(queue: DispatchQueue.main)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called from the UI, providing an endpoint.
|
// Called from the UI with an endpoint.
|
||||||
// Momentarily connect/disconnects from the endpoint to retrieve the host/port
|
// Momentarily connect/disconnects from the endpoint to retrieve the host/port
|
||||||
// calls private function openWebsocket to process the host/port
|
// calls private function openWebsocket to process the host/port
|
||||||
func resolveBonjourHost(_ endpoint: NWEndpoint) {
|
func resolveBonjourHost(_ endpoint: NWEndpoint) {
|
||||||
|
// Debug stuff
|
||||||
|
endpoint.txtRecord?.forEach({ (key: String, value: NWTXTRecord.Entry) in
|
||||||
|
print("\(key): \(value)")
|
||||||
|
})
|
||||||
|
|
||||||
connection = NWConnection(to: endpoint, using: .tcp)
|
connection = NWConnection(to: endpoint, using: .tcp)
|
||||||
connection.stateUpdateHandler = { [self] state in
|
connection.stateUpdateHandler = { [self] state in
|
||||||
switch state {
|
switch state {
|
||||||
case .ready:
|
case .ready:
|
||||||
if let innerEndpoint = connection.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
if let innerEndpoint = connection.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
||||||
print("Connected to \(host):\(port)")
|
let hostPortDebugOutput = "Connected to \(host):\(port)"
|
||||||
|
|
||||||
|
print(hostPortDebugOutput)
|
||||||
|
writeToDebugLog(hostPortDebugOutput)
|
||||||
|
|
||||||
let hostString = "\(host)"
|
let hostString = "\(host)"
|
||||||
let regex = try! Regex("%en0")
|
let regex = try! Regex("%(.+)")
|
||||||
let match = hostString.firstMatch(of: regex)
|
let match = hostString.firstMatch(of: regex)
|
||||||
let sanitizedHost = hostString.replacingOccurrences(of: match!.0, with: "")
|
let sanitizedHost = hostString.replacingOccurrences(of: match!.0, with: "")
|
||||||
|
|
||||||
print("[sanitized] Resolved \(sanitizedHost):\(port)")
|
print("[sanitized] Resolved \(sanitizedHost):\(port)")
|
||||||
|
|
||||||
socketHost = sanitizedHost
|
socketHost = sanitizedHost
|
||||||
socketPort = "\(port)"
|
socketPort = "\(port)"
|
||||||
connection.cancel()
|
connection.cancel()
|
||||||
@ -137,40 +183,91 @@ class PrinterRequestManager: ObservableObject, WebSocketDelegate {
|
|||||||
connection.start(queue: .global())
|
connection.start(queue: .global())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reconnectWebsocket() {
|
||||||
|
if socket == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
socket!.disconnect()
|
||||||
|
self.openWebsocket()
|
||||||
|
//socket!.write(ping: "PING!".data(using: .utf8)!)
|
||||||
|
}
|
||||||
|
|
||||||
// Opens the websocket connection
|
// Opens the websocket connection
|
||||||
// TODO: host and port should be function arguments probably maybe
|
// TODO: host and port should be function arguments probably maybe
|
||||||
private func openWebsocket() {
|
private func openWebsocket() {
|
||||||
if let host = socketHost, let port = socketPort {
|
//let fullUrlString = "http://\(socketHost):\(socketPort)/websocket"
|
||||||
//let fullUrlString = "http://\(socketHost):\(socketPort)/websocket"
|
var request = URLRequest(url: URL(string: "http://\(socketHost):\(socketPort)/websocket")!)
|
||||||
var request = URLRequest(url: URL(string: "http://\(host):\(port)/websocket")!)
|
request.timeoutInterval = 5
|
||||||
request.timeoutInterval = 5
|
socket = WebSocket(request: request)
|
||||||
socket = WebSocket(request: request)
|
socket!.delegate = self
|
||||||
socket.delegate = self
|
socket!.connect()
|
||||||
socket.connect()
|
|
||||||
|
// TODO: Check that this keeps the connection alive properly
|
||||||
|
Timer.scheduledTimer(withTimeInterval: 30.0, repeats: true) { [self] timer in
|
||||||
|
//self.checkWebsocketIsAlive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old REST way to do it
|
// MARK: delegate callback for Starscream WebSocketClient
|
||||||
// TODO: Stop using this.
|
func didReceive(event: Starscream.WebSocketEvent, client: Starscream.WebSocket) {
|
||||||
func queryPrinterStats() async {
|
switch event {
|
||||||
guard let url = URL(string: "http://10.0.21.39/printer/objects/query?extruder&virtual_sdcard&print_stats&heater_bed") else {
|
case .connected(let headers):
|
||||||
fatalError("Missing URL")
|
isConnected = true
|
||||||
}
|
print("websocket is connected: \(headers)")
|
||||||
|
writeToDebugLog("Connected to WebSocket")
|
||||||
let urlRequest = URLRequest(url: url)
|
let jsonRpcRequest = JsonRpcRequest(method: "printer.objects.subscribe",
|
||||||
do {
|
params: ["objects":
|
||||||
let (data, response) = try await URLSession.shared.data(for: urlRequest)
|
["extruder": nil,
|
||||||
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
"virtual_sdcard": nil,
|
||||||
print("Error with response.")
|
"heater_bed": nil,
|
||||||
return
|
"print_stats": nil]
|
||||||
|
])
|
||||||
|
|
||||||
|
print(String(data: try! JSONEncoder().encode(jsonRpcRequest), encoding: .utf8)!)
|
||||||
|
socket?.write(data: try! JSONEncoder().encode(jsonRpcRequest), completion: {
|
||||||
|
print("[send] json-rpc printer.objects.subscribe query")
|
||||||
|
})
|
||||||
|
case .disconnected(let reason, let code):
|
||||||
|
isConnected = false
|
||||||
|
print("websocket is disconnected: \(reason) with code: \(code)")
|
||||||
|
writeToDebugLog("Websocket is disconnected: \(reason) with code: \(code)")
|
||||||
|
case .text(let string):
|
||||||
|
self.writeToDebugLog(string)
|
||||||
|
// Check for initial RPC response
|
||||||
|
let statusResponse = try? JSONDecoder().decode(jsonRpcResponse.self, from: Data(string.utf8))
|
||||||
|
if let statusResponseSafe = statusResponse {
|
||||||
|
self.parse_response(statusResponseSafe)
|
||||||
}
|
}
|
||||||
// handle data as JSON
|
// Check for RPC updates
|
||||||
let decoder = JSONDecoder()
|
if let updateResponse = try? JSONDecoder().decode(jsonRpcUpdate.self, from: Data(string.utf8)) {
|
||||||
printerObjectsQuery = try decoder.decode(PrinterObjectsQuery.self, from: data)
|
self.parse_update(updateResponse)
|
||||||
printerCommsOkay = true
|
}
|
||||||
} catch {
|
case .binary(let data):
|
||||||
print("Exception thrown: \(error)")
|
self.writeToDebugLog(String(data: data, encoding: .utf8)!)
|
||||||
printerCommsOkay = false
|
print("Received data: \(data.count)")
|
||||||
|
case .ping(_):
|
||||||
|
print("PING! \(Date())")
|
||||||
|
// TODO: There's probably a better way to do this
|
||||||
|
if(lastPingDate.addingTimeInterval(WEBSOCKET_TIMEOUT_INTERVAL) < Date.now) {
|
||||||
|
print("Forcing reconnection of websocket..")
|
||||||
|
self.reconnectWebsocket()
|
||||||
|
}
|
||||||
|
lastPingDate = Date()
|
||||||
|
break
|
||||||
|
case .pong(_):
|
||||||
|
print("PONG!")
|
||||||
|
break
|
||||||
|
case .viabilityChanged(_):
|
||||||
|
break
|
||||||
|
case .reconnectSuggested(_):
|
||||||
|
break
|
||||||
|
case .cancelled:
|
||||||
|
isConnected = false
|
||||||
|
case .error(let error):
|
||||||
|
isConnected = false
|
||||||
|
print("[error] Starscream: \(error.debugDescription)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
44
KlipperMon/PrinterStats.swift
Normal file
44
KlipperMon/PrinterStats.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// PrinterStats.swift
|
||||||
|
// KlipperMon
|
||||||
|
//
|
||||||
|
// Created by maddiefuzz on 2/9/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PrinterStats: ObservableObject {
|
||||||
|
@Published var state: String
|
||||||
|
@Published var progress: Double
|
||||||
|
@Published var extruderTemperature: Double
|
||||||
|
@Published var bedTemperature: Double
|
||||||
|
|
||||||
|
init(response: jsonRpcResponse) {
|
||||||
|
state = response.result.status.print_stats?.state ?? ""
|
||||||
|
progress = response.result.status.virtual_sdcard?.progress ?? 0.0
|
||||||
|
extruderTemperature = response.result.status.extruder?.temperature ?? 0.0
|
||||||
|
bedTemperature = response.result.status.heater_bed?.temperature ?? 0.0
|
||||||
|
|
||||||
|
print(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(update: jsonRpcUpdate) {
|
||||||
|
// print(update)
|
||||||
|
if let newState = update.params.status?.print_stats?.state {
|
||||||
|
//state = update.params[0].print_stats?.state
|
||||||
|
state = newState
|
||||||
|
}
|
||||||
|
if let newProgress = update.params.status?.virtual_sdcard?.progress {
|
||||||
|
print("Update progress")
|
||||||
|
progress = newProgress
|
||||||
|
}
|
||||||
|
if let newExtruderTemp = update.params.status?.extruder?.temperature {
|
||||||
|
print("Update extruder temp \(newExtruderTemp)")
|
||||||
|
extruderTemperature = newExtruderTemp
|
||||||
|
}
|
||||||
|
if let newBedTemp = update.params.status?.heater_bed?.temperature {
|
||||||
|
print("Update heated bed \(newBedTemp)")
|
||||||
|
bedTemperature = newBedTemp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
KlipperMonTests/KlipperMonScratchTests.swift
Normal file
17
KlipperMonTests/KlipperMonScratchTests.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// KlipperMonScratchTests.swift
|
||||||
|
// KlipperMonTests
|
||||||
|
//
|
||||||
|
// Created by maddiefuzz on 2/20/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class ScratchTests: XCTestCase {
|
||||||
|
|
||||||
|
func testOneAndOneIsEqual() {
|
||||||
|
let a = 1
|
||||||
|
let b = 1
|
||||||
|
XCTAssertEqual(a, b, "1 did not equal 1.")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user