Compare commits
10 Commits
ab849a721b
...
8912745f7a
Author | SHA1 | Date |
---|---|---|
Madeline Pace | 8912745f7a | |
Madeline Pace | e04a6bc31f | |
Madeline Pace | 0f0d0c0628 | |
Madeline | d21fd9d331 | |
Madeline | 0a8670ed71 | |
Madeline | ba42ff1207 | |
Madeline | 6341b0464d | |
Madeline | ff551c9a5f | |
Madeline | 65daae4bf7 | |
Madeline | d084ebb1ac |
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
//
|
||||
// KlipperMonApp.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct KlipperMonMenuBarApp: App {
|
||||
let persistenceController = PersistenceController.shared
|
||||
|
||||
@State var currentIcon = "move.3d"
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup(id: "floating-stats") {
|
||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
//.frame(width: 300, height: 140)
|
||||
}
|
||||
//.windowResizability(.contentSize)
|
||||
|
||||
Window("Configuration", id: "soyuz_cfg", content: {
|
||||
PrinterConfigView()
|
||||
})
|
||||
|
||||
MenuBarExtra("Soyuz", systemImage: currentIcon) {
|
||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||
.padding([.top, .leading, .trailing], 8)
|
||||
.padding([.bottom], 6)
|
||||
}
|
||||
.menuBarExtraStyle(.window)
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
//
|
||||
// KlipperMonMenuBarExtraView.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
@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()
|
||||
|
||||
// TODO: Use @published API data instead of instance state variable
|
||||
var body: some View {
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct KlipperMonMenuBarExtraView_Previews: PreviewProvider {
|
||||
@State static var currentMenuBarIcon = "move.3d"
|
||||
static var previews: some View {
|
||||
KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentMenuBarIcon)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
//
|
||||
// PrinterObjectsQuery.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct PrinterObjectsQuery: Decodable {
|
||||
let result: ResultsData
|
||||
}
|
||||
|
||||
struct ResultsData: Decodable {
|
||||
let eventtime: Double
|
||||
let status: StatusData
|
||||
}
|
||||
|
||||
struct StatusData: Decodable {
|
||||
let virtual_sdcard: VirtualSDCardData
|
||||
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 {
|
||||
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
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
//
|
||||
// PrinterRequestManager.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import Starscream
|
||||
|
||||
struct JsonRpcRequest: Codable {
|
||||
var jsonrpc = "2.0"
|
||||
let method: String
|
||||
let params: [String: [String: String?]]
|
||||
var id = 1
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(jsonrpc, forKey: .jsonrpc)
|
||||
try container.encode(method, forKey: .method)
|
||||
try container.encode(params, forKey: .params)
|
||||
try container.encode(id, forKey: .id)
|
||||
}
|
||||
}
|
||||
|
||||
class PrinterRequestManager: ObservableObject, WebSocketDelegate {
|
||||
func didReceive(event: Starscream.WebSocketEvent, client: Starscream.WebSocket) {
|
||||
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()
|
||||
|
||||
private init() {
|
||||
// MARK: Bonjour browser initialization at instantiation
|
||||
nwBrowser.browseResultsChangedHandler = { (newResults, changes) in
|
||||
print("[update] Results changed.")
|
||||
newResults.forEach { result in
|
||||
print(result)
|
||||
self.nwBrowserDiscoveredItems.append(result.endpoint)
|
||||
}
|
||||
}
|
||||
// State update handler
|
||||
nwBrowser.stateUpdateHandler = { newState in
|
||||
switch newState {
|
||||
case .failed(let error):
|
||||
print("[error] nwbrowser: \(error)")
|
||||
case .ready:
|
||||
print("[ready] nwbrowser")
|
||||
if let innerEndpoint = self.connection?.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
||||
print("Connected to:", "\(host):\(port)")
|
||||
}
|
||||
case .setup:
|
||||
print("[setup] nwbrowser")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
// Start up the bonjour browser, get results and process them in the update handler
|
||||
nwBrowser.start(queue: DispatchQueue.main)
|
||||
}
|
||||
|
||||
// Called from the UI, providing an endpoint.
|
||||
// Momentarily connect/disconnects from the endpoint to retrieve the host/port
|
||||
// calls private function openWebsocket to process the host/port
|
||||
func resolveBonjourHost(_ endpoint: NWEndpoint) {
|
||||
connection = NWConnection(to: endpoint, using: .tcp)
|
||||
connection.stateUpdateHandler = { [self] state in
|
||||
switch state {
|
||||
case .ready:
|
||||
if let innerEndpoint = connection.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
||||
print("Connected to \(host):\(port)")
|
||||
let hostString = "\(host)"
|
||||
let regex = try! Regex("%en0")
|
||||
let match = hostString.firstMatch(of: regex)
|
||||
let sanitizedHost = hostString.replacingOccurrences(of: match!.0, with: "")
|
||||
|
||||
print("[sanitized] Resolved \(sanitizedHost):\(port)")
|
||||
socketHost = sanitizedHost
|
||||
socketPort = "\(port)"
|
||||
connection.cancel()
|
||||
self.openWebsocket()
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
connection.start(queue: .global())
|
||||
}
|
||||
|
||||
// Opens the websocket connection
|
||||
// TODO: host and port should be function arguments probably maybe
|
||||
private func openWebsocket() {
|
||||
if let host = socketHost, let port = socketPort {
|
||||
//let fullUrlString = "http://\(socketHost):\(socketPort)/websocket"
|
||||
var request = URLRequest(url: URL(string: "http://\(host):\(port)/websocket")!)
|
||||
request.timeoutInterval = 5
|
||||
socket = WebSocket(request: request)
|
||||
socket.delegate = self
|
||||
socket.connect()
|
||||
}
|
||||
}
|
||||
|
||||
// Old REST way to do it
|
||||
// TODO: Stop using this.
|
||||
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
|
||||
} catch {
|
||||
print("Exception thrown: \(error)")
|
||||
printerCommsOkay = false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// KlipperMonTests.swift
|
||||
// KlipperMonTests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import KlipperMon
|
||||
|
||||
final class KlipperMonTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// Any test you write for XCTest can be annotated as throws and async.
|
||||
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
|
||||
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
|
||||
}
|
||||
|
||||
func testPerformanceExample() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
//
|
||||
// KlipperMonUITestsLaunchTests.swift
|
||||
// KlipperMonUITests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class KlipperMonUITestsLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
func testLaunch() throws {
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Insert steps here to perform after app launch but before taking a screenshot,
|
||||
// such as logging into a test account or navigating somewhere in the app
|
||||
|
||||
let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||
attachment.name = "Launch Screen"
|
||||
attachment.lifetime = .keepAlways
|
||||
add(attachment)
|
||||
}
|
||||
}
|
182
KlipperMon.xcodeproj/project.pbxproj → Soyuz.xcodeproj/project.pbxproj
Normal file → Executable file
|
@ -8,17 +8,20 @@
|
|||
|
||||
/* 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 */; };
|
||||
E16378B429A491E6002F05E9 /* MoonrakerSocketManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */; };
|
||||
E180B5E92992CD9100425DB0 /* SoyuzApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* SoyuzApp.swift */; };
|
||||
E180B5ED2992CD9200425DB0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E180B5EC2992CD9200425DB0 /* 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 */; };
|
||||
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E180B5F32992CD9200425DB0 /* KlipperMon.xcdatamodeld */; };
|
||||
E180B6002992CD9300425DB0 /* KlipperMonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */; };
|
||||
E180B60A2992CD9300425DB0 /* KlipperMonUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6092992CD9300425DB0 /* KlipperMonUITests.swift */; };
|
||||
E180B60C2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */; };
|
||||
E180B6002992CD9300425DB0 /* SoyuzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5FF2992CD9300425DB0 /* SoyuzTests.swift */; };
|
||||
E180B60A2992CD9300425DB0 /* SoyuzUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6092992CD9300425DB0 /* SoyuzUITests.swift */; };
|
||||
E180B60C2992CD9300425DB0 /* SoyuzUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B60B2992CD9300425DB0 /* SoyuzUITestsLaunchTests.swift */; };
|
||||
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */; };
|
||||
E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */; };
|
||||
E180B6222993256E00425DB0 /* PrinterRequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6212993256E00425DB0 /* PrinterRequestManager.swift */; };
|
||||
E180B61F2992DBB000425DB0 /* SoyuzMenuBarExtraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61E2992DBB000425DB0 /* SoyuzMenuBarExtraView.swift */; };
|
||||
E180B6222993256E00425DB0 /* MoonrakerSocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B6212993256E00425DB0 /* MoonrakerSocketManager.swift */; };
|
||||
E1A93C6729C932E200BAE750 /* BonjourBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A93C6629C932E200BAE750 /* BonjourBrowser.swift */; };
|
||||
E1A93C6929CD627100BAE750 /* BonjourBrowserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A93C6829CD627100BAE750 /* BonjourBrowserTests.swift */; };
|
||||
E1E8B07729949E2700BABE4B /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = E1E8B07629949E2700BABE4B /* Starscream */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -42,21 +45,24 @@
|
|||
/* 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; };
|
||||
E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonApp.swift; sourceTree = "<group>"; };
|
||||
E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerTests.swift; sourceTree = "<group>"; };
|
||||
E180B5E52992CD9100425DB0 /* Soyuz.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Soyuz.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E180B5E82992CD9100425DB0 /* SoyuzApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoyuzApp.swift; 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>"; };
|
||||
E180B5F12992CD9200425DB0 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||
E180B5F42992CD9200425DB0 /* KlipperMon.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = KlipperMon.xcdatamodel; sourceTree = "<group>"; };
|
||||
E180B5F62992CD9200425DB0 /* KlipperMon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KlipperMon.entitlements; sourceTree = "<group>"; };
|
||||
E180B5FB2992CD9300425DB0 /* KlipperMonTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KlipperMonTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonTests.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||
E180B5F62992CD9200425DB0 /* Soyuz.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Soyuz.entitlements; sourceTree = "<group>"; };
|
||||
E180B5FB2992CD9300425DB0 /* SoyuzTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SoyuzTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E180B5FF2992CD9300425DB0 /* SoyuzTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoyuzTests.swift; sourceTree = "<group>"; };
|
||||
E180B6052992CD9300425DB0 /* SoyuzUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SoyuzUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E180B6092992CD9300425DB0 /* SoyuzUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoyuzUITests.swift; sourceTree = "<group>"; };
|
||||
E180B60B2992CD9300425DB0 /* SoyuzUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoyuzUITestsLaunchTests.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>"; };
|
||||
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterRequestManager.swift; sourceTree = "<group>"; };
|
||||
E180B61E2992DBB000425DB0 /* SoyuzMenuBarExtraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoyuzMenuBarExtraView.swift; sourceTree = "<group>"; };
|
||||
E180B6212993256E00425DB0 /* MoonrakerSocketManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManager.swift; sourceTree = "<group>"; };
|
||||
E1A93C6629C932E200BAE750 /* BonjourBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BonjourBrowser.swift; sourceTree = "<group>"; };
|
||||
E1A93C6829CD627100BAE750 /* BonjourBrowserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BonjourBrowserTests.swift; path = SoyuzTests/BonjourBrowserTests.swift; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -88,9 +94,9 @@
|
|||
E180B5DC2992CD9100425DB0 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E180B5E72992CD9100425DB0 /* KlipperMon */,
|
||||
E180B5FE2992CD9300425DB0 /* KlipperMonTests */,
|
||||
E180B6082992CD9300425DB0 /* KlipperMonUITests */,
|
||||
E180B5E72992CD9100425DB0 /* Soyuz */,
|
||||
E180B5FE2992CD9300425DB0 /* SoyuzTests */,
|
||||
E180B6082992CD9300425DB0 /* SoyuzUITests */,
|
||||
E180B5E62992CD9100425DB0 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
|
@ -98,29 +104,27 @@
|
|||
E180B5E62992CD9100425DB0 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E180B5E52992CD9100425DB0 /* KlipperMon.app */,
|
||||
E180B5FB2992CD9300425DB0 /* KlipperMonTests.xctest */,
|
||||
E180B6052992CD9300425DB0 /* KlipperMonUITests.xctest */,
|
||||
E180B5E52992CD9100425DB0 /* Soyuz.app */,
|
||||
E180B5FB2992CD9300425DB0 /* SoyuzTests.xctest */,
|
||||
E180B6052992CD9300425DB0 /* SoyuzUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E180B5E72992CD9100425DB0 /* KlipperMon */ = {
|
||||
E180B5E72992CD9100425DB0 /* Soyuz */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E1A93C6529C92B3500BAE750 /* ViewModels */,
|
||||
E1A93C6429C92AEE00BAE750 /* Views */,
|
||||
E124B9D72993FE5500C0D2D2 /* Info.plist */,
|
||||
E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */,
|
||||
E180B5E82992CD9100425DB0 /* SoyuzApp.swift */,
|
||||
E180B5EC2992CD9200425DB0 /* Assets.xcassets */,
|
||||
E180B5F12992CD9200425DB0 /* Persistence.swift */,
|
||||
E180B5F62992CD9200425DB0 /* KlipperMon.entitlements */,
|
||||
E180B5F62992CD9200425DB0 /* Soyuz.entitlements */,
|
||||
E180B5F32992CD9200425DB0 /* KlipperMon.xcdatamodeld */,
|
||||
E180B5EE2992CD9200425DB0 /* Preview Content */,
|
||||
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */,
|
||||
E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */,
|
||||
E180B6212993256E00425DB0 /* PrinterRequestManager.swift */,
|
||||
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */,
|
||||
);
|
||||
path = KlipperMon;
|
||||
path = Soyuz;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E180B5EE2992CD9200425DB0 /* Preview Content */ = {
|
||||
|
@ -131,29 +135,50 @@
|
|||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E180B5FE2992CD9300425DB0 /* KlipperMonTests */ = {
|
||||
E180B5FE2992CD9300425DB0 /* SoyuzTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E180B5FF2992CD9300425DB0 /* KlipperMonTests.swift */,
|
||||
E180B5FF2992CD9300425DB0 /* SoyuzTests.swift */,
|
||||
E1A93C6829CD627100BAE750 /* BonjourBrowserTests.swift */,
|
||||
E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */,
|
||||
);
|
||||
path = KlipperMonTests;
|
||||
path = SoyuzTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E180B6082992CD9300425DB0 /* KlipperMonUITests */ = {
|
||||
E180B6082992CD9300425DB0 /* SoyuzUITests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E180B6092992CD9300425DB0 /* KlipperMonUITests.swift */,
|
||||
E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */,
|
||||
E180B6092992CD9300425DB0 /* SoyuzUITests.swift */,
|
||||
E180B60B2992CD9300425DB0 /* SoyuzUITestsLaunchTests.swift */,
|
||||
);
|
||||
path = KlipperMonUITests;
|
||||
path = SoyuzUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E1A93C6429C92AEE00BAE750 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E180B61E2992DBB000425DB0 /* SoyuzMenuBarExtraView.swift */,
|
||||
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E1A93C6529C92B3500BAE750 /* ViewModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */,
|
||||
E180B6212993256E00425DB0 /* MoonrakerSocketManager.swift */,
|
||||
E1A93C6629C932E200BAE750 /* BonjourBrowser.swift */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
E180B5E42992CD9100425DB0 /* KlipperMon */ = {
|
||||
E180B5E42992CD9100425DB0 /* Soyuz */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = E180B60F2992CD9300425DB0 /* Build configuration list for PBXNativeTarget "KlipperMon" */;
|
||||
buildConfigurationList = E180B60F2992CD9300425DB0 /* Build configuration list for PBXNativeTarget "Soyuz" */;
|
||||
buildPhases = (
|
||||
E180B5E12992CD9100425DB0 /* Sources */,
|
||||
E180B5E22992CD9100425DB0 /* Frameworks */,
|
||||
|
@ -163,17 +188,17 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = KlipperMon;
|
||||
name = Soyuz;
|
||||
packageProductDependencies = (
|
||||
E1E8B07629949E2700BABE4B /* Starscream */,
|
||||
);
|
||||
productName = KlipperMon;
|
||||
productReference = E180B5E52992CD9100425DB0 /* KlipperMon.app */;
|
||||
productReference = E180B5E52992CD9100425DB0 /* Soyuz.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
E180B5FA2992CD9300425DB0 /* KlipperMonTests */ = {
|
||||
E180B5FA2992CD9300425DB0 /* SoyuzTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = E180B6122992CD9300425DB0 /* Build configuration list for PBXNativeTarget "KlipperMonTests" */;
|
||||
buildConfigurationList = E180B6122992CD9300425DB0 /* Build configuration list for PBXNativeTarget "SoyuzTests" */;
|
||||
buildPhases = (
|
||||
E180B5F72992CD9300425DB0 /* Sources */,
|
||||
E180B5F82992CD9300425DB0 /* Frameworks */,
|
||||
|
@ -184,14 +209,14 @@
|
|||
dependencies = (
|
||||
E180B5FD2992CD9300425DB0 /* PBXTargetDependency */,
|
||||
);
|
||||
name = KlipperMonTests;
|
||||
name = SoyuzTests;
|
||||
productName = KlipperMonTests;
|
||||
productReference = E180B5FB2992CD9300425DB0 /* KlipperMonTests.xctest */;
|
||||
productReference = E180B5FB2992CD9300425DB0 /* SoyuzTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
E180B6042992CD9300425DB0 /* KlipperMonUITests */ = {
|
||||
E180B6042992CD9300425DB0 /* SoyuzUITests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = E180B6152992CD9300425DB0 /* Build configuration list for PBXNativeTarget "KlipperMonUITests" */;
|
||||
buildConfigurationList = E180B6152992CD9300425DB0 /* Build configuration list for PBXNativeTarget "SoyuzUITests" */;
|
||||
buildPhases = (
|
||||
E180B6012992CD9300425DB0 /* Sources */,
|
||||
E180B6022992CD9300425DB0 /* Frameworks */,
|
||||
|
@ -202,9 +227,9 @@
|
|||
dependencies = (
|
||||
E180B6072992CD9300425DB0 /* PBXTargetDependency */,
|
||||
);
|
||||
name = KlipperMonUITests;
|
||||
name = SoyuzUITests;
|
||||
productName = KlipperMonUITests;
|
||||
productReference = E180B6052992CD9300425DB0 /* KlipperMonUITests.xctest */;
|
||||
productReference = E180B6052992CD9300425DB0 /* SoyuzUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
@ -230,7 +255,7 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = E180B5E02992CD9100425DB0 /* Build configuration list for PBXProject "KlipperMon" */;
|
||||
buildConfigurationList = E180B5E02992CD9100425DB0 /* Build configuration list for PBXProject "Soyuz" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
|
@ -246,9 +271,9 @@
|
|||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
E180B5E42992CD9100425DB0 /* KlipperMon */,
|
||||
E180B5FA2992CD9300425DB0 /* KlipperMonTests */,
|
||||
E180B6042992CD9300425DB0 /* KlipperMonUITests */,
|
||||
E180B5E42992CD9100425DB0 /* Soyuz */,
|
||||
E180B5FA2992CD9300425DB0 /* SoyuzTests */,
|
||||
E180B6042992CD9300425DB0 /* SoyuzUITests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -288,9 +313,10 @@
|
|||
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */,
|
||||
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */,
|
||||
E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */,
|
||||
E180B6222993256E00425DB0 /* PrinterRequestManager.swift in Sources */,
|
||||
E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */,
|
||||
E1A93C6729C932E200BAE750 /* BonjourBrowser.swift in Sources */,
|
||||
E180B5E92992CD9100425DB0 /* SoyuzApp.swift in Sources */,
|
||||
E180B6222993256E00425DB0 /* MoonrakerSocketManager.swift in Sources */,
|
||||
E180B61F2992DBB000425DB0 /* SoyuzMenuBarExtraView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -298,7 +324,9 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E180B6002992CD9300425DB0 /* KlipperMonTests.swift in Sources */,
|
||||
E180B6002992CD9300425DB0 /* SoyuzTests.swift in Sources */,
|
||||
E16378B429A491E6002F05E9 /* MoonrakerSocketManagerTests.swift in Sources */,
|
||||
E1A93C6929CD627100BAE750 /* BonjourBrowserTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -306,8 +334,8 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E180B60A2992CD9300425DB0 /* KlipperMonUITests.swift in Sources */,
|
||||
E180B60C2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift in Sources */,
|
||||
E180B60A2992CD9300425DB0 /* SoyuzUITests.swift in Sources */,
|
||||
E180B60C2992CD9300425DB0 /* SoyuzUITestsLaunchTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -316,12 +344,12 @@
|
|||
/* Begin PBXTargetDependency section */
|
||||
E180B5FD2992CD9300425DB0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = E180B5E42992CD9100425DB0 /* KlipperMon */;
|
||||
target = E180B5E42992CD9100425DB0 /* Soyuz */;
|
||||
targetProxy = E180B5FC2992CD9300425DB0 /* PBXContainerItemProxy */;
|
||||
};
|
||||
E180B6072992CD9300425DB0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = E180B5E42992CD9100425DB0 /* KlipperMon */;
|
||||
target = E180B5E42992CD9100425DB0 /* Soyuz */;
|
||||
targetProxy = E180B6062992CD9300425DB0 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
@ -445,23 +473,25 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = KlipperMon/KlipperMon.entitlements;
|
||||
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;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"KlipperMon/Preview Content\"";
|
||||
DEVELOPMENT_ASSET_PATHS = "Soyuz/Preview\\ Content";
|
||||
DEVELOPMENT_TEAM = W9ASV855X5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = KlipperMon/Info.plist;
|
||||
INFOPLIST_FILE = Soyuz/Info.plist;
|
||||
INFOPLIST_KEY_LSUIElement = NO;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = info.maddie.KlipperMon;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = info.maddie.Soyuz;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -473,23 +503,25 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = KlipperMon/KlipperMon.entitlements;
|
||||
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;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"KlipperMon/Preview Content\"";
|
||||
DEVELOPMENT_ASSET_PATHS = "Soyuz/Preview\\ Content";
|
||||
DEVELOPMENT_TEAM = W9ASV855X5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = KlipperMon/Info.plist;
|
||||
INFOPLIST_FILE = Soyuz/Info.plist;
|
||||
INFOPLIST_KEY_LSUIElement = NO;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = info.maddie.KlipperMon;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = info.maddie.Soyuz;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -511,7 +543,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KlipperMon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/KlipperMon";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Soyuz.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Soyuz";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -530,7 +562,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KlipperMon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/KlipperMon";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Soyuz.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Soyuz";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -571,7 +603,7 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
E180B5E02992CD9100425DB0 /* Build configuration list for PBXProject "KlipperMon" */ = {
|
||||
E180B5E02992CD9100425DB0 /* Build configuration list for PBXProject "Soyuz" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
E180B60D2992CD9300425DB0 /* Debug */,
|
||||
|
@ -580,7 +612,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
E180B60F2992CD9300425DB0 /* Build configuration list for PBXNativeTarget "KlipperMon" */ = {
|
||||
E180B60F2992CD9300425DB0 /* Build configuration list for PBXNativeTarget "Soyuz" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
E180B6102992CD9300425DB0 /* Debug */,
|
||||
|
@ -589,7 +621,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
E180B6122992CD9300425DB0 /* Build configuration list for PBXNativeTarget "KlipperMonTests" */ = {
|
||||
E180B6122992CD9300425DB0 /* Build configuration list for PBXNativeTarget "SoyuzTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
E180B6132992CD9300425DB0 /* Debug */,
|
||||
|
@ -598,7 +630,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
E180B6152992CD9300425DB0 /* Build configuration list for PBXNativeTarget "KlipperMonUITests" */ = {
|
||||
E180B6152992CD9300425DB0 /* Build configuration list for PBXNativeTarget "SoyuzUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
E180B6162992CD9300425DB0 /* Debug */,
|
0
KlipperMon.xcodeproj/project.xcworkspace/contents.xcworkspacedata → Soyuz.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file → Executable file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "starscream",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/daltoniam/Starscream.git",
|
||||
"state" : {
|
||||
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
|
||||
"version" : "4.0.4"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>KlipperMon.xcscheme_^#shared#^_</key>
|
||||
<key>Soyuz.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "9DCD6317-B85A-47F1-8DA6-BE708C290036"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "1FA57A18-B3EC-4819-A5C7-77D0EA7B1B3C"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "SoyuzTests/BonjourBrowserTests.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "33"
|
||||
endingLineNumber = "33"
|
||||
landmarkName = "testBonjourDiscoveredItemsNotNil()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "E361E24C-A6D3-4C02-B11D-290874A15033"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "SoyuzTests/MoonrakerSocketManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "34"
|
||||
endingLineNumber = "34"
|
||||
landmarkName = "testBlah()"
|
||||
landmarkType = "7">
|
||||
<Locations>
|
||||
<Location
|
||||
uuid = "E361E24C-A6D3-4C02-B11D-290874A15033 - 16ea1fa406b6a8b5"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "SoyuzTests.PrinterRequestManagerTests.testBlah() -> ()"
|
||||
moduleName = "SoyuzTests"
|
||||
usesParentBreakpointCondition = "Yes"
|
||||
urlString = "file:///Volumes/SNAP/Users/averyadapace/Code/MacOS/KlipperMon/SoyuzTests/MoonrakerSocketManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "34"
|
||||
endingLineNumber = "34"
|
||||
offsetFromSymbolStart = "404">
|
||||
</Location>
|
||||
<Location
|
||||
uuid = "E361E24C-A6D3-4C02-B11D-290874A15033 - 16ea1fa406b6a8b5"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "SoyuzTests.PrinterRequestManagerTests.testBlah() -> ()"
|
||||
moduleName = "SoyuzTests"
|
||||
usesParentBreakpointCondition = "Yes"
|
||||
urlString = "file:///Volumes/SNAP/Users/averyadapace/Code/MacOS/KlipperMon/SoyuzTests/MoonrakerSocketManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "34"
|
||||
endingLineNumber = "34"
|
||||
offsetFromSymbolStart = "820">
|
||||
</Location>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
|
@ -0,0 +1,19 @@
|
|||
<?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>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>KlipperMon.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>Soyuz.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
0
KlipperMon/Assets.xcassets/AccentColor.colorset/Contents.json → Soyuz/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file → Executable file
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 791 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,347 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "57.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "114.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "50.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "72.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "144.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"filename" : "16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "64.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "48.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "24x24",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "55.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "27.5x27.5",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "66.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "33x33",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "88.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "44x44",
|
||||
"subtype" : "40mm"
|
||||
},
|
||||
{
|
||||
"filename" : "92.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "46x46",
|
||||
"subtype" : "41mm"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"filename" : "102.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "51x51",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "54x54",
|
||||
"subtype" : "49mm"
|
||||
},
|
||||
{
|
||||
"filename" : "172.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "86x86",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "196.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "98x98",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"filename" : "216.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "108x108",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "117x117",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "129x129",
|
||||
"subtype" : "49mm"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "watch-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.300",
|
||||
"green" : "0.300",
|
||||
"red" : "0.300"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
0
KlipperMon/Assets.xcassets/Contents.json → Soyuz/Assets.xcassets/Contents.json
Normal file → Executable file
|
@ -2,11 +2,13 @@
|
|||
<!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>_moonraker._tcp.</string>
|
||||
<string>_http._tcp.</string>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
@ -20,7 +22,11 @@
|
|||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSServices</key>
|
||||
<array/>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_xctest._tcp.</string>
|
||||
<string>_moonraker._tcp.</string>
|
||||
<string>_http._tcp.</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
0
KlipperMon/KlipperMon.xcdatamodeld/.xccurrentversion → Soyuz/KlipperMon.xcdatamodeld/.xccurrentversion
Normal file → Executable file
0
KlipperMon/Preview Content/Preview Assets.xcassets/Contents.json → Soyuz/Preview Content/Preview Assets.xcassets/Contents.json
Normal file → Executable file
|
@ -8,5 +8,7 @@
|
|||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// KlipperMonApp.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct SoyuzApp: App {
|
||||
let persistenceController = PersistenceController.shared
|
||||
|
||||
@State var currentIcon = "move.3d"
|
||||
|
||||
@ObservedObject static var printerManager = MoonrakerSocketManager()
|
||||
|
||||
var body: some Scene {
|
||||
// WindowGroup(id: "floating-stats") {
|
||||
// KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||
// .environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
// }
|
||||
|
||||
WindowGroup("Configuration", id: "soyuz_cfg", content: {
|
||||
PrinterConfigView(printerManager: SoyuzApp.printerManager)
|
||||
//.frame(minWidth: 300, maxWidth: 600, minHeight: 60, maxHeight: 100)
|
||||
})
|
||||
.windowResizability(.contentSize)
|
||||
|
||||
MenuBarExtra("Soyuz", systemImage: currentIcon) {
|
||||
SoyuzMenuBarExtraView(printerManager: SoyuzApp.printerManager, currentMenuBarIcon: $currentIcon)
|
||||
.padding([.top, .leading, .trailing], 8)
|
||||
.padding([.bottom], 6)
|
||||
}
|
||||
.menuBarExtraStyle(.window)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// BonjourBrowser.swift
|
||||
// Soyuz
|
||||
//
|
||||
// Created by maddiefuzz on 3/20/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
// Protocol defining minimal API for network discovery
|
||||
// MARK: Net Discovery Protocol
|
||||
protocol NetworkDiscoveryEngine {
|
||||
func startScan(queue: DispatchQueue)
|
||||
|
||||
func setBrowseResultsChangedHandler(_ handler: @escaping ((Set<NWBrowser.Result>, Set<NWBrowser.Result.Change>) -> Void))
|
||||
func setStateUpdateHandler(_ handler: @escaping ((NWBrowser.State) -> Void))
|
||||
}
|
||||
|
||||
extension NWBrowser: NetworkDiscoveryEngine {
|
||||
|
||||
func startScan(queue: DispatchQueue) {
|
||||
start(queue: queue)
|
||||
}
|
||||
|
||||
func setBrowseResultsChangedHandler(_ handler: @escaping ((Set<NWBrowser.Result>, Set<NWBrowser.Result.Change>) -> Void)) {
|
||||
self.browseResultsChangedHandler = handler
|
||||
}
|
||||
|
||||
func setStateUpdateHandler(_ handler: @escaping ((State) -> Void)) {
|
||||
self.stateUpdateHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: BonjourBrowser
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
//
|
||||
// MoonrakerSocketManager.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import AppKit
|
||||
import Starscream
|
||||
|
||||
|
||||
class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
||||
let WEBSOCKET_TIMEOUT_INTERVAL: TimeInterval = 60.0
|
||||
|
||||
// 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
|
||||
|
||||
// Published NWConnection for listing connection information
|
||||
@Published var connection: NWConnection?
|
||||
|
||||
private var socket: WebSocket?
|
||||
private var lastPingDate = Date()
|
||||
private var starscreamEngine: Engine
|
||||
|
||||
|
||||
// MARK: PRM init()
|
||||
init(starscreamEngine: Engine = WSEngine(transport: TCPTransport())) {
|
||||
state = ""
|
||||
progress = 0.0
|
||||
extruderTemperature = 0.0
|
||||
bedTemperature = 0.0
|
||||
socketHost = ""
|
||||
socketPort = ""
|
||||
|
||||
self.starscreamEngine = starscreamEngine
|
||||
|
||||
// Set up sleep/wake notification observers
|
||||
let center = NSWorkspace.shared.notificationCenter;
|
||||
let mainQueue = OperationQueue.main
|
||||
|
||||
center.addObserver(forName: NSWorkspace.screensDidWakeNotification, object: nil, queue: mainQueue) { notification in
|
||||
self.screenChangedSleepState(notification)
|
||||
}
|
||||
|
||||
center.addObserver(forName: NSWorkspace.screensDidSleepNotification, object: nil, queue: mainQueue) { notification in
|
||||
self.screenChangedSleepState(notification)
|
||||
}
|
||||
}
|
||||
|
||||
// Called from the UI with an endpoint.
|
||||
// Momentarily connect/disconnects from the endpoint to retrieve the host/port
|
||||
// calls private function openWebsocket to process the host/port
|
||||
func connectToBonjourEndpoint(_ endpoint: NWEndpoint) {
|
||||
// Debug stuff
|
||||
endpoint.txtRecord?.forEach({ (key: String, value: NWTXTRecord.Entry) in
|
||||
print("\(key): \(value)")
|
||||
})
|
||||
|
||||
if connection == nil {
|
||||
connection = NWConnection(to: endpoint, using: .tcp)
|
||||
}
|
||||
|
||||
connection?.stateUpdateHandler = { [self] state in
|
||||
switch state {
|
||||
case .ready:
|
||||
if let innerEndpoint = connection?.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
||||
let hostPortDebugOutput = "Connected to \(host):\(port)"
|
||||
|
||||
print(hostPortDebugOutput)
|
||||
|
||||
let hostString = "\(host)"
|
||||
let regex = try! Regex("%(.+)")
|
||||
let match = hostString.firstMatch(of: regex)
|
||||
|
||||
let sanitizedHost = hostString.replacingOccurrences(of: match?.0 ?? "", with: "")
|
||||
|
||||
print("[sanitized] Resolved \(sanitizedHost):\(port)")
|
||||
|
||||
connection?.cancel()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.socketHost = sanitizedHost
|
||||
self.socketPort = "\(port)"
|
||||
self.openWebsocket()
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
connection?.start(queue: .global())
|
||||
}
|
||||
|
||||
func disconnect() {
|
||||
socket?.disconnect()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private functions
|
||||
|
||||
// Opens the websocket connection
|
||||
private func openWebsocket() {
|
||||
//let fullUrlString = "http://\(socketHost):\(socketPort)/websocket"
|
||||
var request = URLRequest(url: URL(string: "http://\(socketHost):\(socketPort)/websocket")!)
|
||||
request.timeoutInterval = 5
|
||||
socket = WebSocket(request: request, engine: starscreamEngine)
|
||||
socket!.delegate = self
|
||||
print("About to connect to WebSocket at: \(request.debugDescription)")
|
||||
socket!.connect()
|
||||
}
|
||||
|
||||
private func reconnectWebsocket() {
|
||||
if socket == nil {
|
||||
return
|
||||
}
|
||||
|
||||
socket!.disconnect()
|
||||
self.openWebsocket()
|
||||
}
|
||||
|
||||
// MARK: Callbacks
|
||||
func screenChangedSleepState(_ notification: Notification) {
|
||||
switch(notification.name) {
|
||||
case NSWorkspace.screensDidSleepNotification:
|
||||
socket?.disconnect()
|
||||
case NSWorkspace.screensDidWakeNotification:
|
||||
self.openWebsocket()
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func didReceive(event: Starscream.WebSocketEvent, client: Starscream.WebSocket) {
|
||||
switch event {
|
||||
case .connected(let headers):
|
||||
isConnected = true
|
||||
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("[send] json-rpc printer.objects.subscribe query")
|
||||
})
|
||||
case .disconnected(let reason, let code):
|
||||
isConnected = false
|
||||
print("websocket is disconnected: \(reason) with code: \(code)")
|
||||
case .text(let 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)
|
||||
}
|
||||
// Check for RPC updates
|
||||
if let updateResponse = try? JSONDecoder().decode(jsonRpcUpdate.self, from: Data(string.utf8)) {
|
||||
self.parse_update(updateResponse)
|
||||
}
|
||||
case .binary(let data):
|
||||
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)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: JSON-RPC Parsing
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Properly formatted JSON-RPC Request for use with Starscream
|
||||
// MARK: JSON-RPC Request Codable
|
||||
struct JsonRpcRequest: Codable {
|
||||
var jsonrpc = "2.0"
|
||||
let method: String
|
||||
let params: [String: [String: String?]]
|
||||
var id = 1
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(jsonrpc, forKey: .jsonrpc)
|
||||
try container.encode(method, forKey: .method)
|
||||
try container.encode(params, forKey: .params)
|
||||
try container.encode(id, forKey: .id)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// PrinterObjectsQuery.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Root struct to decode for REST response
|
||||
struct PrinterObjectsQuery: Decodable {
|
||||
let result: ResultsData
|
||||
}
|
||||
|
||||
struct ResultsData: Decodable {
|
||||
let eventtime: Double
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
let virtual_sdcard: VirtualSDCardData?
|
||||
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 {
|
||||
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?
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// PrinterConfigView.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/8/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Network
|
||||
|
||||
// MARK: PrinterConfigView
|
||||
struct PrinterConfigView: View {
|
||||
@ObservedObject var printerManager: MoonrakerSocketManager
|
||||
@ObservedObject var bonjourBrowser = BonjourBrowser()
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if(printerManager.isConnected) {
|
||||
HStack {
|
||||
Image(systemName: "network")
|
||||
Text(printerManager.connection?.endpoint.toFriendlyString() ?? "Unknown Host")
|
||||
Text("\(printerManager.socketHost):\(printerManager.socketPort)")
|
||||
Button {
|
||||
printerManager.disconnect()
|
||||
} label: {
|
||||
Text("Disconnect")
|
||||
}
|
||||
}
|
||||
.frame(width: 500, height: 80)
|
||||
} else {
|
||||
VStack {
|
||||
Text("Auto-detected Printers")
|
||||
.font(.title)
|
||||
ForEach(bonjourBrowser.NDEngineResults , id: \.hashValue) { result in
|
||||
HStack {
|
||||
Text(result.endpoint.toFriendlyString())
|
||||
Button {
|
||||
printerManager.connectToBonjourEndpoint(result.endpoint)
|
||||
} label: {
|
||||
Text("Connect")
|
||||
//.foregroundColor(.white)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: 500, height: 100)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PrinterConfigView_Previews: PreviewProvider {
|
||||
@State static var printerManager = MoonrakerSocketManager()
|
||||
|
||||
static var previews: some View {
|
||||
PrinterConfigView(printerManager: printerManager)
|
||||
}
|
||||
}
|
||||
|
||||
extension NWEndpoint {
|
||||
func toFriendlyString() -> String {
|
||||
let regex = /\.(.+)/
|
||||
let match = self.debugDescription.firstMatch(of: regex)
|
||||
return self.debugDescription.replacingOccurrences(of: match!.0, with: "")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// KlipperMonMenuBarExtraView.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
import Network
|
||||
|
||||
struct SoyuzMenuBarExtraView: View {
|
||||
// The threshhold considered a burn-risk, at which point certain UI elements turn red.
|
||||
// Measured in degrees Celsius
|
||||
let DANGERTEMP = 40.0
|
||||
|
||||
@Environment(\.openWindow) var openWindow
|
||||
|
||||
@ObservedObject var printerManager: MoonrakerSocketManager
|
||||
|
||||
@State var printPercentage: Double = 0
|
||||
|
||||
@Binding var currentMenuBarIcon: String
|
||||
|
||||
@State var hotendHotTemp: Bool = false
|
||||
@State var bedHotTemp: Bool = false
|
||||
|
||||
// TODO: Use @published API data instead of instance state variable
|
||||
var body: some View {
|
||||
VStack {
|
||||
// Printer Readouts
|
||||
//if let printerStats = printerManager.printerStats {
|
||||
if(printerManager.isConnected) {
|
||||
VStack {
|
||||
Text(printerManager.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: printerManager.progress, total: 1.0)
|
||||
.progressViewStyle(.linear)
|
||||
.offset(x: 10)
|
||||
Text("\(Int(printerManager.progress * 100))%")
|
||||
.padding(2)
|
||||
.padding([.leading], 8)
|
||||
}
|
||||
// Temperatures
|
||||
HStack {
|
||||
// Hot-end temperature
|
||||
HStack {
|
||||
Image(systemName: "flame")
|
||||
.foregroundColor( printerManager.extruderTemperature > DANGERTEMP ? .red : .white )
|
||||
.opacity( printerManager.extruderTemperature > DANGERTEMP ? 1.0 : 0.3 )
|
||||
Text("Hotend")
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
//.frame(minWidth: 220, minHeight: 100)
|
||||
// Footer information
|
||||
HStack {
|
||||
Button {
|
||||
print("Button pressed")
|
||||
openWindow(id: "soyuz_cfg")
|
||||
} label: {
|
||||
Text("Printers")
|
||||
.foregroundColor(Color("ButtonForegroundColor"))
|
||||
}
|
||||
Spacer()
|
||||
if(printerManager.isConnected) {
|
||||
Image(systemName: "network")
|
||||
Text("Online")
|
||||
} else {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
Text("Offline")
|
||||
}
|
||||
}
|
||||
.padding(2)
|
||||
.frame(minWidth: 220, maxWidth: 375)
|
||||
}
|
||||
}
|
||||
|
||||
struct KlipperMonMenuBarExtraView_Previews: PreviewProvider {
|
||||
@State static var currentMenuBarIcon = "move.3d"
|
||||
@State static var printerManager = MoonrakerSocketManager()
|
||||
|
||||
static var previews: some View {
|
||||
SoyuzMenuBarExtraView(printerManager: printerManager, currentMenuBarIcon: $currentMenuBarIcon)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// BonjourBrowserTests.swift
|
||||
// SoyuzTests
|
||||
//
|
||||
// Created by maddiefuzz on 3/24/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Network
|
||||
import Combine
|
||||
@testable import Soyuz
|
||||
|
||||
class SoyuzBonjourBrowserTests: XCTestCase {
|
||||
var bonjourBrowser: BonjourBrowser?
|
||||
var bonjourListener: NWListener?
|
||||
var cancellable: AnyCancellable?
|
||||
|
||||
override func setUp() {
|
||||
do {
|
||||
bonjourListener = try NWListener(using: .tcp, on: .http)
|
||||
bonjourListener!.service = NWListener.Service(name: "Test", type: "_xctest._tcp")
|
||||
|
||||
bonjourListener!.newConnectionHandler = { newConnection in
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
print("Error: \(error)")
|
||||
}
|
||||
bonjourBrowser = BonjourBrowser(browser: NWBrowser(for: .bonjour(type: "_xctest._tcp", domain: "local."), using: .tcp))
|
||||
}
|
||||
|
||||
func testBonjourDiscoveredItemsPopulated() {
|
||||
guard let browser = bonjourBrowser else {
|
||||
XCTAssert(false)
|
||||
return
|
||||
}
|
||||
|
||||
let expectation = XCTestExpectation(description: "BonjourBrowser publishes network services")
|
||||
|
||||
cancellable = browser.$NDEngineResults
|
||||
.dropFirst()
|
||||
.sink(receiveValue: { newValue in
|
||||
if newValue.count > 0 {
|
||||
expectation.fulfill()
|
||||
}
|
||||
})
|
||||
|
||||
bonjourListener!.start(queue: DispatchQueue.main)
|
||||
wait(for: [expectation], timeout: 2)
|
||||
XCTAssert(!browser.NDEngineResults.isEmpty)
|
||||
XCTAssertEqual(browser.NDEngineResults.count, 1)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// MoonrakerSocketManagerTests.swift
|
||||
// SoyuzTests
|
||||
//
|
||||
// Created by maddiefuzz on 2/21/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Starscream
|
||||
import Combine
|
||||
import Network
|
||||
@testable import Soyuz
|
||||
|
||||
class DummyEngine: Engine {
|
||||
var delegate: Starscream.EngineDelegate?
|
||||
|
||||
@Published var startCalled = false
|
||||
@Published var stopCalled = false
|
||||
|
||||
func resetForNextTest() {
|
||||
self.startCalled = false
|
||||
self.stopCalled = false
|
||||
}
|
||||
|
||||
func register(delegate: Starscream.EngineDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
func start(request: URLRequest) {
|
||||
startCalled = true
|
||||
}
|
||||
|
||||
func stop(closeCode: UInt16) {
|
||||
stopCalled = true
|
||||
return
|
||||
}
|
||||
|
||||
func forceStop() {
|
||||
stopCalled = true
|
||||
return
|
||||
}
|
||||
|
||||
func write(data: Data, opcode: Starscream.FrameOpCode, completion: (() -> ())?) {
|
||||
return
|
||||
}
|
||||
|
||||
func write(string: String, completion: (() -> ())?) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class MoonrakerSocketManagerTests: XCTestCase {
|
||||
var socketManager: MoonrakerSocketManager?
|
||||
var bonjourListener: NWListener?
|
||||
var engine: DummyEngine!
|
||||
var cancellable: AnyCancellable?
|
||||
|
||||
override func setUp() {
|
||||
engine = DummyEngine()
|
||||
socketManager = MoonrakerSocketManager(starscreamEngine: engine!)
|
||||
|
||||
do {
|
||||
bonjourListener = try NWListener(using: .tcp, on: .http)
|
||||
|
||||
bonjourListener!.newConnectionHandler = { newConnection in
|
||||
return
|
||||
}
|
||||
|
||||
bonjourListener!.start(queue: DispatchQueue.main)
|
||||
} catch {
|
||||
print("Error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testConnectToBonjourEndpoint() {
|
||||
let endpoint = NWEndpoint.hostPort(host: "localhost", port: .http)
|
||||
print("Trying to connect to bonjour endpoint \(endpoint)")
|
||||
|
||||
// Test connecting to endpoint
|
||||
let connectExpectation = XCTestExpectation(description: "MoonrakerSocketManager.connectToBonjourEndpoint opens Starscream socket")
|
||||
|
||||
cancellable = engine.$startCalled
|
||||
.sink(receiveValue: { newValue in
|
||||
if newValue == true {
|
||||
connectExpectation.fulfill()
|
||||
}
|
||||
})
|
||||
|
||||
socketManager?.connectToBonjourEndpoint(endpoint)
|
||||
wait(for: [connectExpectation], timeout: 2)
|
||||
XCTAssertTrue(engine.startCalled)
|
||||
|
||||
// Test screen sleeping
|
||||
engine.resetForNextTest()
|
||||
let screenSleepExpectation = XCTestExpectation(description: "MoonrakerSocketManager.screenChangedSleepState screen sleep triggers Starscream socket disconnection")
|
||||
|
||||
let sleepNotification = Notification(name: NSWorkspace.screensDidSleepNotification)
|
||||
|
||||
cancellable = engine.$stopCalled
|
||||
.sink(receiveValue: { newValue in
|
||||
if newValue == true {
|
||||
screenSleepExpectation.fulfill()
|
||||
}
|
||||
})
|
||||
|
||||
socketManager?.screenChangedSleepState(sleepNotification)
|
||||
wait(for: [screenSleepExpectation], timeout: 2)
|
||||
XCTAssertTrue(engine.stopCalled)
|
||||
|
||||
// Test screen waking
|
||||
engine.resetForNextTest()
|
||||
let screenWakeExpectation = XCTestExpectation(description: "MoonrakerSocketManager.screenChangedSleepState screen wake triggers Starscream socket reconnection")
|
||||
|
||||
let wakeNotification = Notification(name: NSWorkspace.screensDidWakeNotification)
|
||||
|
||||
cancellable = engine.$startCalled
|
||||
.sink(receiveValue: { newValue in
|
||||
if newValue == true {
|
||||
screenWakeExpectation.fulfill()
|
||||
}
|
||||
})
|
||||
|
||||
socketManager?.screenChangedSleepState(wakeNotification)
|
||||
wait(for: [screenWakeExpectation], timeout: 2)
|
||||
XCTAssertTrue(engine.startCalled)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// KlipperMonTests.swift
|
||||
// KlipperMonTests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Soyuz
|
||||
|
||||
final class SoyuzTests: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
// func testExample() throws {
|
||||
// // This is an example of a functional test case.
|
||||
// // Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// // Any test you write for XCTest can be annotated as throws and async.
|
||||
// // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
|
||||
// // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
|
||||
// }
|
||||
//
|
||||
// func testPerformanceExample() throws {
|
||||
// // This is an example of a performance test case.
|
||||
// self.measure {
|
||||
// // Put the code you want to measure the time of here.
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
32
KlipperMonUITests/KlipperMonUITests.swift → SoyuzUITests/SoyuzUITests.swift
Normal file → Executable file
|
@ -22,20 +22,20 @@ final class KlipperMonUITests: XCTestCase {
|
|||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testLaunchPerformance() throws {
|
||||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
|
||||
// This measures how long it takes to launch your application.
|
||||
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||
XCUIApplication().launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
// func testExample() throws {
|
||||
// // UI tests must launch the application that they test.
|
||||
// let app = XCUIApplication()
|
||||
// app.launch()
|
||||
//
|
||||
// // Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
// }
|
||||
//
|
||||
// func testLaunchPerformance() throws {
|
||||
// if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
|
||||
// // This measures how long it takes to launch your application.
|
||||
// measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||
// XCUIApplication().launch()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// KlipperMonUITestsLaunchTests.swift
|
||||
// KlipperMonUITests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class KlipperMonUITestsLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
// func testLaunch() throws {
|
||||
// let app = XCUIApplication()
|
||||
// app.launch()
|
||||
//
|
||||
// // Insert steps here to perform after app launch but before taking a screenshot,
|
||||
// // such as logging into a test account or navigating somewhere in the app
|
||||
//
|
||||
// let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||
// attachment.name = "Launch Screen"
|
||||
// attachment.lifetime = .keepAlways
|
||||
// add(attachment)
|
||||
// }
|
||||
}
|