From a3ef868802894eb8573bd3920d672879c7355475 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 30 Dec 2023 09:21:42 -0800 Subject: [PATCH] Add cal topo, wip firmware page --- Meshtastic.xcodeproj/project.pbxproj | 8 +- Meshtastic/Enums/SerialConfigEnums.swift | 5 + Meshtastic/Helpers/BLEManager.swift | 9 +- .../contents | 1 + .../Settings/Config/Module/SerialConfig.swift | 12 + Meshtastic/Views/Settings/Firmware.swift | 302 +++++++++--------- Meshtastic/Views/Settings/FirmwareApi.swift | 39 +++ 7 files changed, 218 insertions(+), 158 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index ed6be43b..0052e112 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; }; DD2AD8A8296D2DF9001FF0E7 /* MapViewSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */; }; DD2DC2C029BCD8AB003B383C /* HardwareModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */; }; + DD33DB622B3D27C7003E1EA0 /* FirmwareApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD33DB612B3D27C7003E1EA0 /* FirmwareApi.swift */; }; DD3501892852FC3B000FC853 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3501882852FC3B000FC853 /* Settings.swift */; }; DD3619152B1EF9F900C41C8C /* LocationsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */; }; DD3CC6B528E33FD100FA9159 /* ShareChannels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3CC6B428E33FD100FA9159 /* ShareChannels.swift */; }; @@ -247,6 +248,8 @@ DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewSwiftUI.swift; sourceTree = ""; }; DD2CC2E52ABFE04E00EDFDA7 /* MeshtasticDataModelV19.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV19.xcdatamodel; sourceTree = ""; }; DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardwareModels.swift; sourceTree = ""; }; + DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 23.xcdatamodel"; sourceTree = ""; }; + DD33DB612B3D27C7003E1EA0 /* FirmwareApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirmwareApi.swift; sourceTree = ""; }; DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; DD3619132B1EE20700C41C8C /* MeshtasticDataModelV21.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV21.xcdatamodel; sourceTree = ""; }; DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsHandler.swift; sourceTree = ""; }; @@ -550,6 +553,7 @@ DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */, DDA0B6B1294CDC55001356EC /* Channels.swift */, DDD6EEAE29BC024700383354 /* Firmware.swift */, + DD33DB612B3D27C7003E1EA0 /* FirmwareApi.swift */, DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */, DD3501882852FC3B000FC853 /* Settings.swift */, @@ -1190,6 +1194,7 @@ DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */, DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */, DD007BAE2AA4E91200F5FA12 /* MyInfoEntityExtension.swift in Sources */, + DD33DB622B3D27C7003E1EA0 /* FirmwareApi.swift in Sources */, DD3CC6B528E33FD100FA9159 /* ShareChannels.swift in Sources */, DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */, DD5E5207298EE33B00D21B61 /* connection_status.pb.swift in Sources */, @@ -1790,6 +1795,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */, DD295CE92B323ED9002CC4AC /* MeshtasticDataModelV22.xcdatamodel */, DD3619132B1EE20700C41C8C /* MeshtasticDataModelV21.xcdatamodel */, DDAB580B2B0D913500147258 /* MeshtasticDataModelV20.xcdatamodel */, @@ -1813,7 +1819,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DD295CE92B323ED9002CC4AC /* MeshtasticDataModelV22.xcdatamodel */; + currentVersion = DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Enums/SerialConfigEnums.swift b/Meshtastic/Enums/SerialConfigEnums.swift index 770ddb19..046860f9 100644 --- a/Meshtastic/Enums/SerialConfigEnums.swift +++ b/Meshtastic/Enums/SerialConfigEnums.swift @@ -111,6 +111,7 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable { case proto = 2 case txtmsg = 3 case nmea = 4 + case caltopo = 5 var id: Int { self.rawValue } var description: String { @@ -125,6 +126,8 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable { return "serial.mode.txtmsg".localized case .nmea: return "serial.mode.nmea".localized + case .caltopo: + return "serial.mode.caltopo".localized } } func protoEnumValue() -> ModuleConfig.SerialConfig.Serial_Mode { @@ -141,6 +144,8 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable { return ModuleConfig.SerialConfig.Serial_Mode.textmsg case .nmea: return ModuleConfig.SerialConfig.Serial_Mode.nmea + case .caltopo: + return ModuleConfig.SerialConfig.Serial_Mode.caltopo } } } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index df3219b6..3834aa13 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -759,16 +759,15 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate if UserDefaults.provideLocation { let interval = UserDefaults.provideLocationInterval > 0 ? UserDefaults.provideLocationInterval : 30 if positionTimer != nil { - } - positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval(interval), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true) - if positionTimer != nil { - RunLoop.current.add(positionTimer!, forMode: .common) + positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval(interval), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true) + if positionTimer != nil { + RunLoop.current.add(positionTimer!, forMode: .common) + } } } return } - case FROMNUM_UUID: print("🗞️ BLE (Notify) characteristic, value will be read next") diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 23.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 23.xcdatamodel/contents index 3b7675c0..8085cfa4 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 23.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 23.xcdatamodel/contents @@ -309,6 +309,7 @@ + diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index 4386b3ca..2715e951 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -23,7 +23,10 @@ struct SerialConfig: View { @State var txd = 0 @State var baudRate = 0 @State var timeout = 0 + @State var overrideConsoleSerialPort = false @State var mode = 0 + + var body: some View { VStack { @@ -153,6 +156,7 @@ struct SerialConfig: View { sc.txd = UInt32(txd) sc.baud = SerialBaudRates(rawValue: baudRate)!.protoEnumValue() sc.timeout = UInt32(timeout) + sc.overrideConsoleSerialPort = overrideConsoleSerialPort sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue() let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0) @@ -231,6 +235,13 @@ struct SerialConfig: View { if newTimeout != node!.serialConfig!.timeout { hasChanges = true } } } + .onChange(of: overrideConsoleSerialPort) { newOverrideConsoleSerialPort in + + if node != nil && node!.serialConfig != nil { + + if newOverrideConsoleSerialPort != node!.serialConfig!.overrideConsoleSerialPort { hasChanges = true } + } + } .onChange(of: mode) { newMode in if node != nil && node!.serialConfig != nil { @@ -248,6 +259,7 @@ struct SerialConfig: View { self.baudRate = Int(node?.serialConfig?.baudRate ?? 0) self.timeout = Int(node?.serialConfig?.timeout ?? 0) self.mode = Int(node?.serialConfig?.mode ?? 0) + self.overrideConsoleSerialPort = false // node?.serialConfig?.overrideConsoleSerialPort ?? false self.hasChanges = false } } diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 20985c3f..55ec03ce 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -18,12 +18,13 @@ struct Firmware: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager var node: NodeInfoEntity? - @State var minimumVersion = "2.1.0" + @State var minimumVersion = "2.2.16" @State var version = "" - @State private var firmwareReleaseData: FirmwareRelease = FirmwareRelease() + //var currentDevice: DeviceHardware + var body: some View { // NavigationSplitView { - NavigationStack { + VStack { let hwModel: HardwareModels = HardwareModels.allCases.first(where: { $0.rawValue == node?.user?.hwModel ?? "UNSET" }) ?? HardwareModels.UNSET VStack(alignment: .leading) { Text("Current Version: \(bleManager.connectedVersion)") @@ -91,166 +92,163 @@ struct Firmware: View { Text(hwModel.platform().description) .font(.title3) } - }.padding() + } + Spacer() VStack(alignment: .leading) { Text("Firmware Releases") .font(.title3) .padding([.leading, .trailing]) - List { - Section(header: Text("Stable")) { - ForEach(firmwareReleaseData.releases?.stable ?? [], id: \.id) { fr in - Link(destination: URL(string: fr.zipUrl ?? "")!) { - HStack { - Text(fr.title ?? "Unknown") - .font(.caption) - Spacer() - Image(systemName: "square.and.arrow.down") - .font(.title3) - } - } - } - } - Section("Alpha") { - ForEach(firmwareReleaseData.releases?.alpha ?? [], id: \.id) { fr in - Link(destination: URL(string: fr.zipUrl ?? "")!) { - HStack { - Text(fr.title ?? "Unknown") - .font(.caption) - Spacer() - Image(systemName: "square.and.arrow.down") - .font(.title3) - } - } - } - } - Section("Pull Requests") { - ForEach(firmwareReleaseData.pullRequests ?? [], id: \.id) { fr in - Link(destination: URL(string: fr.zipUrl ?? "")!) { - HStack { - Text(fr.title ?? "Unknown") - .font(.caption) - Spacer() - Image(systemName: "square.and.arrow.down") - .font(.title3) - } - } +// List { +// Section(header: Text("Stable")) { +// ForEach(firmwareReleaseData.releases?.stable ?? [], id: \.id) { fr in +// Link(destination: URL(string: fr.zipUrl ?? "")!) { +// HStack { +// Text(fr.title ?? "Unknown") +// .font(.caption) +// Spacer() +// Image(systemName: "square.and.arrow.down") +// .font(.title3) +// } +// } +// } +// } +// Section("Alpha") { +// ForEach(firmwareReleaseData.releases?.alpha ?? [], id: \.id) { fr in +// Link(destination: URL(string: fr.zipUrl ?? "")!) { +// HStack { +// Text(fr.title ?? "Unknown") +// .font(.caption) +// Spacer() +// Image(systemName: "square.and.arrow.down") +// .font(.title3) +// } +// } +// } +// } +// Section("Pull Requests") { +// ForEach(firmwareReleaseData.pullRequests ?? [], id: \.id) { fr in +// Link(destination: URL(string: fr.zipUrl ?? "")!) { +// HStack { +// Text(fr.title ?? "Unknown") +// .font(.caption) +// Spacer() +// Image(systemName: "square.and.arrow.down") +// .font(.title3) +// } +// } +// } +// } +// } + } + .padding(.bottom, 5) + .onAppear() { + Api().loadDeviceHardwareData { (hw) in + for device in hw { + if device.hwModelSlug == node?.user?.hwModel ?? "UNSET" { + print("Selected: \(device)") } } } +// Api().loadFirmwareReleaseData { (bks) in +// //sel = bks +// } } - .padding(.bottom, 5) - .onAppear(perform: loadData) .navigationTitle("Firmware Updates") .navigationBarTitleDisplayMode(.inline) } } - func loadData() { - guard let url = URL(string: "https://api.meshtastic.org/github/firmware/list") else { - return - } - let request = URLRequest(url: url) - URLSession.shared.dataTask(with: request) { data, _, _ in - if let data = data { - if let response_obj = try? JSONDecoder().decode(FirmwareRelease.self, from: data) { - DispatchQueue.main.async { - self.firmwareReleaseData = response_obj - } - } - } - }.resume() - } } -struct FirmwareRelease: Codable { - var releases: Releases? = Releases() - var pullRequests: [PullRequests]? = [] - enum CodingKeys: String, CodingKey { - case releases = "Releases" - case pullRequests = "Pull Requests" - } - init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - releases = try values.decodeIfPresent(Releases.self, forKey: .releases ) - pullRequests = try values.decodeIfPresent([PullRequests].self, forKey: .pullRequests ) - } - init() { - } -} - -struct Releases: Codable { - var stable: [Stable]? = [] - var alpha: [Alpha]? = [] - enum CodingKeys: String, CodingKey { - case stable = "Stable" - case alpha = "Alpha" - } - init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - stable = try values.decodeIfPresent([Stable].self, forKey: .stable ) - alpha = try values.decodeIfPresent([Alpha].self, forKey: .alpha ) - } - init() {} -} - -struct Alpha: Codable { - var id: String? - var title: String? - var pageUrl: String? - var zipUrl: String? - enum CodingKeys: String, CodingKey { - case id = "id" - case title = "title" - case pageUrl = "page_url" - case zipUrl = "zip_url" - } - init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - id = try values.decodeIfPresent(String.self, forKey: .id ) - title = try values.decodeIfPresent(String.self, forKey: .title ) - pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) - zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) - } - init() {} -} - -struct Stable: Codable { - var id: String? - var title: String? - var pageUrl: String? - var zipUrl: String? - enum CodingKeys: String, CodingKey { - case id = "id" - case title = "title" - case pageUrl = "page_url" - case zipUrl = "zip_url" - } - init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - id = try values.decodeIfPresent(String.self, forKey: .id ) - title = try values.decodeIfPresent(String.self, forKey: .title ) - pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) - zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) - } - init() {} -} - -struct PullRequests: Codable { - var id: String? - var title: String? - var pageUrl: String? - var zipUrl: String? - enum CodingKeys: String, CodingKey { - case id = "id" - case title = "title" - case pageUrl = "page_url" - case zipUrl = "zip_url" - } - init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - id = try values.decodeIfPresent(String.self, forKey: .id ) - title = try values.decodeIfPresent(String.self, forKey: .title ) - pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) - zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) - } - init() {} -} +//struct FirmwareRelease: Codable { +// var releases: Releases? = Releases() +// var pullRequests: [PullRequests]? = [] +// enum CodingKeys: String, CodingKey { +// case releases = "Releases" +// case pullRequests = "Pull Requests" +// } +// init(from decoder: Decoder) throws { +// let values = try decoder.container(keyedBy: CodingKeys.self) +// releases = try values.decodeIfPresent(Releases.self, forKey: .releases ) +// pullRequests = try values.decodeIfPresent([PullRequests].self, forKey: .pullRequests ) +// } +// init() { +// } +//} +// +//struct Releases: Codable { +// var stable: [Stable]? = [] +// var alpha: [Alpha]? = [] +// enum CodingKeys: String, CodingKey { +// case stable = "Stable" +// case alpha = "Alpha" +// } +// init(from decoder: Decoder) throws { +// let values = try decoder.container(keyedBy: CodingKeys.self) +// stable = try values.decodeIfPresent([Stable].self, forKey: .stable ) +// alpha = try values.decodeIfPresent([Alpha].self, forKey: .alpha ) +// } +// init() {} +//} +// +//struct Alpha: Codable { +// var id: String? +// var title: String? +// var pageUrl: String? +// var zipUrl: String? +// enum CodingKeys: String, CodingKey { +// case id = "id" +// case title = "title" +// case pageUrl = "page_url" +// case zipUrl = "zip_url" +// } +// init(from decoder: Decoder) throws { +// let values = try decoder.container(keyedBy: CodingKeys.self) +// id = try values.decodeIfPresent(String.self, forKey: .id ) +// title = try values.decodeIfPresent(String.self, forKey: .title ) +// pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) +// zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) +// } +// init() {} +//} +// +//struct Stable: Codable { +// var id: String? +// var title: String? +// var pageUrl: String? +// var zipUrl: String? +// enum CodingKeys: String, CodingKey { +// case id = "id" +// case title = "title" +// case pageUrl = "page_url" +// case zipUrl = "zip_url" +// } +// init(from decoder: Decoder) throws { +// let values = try decoder.container(keyedBy: CodingKeys.self) +// id = try values.decodeIfPresent(String.self, forKey: .id ) +// title = try values.decodeIfPresent(String.self, forKey: .title ) +// pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) +// zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) +// } +// init() {} +//} +// +//struct PullRequests: Codable { +// var id: String? +// var title: String? +// var pageUrl: String? +// var zipUrl: String? +// enum CodingKeys: String, CodingKey { +// case id = "id" +// case title = "title" +// case pageUrl = "page_url" +// case zipUrl = "zip_url" +// } +// init(from decoder: Decoder) throws { +// let values = try decoder.container(keyedBy: CodingKeys.self) +// id = try values.decodeIfPresent(String.self, forKey: .id ) +// title = try values.decodeIfPresent(String.self, forKey: .title ) +// pageUrl = try values.decodeIfPresent(String.self, forKey: .pageUrl ) +// zipUrl = try values.decodeIfPresent(String.self, forKey: .zipUrl ) +// } +// init() {} +//} diff --git a/Meshtastic/Views/Settings/FirmwareApi.swift b/Meshtastic/Views/Settings/FirmwareApi.swift index 1a80debe..bc075ce7 100644 --- a/Meshtastic/Views/Settings/FirmwareApi.swift +++ b/Meshtastic/Views/Settings/FirmwareApi.swift @@ -6,3 +6,42 @@ // import Foundation + +struct DeviceHardware: Codable { + var hwModel: Int + var hwModelSlug: String + var platformioTarget: String + var activelySupported: Bool + var displayName: String +} + +class Api : ObservableObject{ +// @Published var devices = [DeviceHardware]() + + func loadDeviceHardwareData(completion:@escaping ([DeviceHardware]) -> ()) { + guard let url = URL(string: "https://api.meshtastic.org/resource/deviceHardware") else { + print("Invalid url...") + return + } + URLSession.shared.dataTask(with: url) { data, response, error in + let deviceHardware = try! JSONDecoder().decode([DeviceHardware].self, from: data!) + print(deviceHardware) + DispatchQueue.main.async { + completion(deviceHardware) + } + }.resume() + } +// func loadFirmwareReleaseData(completion:@escaping ([FirmwareRelease]) -> ()) { +// guard let url = URL(string: "https://api.meshtastic.org/github/firmware/list") else { +// print("Invalid url...") +// return +// } +// URLSession.shared.dataTask(with: url) { data, response, error in +// let deviceHardware = try! JSONDecoder().decode([FirmwareRelease].self, from: data!) +// print(deviceHardware) +// DispatchQueue.main.async { +// completion(deviceHardware) +// } +// }.resume() +// } +}