Compare commits
3 Commits
1dd2e37f61
...
e440f3489b
Author | SHA1 | Date |
---|---|---|
maddiebaka | e440f3489b | |
maddiebaka | 90be4321b0 | |
Madeline Pace | a894876623 |
|
@ -7,7 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
C15B60A82A2423760052F712 /* UserNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15B60A72A2423760052F712 /* UserNotificationHandler.swift */; };
|
||||
C15F06A62A20198300C14CD8 /* Soyuz.help in Resources */ = {isa = PBXBuildFile; fileRef = C15F06A42A20171E00C14CD8 /* Soyuz.help */; };
|
||||
C1A11C182A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */; };
|
||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */; };
|
||||
E16378B429A491E6002F05E9 /* MoonrakerSocketManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */; };
|
||||
E180B5E92992CD9100425DB0 /* SoyuzApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* SoyuzApp.swift */; };
|
||||
|
@ -44,7 +46,9 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
C15B60A72A2423760052F712 /* UserNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationHandler.swift; sourceTree = "<group>"; };
|
||||
C15F06A42A20171E00C14CD8 /* Soyuz.help */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Soyuz.help; sourceTree = "<group>"; };
|
||||
C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerNative.swift; 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>"; };
|
||||
E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -172,6 +176,8 @@
|
|||
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */,
|
||||
E180B6212993256E00425DB0 /* MoonrakerSocketManager.swift */,
|
||||
E1A93C6629C932E200BAE750 /* BonjourBrowser.swift */,
|
||||
C15B60A72A2423760052F712 /* UserNotificationHandler.swift */,
|
||||
C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
|
@ -315,8 +321,10 @@
|
|||
files = (
|
||||
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */,
|
||||
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
||||
C1A11C182A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift in Sources */,
|
||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */,
|
||||
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */,
|
||||
C15B60A82A2423760052F712 /* UserNotificationHandler.swift in Sources */,
|
||||
E1A93C6729C932E200BAE750 /* BonjourBrowser.swift in Sources */,
|
||||
E180B5E92992CD9100425DB0 /* SoyuzApp.swift in Sources */,
|
||||
E180B6222993256E00425DB0 /* MoonrakerSocketManager.swift in Sources */,
|
||||
|
@ -477,17 +485,19 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Soyuz/Soyuz.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_ASSET_PATHS = "Soyuz/Preview\\ Content";
|
||||
DEVELOPMENT_TEAM = W9ASV855X5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Soyuz/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
INFOPLIST_KEY_LSUIElement = NO;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
@ -507,17 +517,19 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Soyuz/Soyuz.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_ASSET_PATHS = "Soyuz/Preview\\ Content";
|
||||
DEVELOPMENT_TEAM = W9ASV855X5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Soyuz/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
INFOPLIST_KEY_LSUIElement = NO;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
|
|
@ -10,5 +10,23 @@
|
|||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>E180B5E42992CD9100425DB0</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>E180B5FA2992CD9300425DB0</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>E180B6042992CD9300425DB0</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -2,6 +2,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>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CFBundleHelpBookFolder</key>
|
||||
<string>Soyuz.help</string>
|
||||
<key>CFBundleHelpBookName</key>
|
||||
|
|
|
@ -35,23 +35,27 @@ extension NWBrowser: NetworkDiscoveryEngine {
|
|||
// MARK: BonjourBrowser
|
||||
|
||||
class BonjourBrowser: ObservableObject {
|
||||
@Published var NDEngineResults: [NWBrowser.Result] = []
|
||||
@Published var networkResults: [NWBrowser.Result] = []
|
||||
|
||||
private let nwBrowser: NetworkDiscoveryEngine
|
||||
var connection: NWConnection!
|
||||
private var nwBrowser: NWBrowser!
|
||||
private var connection: NWConnection!
|
||||
|
||||
// TEMPORARY
|
||||
// var bonjourListener: NWListener?
|
||||
|
||||
init(browser: NetworkDiscoveryEngine = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)) {
|
||||
nwBrowser = browser
|
||||
|
||||
init() {
|
||||
if nwBrowser == nil {
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
func setup() {
|
||||
nwBrowser = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)
|
||||
// Bonjour browser results changed handler
|
||||
nwBrowser.setBrowseResultsChangedHandler({ (newResults, changes) in
|
||||
print("[update] Results changed.")
|
||||
self.NDEngineResults.removeAll()
|
||||
self.networkResults.removeAll()
|
||||
newResults.forEach { result in
|
||||
print(result)
|
||||
self.NDEngineResults.append(result)
|
||||
self.networkResults.append(result)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -72,4 +76,63 @@ class BonjourBrowser: ObservableObject {
|
|||
nwBrowser.startScan(queue: DispatchQueue.main)
|
||||
}
|
||||
|
||||
func enableScan(_ queue: DispatchQueue) {
|
||||
if(nwBrowser.state == .cancelled) {
|
||||
self.setup()
|
||||
}
|
||||
nwBrowser.start(queue: queue)
|
||||
}
|
||||
|
||||
func disableScan() {
|
||||
if(nwBrowser.state != .cancelled) {
|
||||
nwBrowser.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//class BonjourBrowser: ObservableObject {
|
||||
// @Published var NDEngineResults: [NWBrowser.Result] = []
|
||||
//
|
||||
// private let nwBrowser: NetworkDiscoveryEngine
|
||||
// var connection: NWConnection!
|
||||
//
|
||||
// // TEMPORARY
|
||||
//// var bonjourListener: NWListener?
|
||||
//
|
||||
// init(browser: NetworkDiscoveryEngine = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)) {
|
||||
// nwBrowser = browser
|
||||
// // Bonjour browser results changed handler
|
||||
// nwBrowser.setBrowseResultsChangedHandler({ (newResults, changes) in
|
||||
// print("[update] Results changed.")
|
||||
// self.NDEngineResults.removeAll()
|
||||
// newResults.forEach { result in
|
||||
// print(result)
|
||||
// self.NDEngineResults.append(result)
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// // Bonjour browser state update handler
|
||||
// nwBrowser.setStateUpdateHandler({ 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.startScan(queue: DispatchQueue.main)
|
||||
// }
|
||||
//
|
||||
// func startScanning(queue: DispatchQueue.main) {
|
||||
// if(self.nwBrowser.state == NWBrowser.State.cancelled) {
|
||||
//
|
||||
// }
|
||||
// self.nwBrowser.startScan(queue)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
|
|
@ -29,6 +29,8 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
@Published var connection: NWConnection?
|
||||
@Published var friendlyHostname: String = ""
|
||||
|
||||
var notification = UserNotificationHandler.shared
|
||||
|
||||
private var socket: WebSocket?
|
||||
private var lastPingDate = Date()
|
||||
private var starscreamEngine: Engine
|
||||
|
@ -67,12 +69,12 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
print("\(key): \(value)")
|
||||
})
|
||||
|
||||
// if isConnected == true {
|
||||
// connection?.cancel()
|
||||
// socket?.disconnect()
|
||||
// }
|
||||
//
|
||||
if connection == nil {
|
||||
// if isConnected == true {
|
||||
// connection?.cancel()
|
||||
// socket?.disconnect()
|
||||
// }
|
||||
//
|
||||
if connection == nil || connection?.state == .cancelled {
|
||||
connection = NWConnection(to: endpoint, using: .tcp)
|
||||
}
|
||||
|
||||
|
@ -95,7 +97,7 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
connection?.cancel()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
friendlyHostname = endpoint.toFriendlyString()
|
||||
self.friendlyHostname = endpoint.toFriendlyString()
|
||||
self.socketHost = sanitizedHost
|
||||
self.socketPort = "\(port)"
|
||||
self.openWebsocket()
|
||||
|
@ -110,25 +112,29 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
|
||||
func disconnect() {
|
||||
print("disconnect() called")
|
||||
isConnected = false
|
||||
//connection?.cancel()
|
||||
connection = nil
|
||||
self.isConnected = false
|
||||
socket?.disconnect()
|
||||
socket = nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: Private functions
|
||||
|
||||
// Opens the websocket connection
|
||||
private func openWebsocket() {
|
||||
func openWebsocket() {
|
||||
// Exit function if there is no server to connect to
|
||||
if socketHost.isEmpty || socketPort.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
if socket != nil {
|
||||
socket!.connect()
|
||||
return
|
||||
}
|
||||
|
||||
lastPingDate = Date.now
|
||||
|
||||
var request = URLRequest(url: URL(string: "http://\(socketHost):\(socketPort)/websocket")!)
|
||||
var request = URLRequest(url: URL(string: "ws://\(socketHost):\(socketPort)/websocket")!)
|
||||
request.timeoutInterval = 30
|
||||
socket = WebSocket(request: request, engine: starscreamEngine)
|
||||
socket!.delegate = self
|
||||
|
@ -152,7 +158,8 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
switch(notification.name) {
|
||||
case NSWorkspace.screensDidSleepNotification:
|
||||
print("Screen slept. Disconnecting..")
|
||||
socket?.disconnect()
|
||||
self.disconnect()
|
||||
//socket?.disconnect()
|
||||
case NSWorkspace.screensDidWakeNotification:
|
||||
print("Screen awoke. Opening websocket..")
|
||||
self.openWebsocket()
|
||||
|
@ -214,6 +221,15 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
case .error(let error):
|
||||
isConnected = false
|
||||
print("[error] Starscream: \(error.debugDescription)")
|
||||
switch(error) {
|
||||
case .some(HTTPUpgradeError.notAnUpgrade(200)):
|
||||
print("[debug] Starscream: Forcing disconnect and reconnect..")
|
||||
self.socket?.forceDisconnect()
|
||||
self.socket = nil
|
||||
self.openWebsocket()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,16 +237,22 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
|||
// 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 {
|
||||
print("Printer state: \(newState)")
|
||||
// Issue a notification when state changes from printing to complete
|
||||
// TODO: Handle this better
|
||||
if newState == "complete" && state == "printing" {
|
||||
notification.sendNotification(.printComplete)
|
||||
}
|
||||
state = newState
|
||||
}
|
||||
if let newProgress = update.params.status?.virtual_sdcard?.progress {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// UserNotificationProtocol.swift
|
||||
// Soyuz
|
||||
//
|
||||
// Created by Madeline Pace on 5/28/23.
|
||||
//
|
||||
|
||||
import UserNotifications
|
||||
|
||||
class UserNotificationHandler {
|
||||
static var shared = UserNotificationHandler()
|
||||
|
||||
private var center = UNUserNotificationCenter.current()
|
||||
|
||||
enum NotificationType {
|
||||
case printComplete
|
||||
}
|
||||
|
||||
private init() {
|
||||
center.requestAuthorization(options: [.alert, .sound, .badge, .provisional]) { granted, error in
|
||||
if let error = error {
|
||||
print("Error requesting authorization: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendNotification(_ type: NotificationType) {
|
||||
print("Sending notification.")
|
||||
// Build notification request
|
||||
let content = UNMutableNotificationContent()
|
||||
// TODO: Replace this with localized strings
|
||||
content.title = "Print Complete! 🎉"
|
||||
let request = UNNotificationRequest(identifier: "Print Finished", content: content, trigger: nil)
|
||||
|
||||
// Dispatch notification to system
|
||||
center.add(request) { (error: Error?) in
|
||||
if let theError = error {
|
||||
print("Error: \(theError)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@ struct PrinterConfigView: View {
|
|||
@ObservedObject var printerManager: MoonrakerSocketManager
|
||||
@ObservedObject var bonjourBrowser = BonjourBrowser()
|
||||
|
||||
//@State var bonjourBrowser = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)
|
||||
|
||||
@Environment(\.openURL) private var openURL
|
||||
|
||||
|
||||
|
@ -61,7 +63,7 @@ struct PrinterConfigView: View {
|
|||
}.buttonStyle(PlainButtonStyle())
|
||||
|
||||
}
|
||||
ForEach(bonjourBrowser.NDEngineResults , id: \.hashValue) { result in
|
||||
ForEach(bonjourBrowser.networkResults, id: \.hashValue) { result in
|
||||
HStack {
|
||||
Text(result.endpoint.toFriendlyString())
|
||||
Button {
|
||||
|
@ -78,7 +80,11 @@ struct PrinterConfigView: View {
|
|||
}
|
||||
}
|
||||
.onAppear {
|
||||
//NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
bonjourBrowser.enableScan(DispatchQueue.main)
|
||||
}
|
||||
.onDisappear {
|
||||
bonjourBrowser.disableScan()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import UserNotifications
|
||||
import AppKit
|
||||
import Network
|
||||
|
||||
|
@ -21,6 +22,7 @@ struct SoyuzMenuBarExtraView: View {
|
|||
@State var printPercentage: Double = 0
|
||||
|
||||
@Binding var currentMenuBarIcon: String
|
||||
var notification = UserNotificationHandler.shared
|
||||
|
||||
@State var hotendHotTemp: Bool = false
|
||||
@State var bedHotTemp: Bool = false
|
||||
|
@ -53,7 +55,7 @@ struct SoyuzMenuBarExtraView: View {
|
|||
// Hot-end temperature
|
||||
HStack {
|
||||
Image(systemName: "flame")
|
||||
.foregroundColor( printerManager.extruderTemperature > DANGERTEMP ? .red : .white )
|
||||
.foregroundColor( printerManager.extruderTemperature > DANGERTEMP ? .red : Color(nsColor: .labelColor))
|
||||
.opacity( printerManager.extruderTemperature > DANGERTEMP ? 1.0 : 0.3 )
|
||||
Text("Hotend")
|
||||
.font(.headline)
|
||||
|
@ -63,7 +65,7 @@ struct SoyuzMenuBarExtraView: View {
|
|||
// Bed temperature
|
||||
HStack {
|
||||
Image(systemName: "flame")
|
||||
.foregroundColor( printerManager.bedTemperature > DANGERTEMP ? .red : .white )
|
||||
.foregroundColor( printerManager.bedTemperature > DANGERTEMP ? .red : Color(nsColor: .labelColor) )
|
||||
.opacity( printerManager.bedTemperature > DANGERTEMP ? 1.0 : 0.3 )
|
||||
Text("Plate")
|
||||
.font(.headline)
|
||||
|
@ -79,7 +81,6 @@ struct SoyuzMenuBarExtraView: View {
|
|||
// Footer information
|
||||
HStack {
|
||||
Button {
|
||||
print("Button pressed")
|
||||
openWindow(id: "soyuz_cfg")
|
||||
} label: {
|
||||
Text("Printers")
|
||||
|
|
Loading…
Reference in New Issue