From 5f429b8cb48030d4118c6f45aaa0757ccc203bd8 Mon Sep 17 00:00:00 2001 From: Madeline Date: Wed, 8 Feb 2023 20:19:23 -0500 Subject: [PATCH] Add NWBrowser and NWConnection code. TODO: Refactor all of this stuff better --- KlipperMon.xcodeproj/project.pbxproj | 20 ++- KlipperMon/ContentView.swift | 85 --------- KlipperMon/Info.plist | 25 +++ KlipperMon/KlipperMonApp.swift | 28 ++- KlipperMon/KlipperMonMenuBarExtraView.swift | 183 +++++++++++++++++--- KlipperMon/KlipperWebsocket.swift | 9 - KlipperMon/PrinterConfigView.swift | 20 +++ KlipperMon/PrinterObjectsQuery.swift | 24 +++ KlipperMon/PrinterRequestManager.swift | 47 +++++ 9 files changed, 293 insertions(+), 148 deletions(-) delete mode 100644 KlipperMon/ContentView.swift create mode 100644 KlipperMon/Info.plist delete mode 100644 KlipperMon/KlipperWebsocket.swift create mode 100644 KlipperMon/PrinterConfigView.swift create mode 100644 KlipperMon/PrinterRequestManager.swift diff --git a/KlipperMon.xcodeproj/project.pbxproj b/KlipperMon.xcodeproj/project.pbxproj index a621982..baa7bab 100644 --- a/KlipperMon.xcodeproj/project.pbxproj +++ b/KlipperMon.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* 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 */; }; - E180B5EB2992CD9100425DB0 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5EA2992CD9100425DB0 /* ContentView.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 */; }; @@ -16,9 +16,9 @@ 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 */; }; - E180B61B2992CF2200425DB0 /* KlipperWebsocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B61A2992CF2200425DB0 /* KlipperWebsocket.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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -39,9 +39,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + E124B9D72993FE5500C0D2D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterConfigView.swift; sourceTree = ""; }; 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 = ""; }; - E180B5EA2992CD9100425DB0 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E180B5EC2992CD9200425DB0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; E180B5EF2992CD9200425DB0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; E180B5F12992CD9200425DB0 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; @@ -52,9 +53,9 @@ 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 = ""; }; E180B60B2992CD9300425DB0 /* KlipperMonUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonUITestsLaunchTests.swift; sourceTree = ""; }; - E180B61A2992CF2200425DB0 /* KlipperWebsocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperWebsocket.swift; sourceTree = ""; }; E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterObjectsQuery.swift; sourceTree = ""; }; E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlipperMonMenuBarExtraView.swift; sourceTree = ""; }; + E180B6212993256E00425DB0 /* PrinterRequestManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterRequestManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -105,16 +106,17 @@ E180B5E72992CD9100425DB0 /* KlipperMon */ = { isa = PBXGroup; children = ( + E124B9D72993FE5500C0D2D2 /* Info.plist */, E180B5E82992CD9100425DB0 /* KlipperMonApp.swift */, - E180B5EA2992CD9100425DB0 /* ContentView.swift */, E180B5EC2992CD9200425DB0 /* Assets.xcassets */, E180B5F12992CD9200425DB0 /* Persistence.swift */, E180B5F62992CD9200425DB0 /* KlipperMon.entitlements */, E180B5F32992CD9200425DB0 /* KlipperMon.xcdatamodeld */, E180B5EE2992CD9200425DB0 /* Preview Content */, - E180B61A2992CF2200425DB0 /* KlipperWebsocket.swift */, E180B61C2992D53700425DB0 /* PrinterObjectsQuery.swift */, E180B61E2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift */, + E180B6212993256E00425DB0 /* PrinterRequestManager.swift */, + E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */, ); path = KlipperMon; sourceTree = ""; @@ -276,10 +278,10 @@ files = ( E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */, E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */, - E180B61B2992CF2200425DB0 /* KlipperWebsocket.swift in Sources */, + E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */, E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */, - E180B5EB2992CD9100425DB0 /* ContentView.swift in Sources */, E180B5E92992CD9100425DB0 /* KlipperMonApp.swift in Sources */, + E180B6222993256E00425DB0 /* PrinterRequestManager.swift in Sources */, E180B61F2992DBB000425DB0 /* KlipperMonMenuBarExtraView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -444,6 +446,7 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = KlipperMon/Info.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -471,6 +474,7 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = KlipperMon/Info.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/KlipperMon/ContentView.swift b/KlipperMon/ContentView.swift deleted file mode 100644 index d6a57e5..0000000 --- a/KlipperMon/ContentView.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// ContentView.swift -// KlipperMon -// -// Created by maddiefuzz on 2/7/23. -// - -import SwiftUI -import CoreData - -struct ContentView: View { - @Environment(\.managedObjectContext) private var viewContext - - @FetchRequest( - sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], - animation: .default) - private var items: FetchedResults - - var body: some View { - NavigationView { - List { - ForEach(items) { item in - NavigationLink { - Text("Item at \(item.timestamp!, formatter: itemFormatter)") - } label: { - Text(item.timestamp!, formatter: itemFormatter) - } - } - .onDelete(perform: deleteItems) - } - .toolbar { - ToolbarItem { - Button(action: addItem) { - Label("Add Item", systemImage: "plus") - } - } - } - Text("Select an item") - } - } - - private func addItem() { - withAnimation { - let newItem = Item(context: viewContext) - newItem.timestamp = Date() - - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - } - } - - private func deleteItems(offsets: IndexSet) { - withAnimation { - offsets.map { items[$0] }.forEach(viewContext.delete) - - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - } - } -} - -private let itemFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .medium - return formatter -}() - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) - } -} diff --git a/KlipperMon/Info.plist b/KlipperMon/Info.plist new file mode 100644 index 0000000..50e6c17 --- /dev/null +++ b/KlipperMon/Info.plist @@ -0,0 +1,25 @@ + + + + + NSBonjourServices + + _http._tcp. + + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLName + soyuz + CFBundleURLSchemes + + soyuz + + + + NSServices + + + diff --git a/KlipperMon/KlipperMonApp.swift b/KlipperMon/KlipperMonApp.swift index 7449117..8392428 100644 --- a/KlipperMon/KlipperMonApp.swift +++ b/KlipperMon/KlipperMonApp.swift @@ -7,7 +7,6 @@ import SwiftUI - @main struct KlipperMonMenuBarApp: App { let persistenceController = PersistenceController.shared @@ -15,29 +14,22 @@ struct KlipperMonMenuBarApp: App { @State var currentIcon = "move.3d" var body: some Scene { - WindowGroup { - ContentView() + 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) } } - -protocol MenuBarExtraIconUpdater { - func updateIcon(systemName: String) -} - -struct KlipperMonApp: App { - let persistenceController = PersistenceController.shared - - var body: some Scene { - WindowGroup { - ContentView() - .environment(\.managedObjectContext, persistenceController.container.viewContext) - } - } -} diff --git a/KlipperMon/KlipperMonMenuBarExtraView.swift b/KlipperMon/KlipperMonMenuBarExtraView.swift index 0dc49d1..0ffbfa8 100644 --- a/KlipperMon/KlipperMonMenuBarExtraView.swift +++ b/KlipperMon/KlipperMonMenuBarExtraView.swift @@ -6,46 +6,172 @@ // import SwiftUI +import AppKit +import Network + +struct KlipperMenuBarButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding() + .foregroundColor(.white) + } +} 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 { - Label(String(printPercentage), systemImage: "thermometer.snowflake.circle") - .onReceive(timer) { input in - Task { - self.printPercentage = await self.getPrintPercentage() + 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..." } } - - Button("Check Printer") { - currentMenuBarIcon = "flame" } - } - - func getPrintPercentage() async -> Double { - guard let url = URL(string: "http://10.0.21.39/printer/objects/query?extruder=temperature") 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!") - return -1 + // Testing bonjour stuff + .onAppear { + nwBrowser.browseResultsChangedHandler = { (newResults, changes) in + print("[update] Results changed.") + newResults.forEach { result in + print(result) + self.nwBrowserDiscoveredItems.append(result.endpoint) + } + //self.nwBrowserDiscoveredItems.append(newResults.description) + } + nwBrowser.stateUpdateHandler = { newState in + switch newState { + case .failed(let error): + print("[error] nwbrowser: \(error)") + case .ready: + print("[ready] nwbrowser") + case .setup: + print("[setup] nwbrowser") + default: + break + } + } + nwBrowser.start(queue: DispatchQueue.main) + } + ForEach(nwBrowserDiscoveredItems, id: \.hashValue) { item in + Button { + let connection = NWConnection(to: item, using: .tcp) + connection.stateUpdateHandler = { newState in + switch newState { + case .failed(let error): + print("[error] nwconnection: \(error)") + case .ready: + print("[ready] nwconnection") + default: + break + } + } + connection.start(queue: DispatchQueue.main) + } label: { + Text(item.debugDescription) } - print(String(data: data, encoding: .utf8)) - let decoder = JSONDecoder() - let printerObjectsQuery = try decoder.decode(PrinterObjectsQuery.self, from: data) - return printerObjectsQuery.result.status.extruder.temperature - // handle data as JSON - } catch { - print("Error!") - return -1 } } } @@ -56,3 +182,4 @@ struct KlipperMonMenuBarExtraView_Previews: PreviewProvider { KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentMenuBarIcon) } } + diff --git a/KlipperMon/KlipperWebsocket.swift b/KlipperMon/KlipperWebsocket.swift deleted file mode 100644 index 4ca3db2..0000000 --- a/KlipperMon/KlipperWebsocket.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// KlipperWebsocket.swift -// KlipperMon -// -// Created by maddiefuzz on 2/7/23. -// - -import Foundation - diff --git a/KlipperMon/PrinterConfigView.swift b/KlipperMon/PrinterConfigView.swift new file mode 100644 index 0000000..b80d8bc --- /dev/null +++ b/KlipperMon/PrinterConfigView.swift @@ -0,0 +1,20 @@ +// +// PrinterConfigView.swift +// KlipperMon +// +// Created by maddiefuzz on 2/8/23. +// + +import SwiftUI + +struct PrinterConfigView: View { + var body: some View { + Text("Config Printer In Here") + } +} + +struct PrinterConfigView_Previews: PreviewProvider { + static var previews: some View { + PrinterConfigView() + } +} diff --git a/KlipperMon/PrinterObjectsQuery.swift b/KlipperMon/PrinterObjectsQuery.swift index 192eb12..970b143 100644 --- a/KlipperMon/PrinterObjectsQuery.swift +++ b/KlipperMon/PrinterObjectsQuery.swift @@ -17,9 +17,33 @@ struct ResultsData: Decodable { } 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 } diff --git a/KlipperMon/PrinterRequestManager.swift b/KlipperMon/PrinterRequestManager.swift new file mode 100644 index 0000000..c02d57d --- /dev/null +++ b/KlipperMon/PrinterRequestManager.swift @@ -0,0 +1,47 @@ +// +// PrinterRequestManager.swift +// KlipperMon +// +// Created by maddiefuzz on 2/7/23. +// + +import Foundation +import Network + +@MainActor +class PrinterRequestManager: ObservableObject { + @Published var printerObjectsQuery: PrinterObjectsQuery? + + @Published var printerCommsOkay = false + + static let shared = PrinterRequestManager() + + //let nwBrowser = NWBrowser(for: .bonjour(type: "_moonraker._tcp", domain: "local."), using: .tcp) + + private init() { + + } + + func queryPrinterStats() async { + guard let url = URL(string: "http://10.0.21.39/printer/objects/query?extruder&virtual_sdcard&print_stats&heater_bed") else { + fatalError("Missing URL") + } + + let urlRequest = URLRequest(url: url) + do { + let (data, response) = try await URLSession.shared.data(for: urlRequest) + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + print("Error with response.") + return + } + // handle data as JSON + let decoder = JSONDecoder() + printerObjectsQuery = try decoder.decode(PrinterObjectsQuery.self, from: data) + printerCommsOkay = true + //return printerObjectsQuery.result.status.extruder.temperature + } catch { + print("Exception thrown: \(error)") + printerCommsOkay = false + } + } +}