Add NWBrowser and NWConnection code.
TODO: Refactor all of this stuff better
This commit is contained in:
parent
dd4324e395
commit
5f429b8cb4
@ -7,8 +7,8 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */; };
|
||||||
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */; };
|
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */; };
|
||||||
E180B5EB2992CD9100425DB0 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5EA2992CD9100425DB0 /* ContentView.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 */; };
|
||||||
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5F12992CD9200425DB0 /* Persistence.swift */; };
|
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5F12992CD9200425DB0 /* Persistence.swift */; };
|
||||||
@ -16,9 +16,9 @@
|
|||||||
E180B6002992CD9300425DB0 /* KlipperMonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */; };
|
E180B6002992CD9300425DB0 /* KlipperMonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */; };
|
||||||
E180B60A2992CD9300425DB0 /* KlipperMonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6092992CD9300425DB0 /* KlipperMonUITests.swift */; };
|
E180B60A2992CD9300425DB0 /* KlipperMonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6092992CD9300425DB0 /* KlipperMonUITests.swift */; };
|
||||||
E180B60C2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */; };
|
E180B60C2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */; };
|
||||||
E180B61B2992CF2200425DB0 /* KlipperWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61A2992CF2200425DB0 /* KlipperWebsocket.swift */; };
|
|
||||||
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */; };
|
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */; };
|
||||||
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 */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -39,9 +39,10 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
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>"; };
|
||||||
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>"; };
|
||||||
E180B5EA2992CD9100425DB0 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.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>"; };
|
||||||
E180B5EF2992CD9200425DB0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
E180B5EF2992CD9200425DB0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
E180B5F12992CD9200425DB0 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
E180B5F12992CD9200425DB0 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||||
@ -52,9 +53,9 @@
|
|||||||
E180B6052992CD9300425DB0 /* KlipperMonUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KlipperMonUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
E180B6052992CD9300425DB0 /* KlipperMonUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KlipperMonUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
E180B6092992CD9300425DB0 /* KlipperMonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonUITests.swift; sourceTree = "<group>"; };
|
E180B6092992CD9300425DB0 /* KlipperMonUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonUITests.swift; sourceTree = "<group>"; };
|
||||||
E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||||
E180B61A2992CF2200425DB0 /* KlipperWebsocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperWebsocket.swift; sourceTree = "<group>"; };
|
|
||||||
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>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -105,16 +106,17 @@
|
|||||||
E180B5E72992CD9100425DB0 /* KlipperMon */ = {
|
E180B5E72992CD9100425DB0 /* KlipperMon */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E124B9D72993FE5500C0D2D2 /* Info.plist */,
|
||||||
E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */,
|
E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */,
|
||||||
E180B5EA2992CD9100425DB0 /* ContentView.swift */,
|
|
||||||
E180B5EC2992CD9200425DB0 /* Assets.xcassets */,
|
E180B5EC2992CD9200425DB0 /* Assets.xcassets */,
|
||||||
E180B5F12992CD9200425DB0 /* Persistence.swift */,
|
E180B5F12992CD9200425DB0 /* Persistence.swift */,
|
||||||
E180B5F62992CD9200425DB0 /* KlipperMon.entitlements */,
|
E180B5F62992CD9200425DB0 /* KlipperMon.entitlements */,
|
||||||
E180B5F32992CD9200425DB0 /* KlipperMon.xcdatamodeld */,
|
E180B5F32992CD9200425DB0 /* KlipperMon.xcdatamodeld */,
|
||||||
E180B5EE2992CD9200425DB0 /* Preview Content */,
|
E180B5EE2992CD9200425DB0 /* Preview Content */,
|
||||||
E180B61A2992CF2200425DB0 /* KlipperWebsocket.swift */,
|
|
||||||
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */,
|
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */,
|
||||||
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */,
|
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */,
|
||||||
|
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */,
|
||||||
|
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */,
|
||||||
);
|
);
|
||||||
path = KlipperMon;
|
path = KlipperMon;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -276,10 +278,10 @@
|
|||||||
files = (
|
files = (
|
||||||
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */,
|
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */,
|
||||||
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
||||||
E180B61B2992CF2200425DB0 /* KlipperWebsocket.swift in Sources */,
|
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */,
|
||||||
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */,
|
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */,
|
||||||
E180B5EB2992CD9100425DB0 /* ContentView.swift in Sources */,
|
|
||||||
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */,
|
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */,
|
||||||
|
E180B6222993256E00425DB0 /* PrinterRequestManager.swift in Sources */,
|
||||||
E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */,
|
E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -444,6 +446,7 @@
|
|||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = KlipperMon/Info.plist;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -471,6 +474,7 @@
|
|||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = KlipperMon/Info.plist;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
//
|
|
||||||
// ContentView.swift
|
|
||||||
// KlipperMon
|
|
||||||
//
|
|
||||||
// Created by maddiefuzz on 2/7/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
struct ContentView: View {
|
|
||||||
@Environment(\.managedObjectContext) private var viewContext
|
|
||||||
|
|
||||||
@FetchRequest(
|
|
||||||
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
|
|
||||||
animation: .default)
|
|
||||||
private var items: FetchedResults<Item>
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
List {
|
|
||||||
ForEach(items) { item in
|
|
||||||
NavigationLink {
|
|
||||||
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
|
|
||||||
} label: {
|
|
||||||
Text(item.timestamp!, formatter: itemFormatter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDelete(perform: deleteItems)
|
|
||||||
}
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem {
|
|
||||||
Button(action: addItem) {
|
|
||||||
Label("Add Item", systemImage: "plus")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("Select an item")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func addItem() {
|
|
||||||
withAnimation {
|
|
||||||
let newItem = Item(context: viewContext)
|
|
||||||
newItem.timestamp = Date()
|
|
||||||
|
|
||||||
do {
|
|
||||||
try viewContext.save()
|
|
||||||
} catch {
|
|
||||||
// Replace this implementation with code to handle the error appropriately.
|
|
||||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
|
||||||
let nsError = error as NSError
|
|
||||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func deleteItems(offsets: IndexSet) {
|
|
||||||
withAnimation {
|
|
||||||
offsets.map { items[$0] }.forEach(viewContext.delete)
|
|
||||||
|
|
||||||
do {
|
|
||||||
try viewContext.save()
|
|
||||||
} catch {
|
|
||||||
// Replace this implementation with code to handle the error appropriately.
|
|
||||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
|
||||||
let nsError = error as NSError
|
|
||||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let itemFormatter: DateFormatter = {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateStyle = .short
|
|
||||||
formatter.timeStyle = .medium
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
|
||||||
}
|
|
||||||
}
|
|
25
KlipperMon/Info.plist
Normal file
25
KlipperMon/Info.plist
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSBonjourServices</key>
|
||||||
|
<array>
|
||||||
|
<string>_http._tcp.</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>soyuz</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>soyuz</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSServices</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct KlipperMonMenuBarApp: App {
|
struct KlipperMonMenuBarApp: App {
|
||||||
let persistenceController = PersistenceController.shared
|
let persistenceController = PersistenceController.shared
|
||||||
@ -15,29 +14,22 @@ struct KlipperMonMenuBarApp: App {
|
|||||||
@State var currentIcon = "move.3d"
|
@State var currentIcon = "move.3d"
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup(id: "floating-stats") {
|
||||||
ContentView()
|
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: {
|
||||||
|
PrinterConfigView()
|
||||||
|
})
|
||||||
|
|
||||||
MenuBarExtra("Soyuz", systemImage: currentIcon) {
|
MenuBarExtra("Soyuz", systemImage: currentIcon) {
|
||||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||||
|
.padding([.top, .leading, .trailing], 8)
|
||||||
|
.padding([.bottom], 6)
|
||||||
}
|
}
|
||||||
.menuBarExtraStyle(.window)
|
.menuBarExtraStyle(.window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol MenuBarExtraIconUpdater {
|
|
||||||
func updateIcon(systemName: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct KlipperMonApp: App {
|
|
||||||
let persistenceController = PersistenceController.shared
|
|
||||||
|
|
||||||
var body: some Scene {
|
|
||||||
WindowGroup {
|
|
||||||
ContentView()
|
|
||||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -6,46 +6,172 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AppKit
|
||||||
|
import Network
|
||||||
|
|
||||||
|
struct KlipperMenuBarButtonStyle: ButtonStyle {
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
configuration.label
|
||||||
|
.padding()
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct KlipperMonMenuBarExtraView: View {
|
struct KlipperMonMenuBarExtraView: View {
|
||||||
|
let DANGERTEMP = 40.0
|
||||||
|
|
||||||
|
@Environment(\.openWindow) var openWindow
|
||||||
|
|
||||||
|
@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 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()
|
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||||
|
|
||||||
|
// TODO: Use @published API data instead of instance state variable
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Label(String(printPercentage), systemImage: "thermometer.snowflake.circle")
|
VStack {
|
||||||
|
// Printer Readouts
|
||||||
|
if let queryResults = printerManager.printerObjectsQuery {
|
||||||
|
Text(queryResults.result.status.print_stats.state.capitalized)
|
||||||
|
.font(.title)
|
||||||
|
.padding(4)
|
||||||
|
// Print information
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "pencil.tip")
|
||||||
|
.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 {
|
||||||
|
Image(systemName: "flame")
|
||||||
|
.foregroundColor( hotendHotTemp ? .red : .white )
|
||||||
|
.opacity( hotendHotTemp ? 1.0 : 0.3 )
|
||||||
|
Text("Hotend")
|
||||||
|
.font(.headline)
|
||||||
|
Spacer()
|
||||||
|
Text("\(Int(queryResults.result.status.extruder.temperature))°C")
|
||||||
|
}
|
||||||
|
// Bed temperature
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "flame")
|
||||||
|
.foregroundColor( bedHotTemp ? .red : .white )
|
||||||
|
.opacity( bedHotTemp ? 1.0 : 0.3 )
|
||||||
|
Text("Plate")
|
||||||
|
.font(.headline)
|
||||||
|
Spacer()
|
||||||
|
Text("\(Int(queryResults.result.status.heater_bed.temperature))°C")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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
|
||||||
|
HStack {
|
||||||
|
Button {
|
||||||
|
print("Button pressed")
|
||||||
|
openWindow(id: "soyuz_cfg")
|
||||||
|
} label: {
|
||||||
|
Text("Server Config")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if(printerManager.printerCommsOkay) {
|
||||||
|
Image(systemName: "network")
|
||||||
|
Text("Online")
|
||||||
|
} else {
|
||||||
|
Image(systemName: "xmark")
|
||||||
|
Text("Offline")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(4)
|
||||||
|
.frame(minWidth: 220, maxWidth: 250)
|
||||||
.onReceive(timer) { input in
|
.onReceive(timer) { input in
|
||||||
Task {
|
Task {
|
||||||
self.printPercentage = await self.getPrintPercentage()
|
await printerManager.queryPrinterStats()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Check Printer") {
|
if let query = printerManager.printerObjectsQuery {
|
||||||
currentMenuBarIcon = "flame"
|
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..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPrintPercentage() async -> Double {
|
|
||||||
guard let url = URL(string: "http://10.0.21.39/printer/objects/query?extruder=temperature") else {
|
|
||||||
fatalError("Missing URL")
|
|
||||||
}
|
}
|
||||||
|
// Testing bonjour stuff
|
||||||
let urlRequest = URLRequest(url: url)
|
.onAppear {
|
||||||
do {
|
nwBrowser.browseResultsChangedHandler = { (newResults, changes) in
|
||||||
let (data, response) = try await URLSession.shared.data(for: urlRequest)
|
print("[update] Results changed.")
|
||||||
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
newResults.forEach { result in
|
||||||
print("Error!")
|
print(result)
|
||||||
return -1
|
self.nwBrowserDiscoveredItems.append(result.endpoint)
|
||||||
|
}
|
||||||
|
//self.nwBrowserDiscoveredItems.append(newResults.description)
|
||||||
|
}
|
||||||
|
nwBrowser.stateUpdateHandler = { newState in
|
||||||
|
switch newState {
|
||||||
|
case .failed(let error):
|
||||||
|
print("[error] nwbrowser: \(error)")
|
||||||
|
case .ready:
|
||||||
|
print("[ready] nwbrowser")
|
||||||
|
case .setup:
|
||||||
|
print("[setup] nwbrowser")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nwBrowser.start(queue: DispatchQueue.main)
|
||||||
|
}
|
||||||
|
ForEach(nwBrowserDiscoveredItems, id: \.hashValue) { item in
|
||||||
|
Button {
|
||||||
|
let connection = NWConnection(to: item, using: .tcp)
|
||||||
|
connection.stateUpdateHandler = { newState in
|
||||||
|
switch newState {
|
||||||
|
case .failed(let error):
|
||||||
|
print("[error] nwconnection: \(error)")
|
||||||
|
case .ready:
|
||||||
|
print("[ready] nwconnection")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.start(queue: DispatchQueue.main)
|
||||||
|
} label: {
|
||||||
|
Text(item.debugDescription)
|
||||||
}
|
}
|
||||||
print(String(data: data, encoding: .utf8))
|
|
||||||
let decoder = JSONDecoder()
|
|
||||||
let printerObjectsQuery = try decoder.decode(PrinterObjectsQuery.self, from: data)
|
|
||||||
return printerObjectsQuery.result.status.extruder.temperature
|
|
||||||
// handle data as JSON
|
|
||||||
} catch {
|
|
||||||
print("Error!")
|
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,3 +182,4 @@ struct KlipperMonMenuBarExtraView_Previews: PreviewProvider {
|
|||||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentMenuBarIcon)
|
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentMenuBarIcon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
//
|
|
||||||
// KlipperWebsocket.swift
|
|
||||||
// KlipperMon
|
|
||||||
//
|
|
||||||
// Created by maddiefuzz on 2/7/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
20
KlipperMon/PrinterConfigView.swift
Normal file
20
KlipperMon/PrinterConfigView.swift
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// PrinterConfigView.swift
|
||||||
|
// KlipperMon
|
||||||
|
//
|
||||||
|
// Created by maddiefuzz on 2/8/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PrinterConfigView: View {
|
||||||
|
var body: some View {
|
||||||
|
Text("Config Printer In Here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrinterConfigView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PrinterConfigView()
|
||||||
|
}
|
||||||
|
}
|
@ -17,9 +17,33 @@ struct ResultsData: Decodable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct StatusData: Decodable {
|
struct StatusData: Decodable {
|
||||||
|
let virtual_sdcard: VirtualSDCardData
|
||||||
let extruder: ExtruderData
|
let extruder: ExtruderData
|
||||||
|
let print_stats: PrintStatsData
|
||||||
|
let heater_bed: HeaterBedData
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VirtualSDCardData: Decodable {
|
||||||
|
let file_path: String?
|
||||||
|
let progress: Double
|
||||||
|
let is_active: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExtruderData: Decodable {
|
struct ExtruderData: Decodable {
|
||||||
let temperature: Double
|
let temperature: Double
|
||||||
|
let target: Double
|
||||||
|
let power: Double
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrintStatsData: Decodable {
|
||||||
|
let filename: String
|
||||||
|
let print_duration: Double
|
||||||
|
let filament_used: Double
|
||||||
|
let state: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HeaterBedData: Decodable {
|
||||||
|
let temperature: Double
|
||||||
|
let target: Double
|
||||||
|
let power: Double
|
||||||
}
|
}
|
||||||
|
47
KlipperMon/PrinterRequestManager.swift
Normal file
47
KlipperMon/PrinterRequestManager.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// PrinterRequestManager.swift
|
||||||
|
// KlipperMon
|
||||||
|
//
|
||||||
|
// Created by maddiefuzz on 2/7/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class PrinterRequestManager: ObservableObject {
|
||||||
|
@Published var printerObjectsQuery: PrinterObjectsQuery?
|
||||||
|
|
||||||
|
@Published var printerCommsOkay = false
|
||||||
|
|
||||||
|
static let shared = PrinterRequestManager()
|
||||||
|
|
||||||
|
//let nwBrowser = NWBrowser(for: .bonjour(type: "_moonraker._tcp", domain: "local."), using: .tcp)
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryPrinterStats() async {
|
||||||
|
guard let url = URL(string: "http://10.0.21.39/printer/objects/query?extruder&virtual_sdcard&print_stats&heater_bed") else {
|
||||||
|
fatalError("Missing URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
let urlRequest = URLRequest(url: url)
|
||||||
|
do {
|
||||||
|
let (data, response) = try await URLSession.shared.data(for: urlRequest)
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||||
|
print("Error with response.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// handle data as JSON
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
printerObjectsQuery = try decoder.decode(PrinterObjectsQuery.self, from: data)
|
||||||
|
printerCommsOkay = true
|
||||||
|
//return printerObjectsQuery.result.status.extruder.temperature
|
||||||
|
} catch {
|
||||||
|
print("Exception thrown: \(error)")
|
||||||
|
printerCommsOkay = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user