diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 3a822d45..fc609027 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -70,6 +70,8 @@ DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC3B273283F411B00AC321C /* LastHeardText.swift */; }; DDC4D568275499A500A4208E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4D567275499A500A4208E /* Persistence.swift */; }; DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; }; + DDCE4E2E286B7BC400BE9F8F /* StoreForwaredConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2D286B7BC400BE9F8F /* StoreForwaredConfig.swift */; }; + DDCE4E30286B7BD800BE9F8F /* InputBrokerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2F286B7BD800BE9F8F /* InputBrokerConfig.swift */; }; DDCFF601285453A7005FA625 /* localonly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCFF600285453A7005FA625 /* localonly.pb.swift */; }; DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; }; DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; }; @@ -165,6 +167,8 @@ DDC3B273283F411B00AC321C /* LastHeardText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastHeardText.swift; sourceTree = ""; }; DDC4D567275499A500A4208E /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserConfig.swift; sourceTree = ""; }; + DDCE4E2D286B7BC400BE9F8F /* StoreForwaredConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForwaredConfig.swift; sourceTree = ""; }; + DDCE4E2F286B7BD800BE9F8F /* InputBrokerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputBrokerConfig.swift; sourceTree = ""; }; DDCFF600285453A7005FA625 /* localonly.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = localonly.pb.swift; sourceTree = ""; }; DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = ""; }; DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = ""; }; @@ -243,8 +247,8 @@ DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, DD8169FE272476C700F4AB02 /* LogDocument.swift */, DD6B85A728009258000ACD6B /* ShareChannel.swift */, - DD61937A2863876A00E59241 /* Config */, DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */, + DD61937A2863876A00E59241 /* Config */, ); path = Settings; sourceTree = ""; @@ -269,6 +273,8 @@ DD41582928585C32009B0E59 /* RangeTestConfig.swift */, DD6193782863875F00E59241 /* SerialConfig.swift */, DD415827285859C4009B0E59 /* TelemetryConfig.swift */, + DDCE4E2D286B7BC400BE9F8F /* StoreForwaredConfig.swift */, + DDCE4E2F286B7BD800BE9F8F /* InputBrokerConfig.swift */, ); path = Module; sourceTree = ""; @@ -636,6 +642,7 @@ C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */, DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, + DDCE4E30286B7BD800BE9F8F /* InputBrokerConfig.swift in Sources */, DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */, DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, @@ -665,6 +672,7 @@ DDAF8C6726ED0C8C0058C060 /* remote_hardware.pb.swift in Sources */, DDAF8C6526ED0A490058C060 /* channel.pb.swift in Sources */, DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */, + DDCE4E2E286B7BC400BE9F8F /* StoreForwaredConfig.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index bf283cf8..38615180 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1037,4 +1037,109 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } + + public func getModuleConfig (configType: AdminMessage.ModuleConfigType, destNum: Int64, wantResponse: Bool) -> Bool { + + var adminPacket = AdminMessage() + adminPacket.getModuleConfigRequest = configType + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.rangeTest = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.serial = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. MyInfoEntity? { let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") diff --git a/Meshtastic/Meshtastic.entitlements b/Meshtastic/Meshtastic.entitlements index 0d89936b..3d8c755d 100644 --- a/Meshtastic/Meshtastic.entitlements +++ b/Meshtastic/Meshtastic.entitlements @@ -4,7 +4,7 @@ com.apple.developer.associated-domains - applinks:meshtastic.org/e/ + applinks:meshtastic.org/* com.apple.security.app-sandbox diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents index db1356bd..a9000be5 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents @@ -104,7 +104,7 @@ - + @@ -136,16 +136,16 @@ + + + + - - - - \ No newline at end of file diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 681c36a6..89be5a4d 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -19,7 +19,22 @@ struct MeshtasticAppleApp: App { .environment(\.managedObjectContext, persistenceController.container.viewContext) .environmentObject(bleManager) .environmentObject(userSettings) - .onOpenURL(perform: { (url) in + .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in + + print("Continue activity \(userActivity)") + guard let url = userActivity.webpageURL else { + return + } + + print("User wants to open URL: \(url)") + // TODO same handling as done in onOpenURL() + + } + .onOpenURL(perform: { (url) in + + print("URL OPENED") + print(url) + //we are expecting a .mbtiles map file that contains raster data //save it to the documents directory, and name it offline_map.mbtiles let fileManager = FileManager.default @@ -43,9 +58,7 @@ struct MeshtasticAppleApp: App { } else { print("💥 Didn't save the map file") } - - } - ) + }) } .onChange(of: scenePhase) { (newScenePhase) in switch newScenePhase { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 7192dac0..9cc4e1b6 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -153,7 +153,6 @@ struct DisplayConfig: View { Text("How long the screen remains on after the user button is pressed or messages are received.") .font(.caption) - .listRowSeparator(.visible) Picker("Carousel Interval", selection: $screenCarouselInterval ) { ForEach(ScreenCarouselSeconds.allCases) { scs in diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index c1da8dfb..be7d8a59 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -297,9 +297,12 @@ struct LoRaConfig: View { } .onChange(of: region) { newRegion in - if newRegion != node.loRaConfig!.regionCode { + if node.loRaConfig != nil { - hasChanges = true + if newRegion != node.loRaConfig!.regionCode { + + hasChanges = true + } } } .onChange(of: modemPreset) { newModemPreset in diff --git a/Meshtastic/Views/Settings/Config/Module/InputBrokerConfig.swift b/Meshtastic/Views/Settings/Config/Module/InputBrokerConfig.swift new file mode 100644 index 00000000..c57dc7c9 --- /dev/null +++ b/Meshtastic/Views/Settings/Config/Module/InputBrokerConfig.swift @@ -0,0 +1,8 @@ +// +// InputBrokerConfig.swift +// Meshtastic +// +// Created by Garth Vander Houwen on 6/28/22. +// + +import Foundation diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 03851530..efbf84cc 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -11,6 +11,12 @@ struct RangeTestConfig: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + var node: NodeInfoEntity + + @State private var isPresentingSaveConfirm: Bool = false + @State var initialLoad: Bool = true + @State var hasChanges = false + @State var enabled = false @State var sender = false @State var save = false @@ -46,7 +52,46 @@ struct RangeTestConfig: View { Text("Saves a CSV with the range test message details, only available on ESP32 devices with a web server.") .font(.caption) } + } + + Button { + + isPresentingSaveConfirm = true + + } label: { + + Label("Save", systemImage: "square.and.arrow.down") + } + .disabled(bleManager.connectedPeripheral == nil || !hasChanges) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + + "Are you sure?", + isPresented: $isPresentingSaveConfirm + ) { + Button("Save Range Test Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var rtc = ModuleConfig.RangeTestConfig() + rtc.enabled = enabled + rtc.save = save + rtc.sender = sender ? 1 : 0 + + if bleManager.saveRangeTestModuleConfig(config: rtc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + + } else { + + } + } + } + .navigationTitle("Range Test Config") .navigationBarItems(trailing: @@ -56,7 +101,41 @@ struct RangeTestConfig: View { }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context +// self.enabled = node.rangeTestConfig?.enabled ?? false +// self.save = node.rangeTestConfig?.save ?? false +// +// if node.rangeTestConfig?.sender != nil { +// +// self.sender = node.rangeTestConfig!.sender == 1 ? true : false +// +// } else { +// self.sender = false +// } +// self.sender = node.rangeTestConfig?.sender != nil + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: enabled) { newEnabled in + + //if newEnabled != node.rangeTestConfig!.enabled { + + hasChanges = true + //} + } + .onChange(of: save) { newSave in + + //if newSave != node.rangeTestConfig!.save { + + hasChanges = true + //} + } + .onChange(of: sender) { newSender in + + hasChanges = true } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index ad855f57..fc44b6cd 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -127,6 +127,10 @@ struct SerialConfig: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + @State private var isPresentingSaveConfirm: Bool = false + @State var initialLoad: Bool = true + @State var hasChanges = false + @State var enabled = false @State var echo = false @State var rxd = 0 @@ -154,6 +158,8 @@ struct SerialConfig: View { Label("Echo", systemImage: "repeat") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("If set, any packets you send will be echoed back to your device.") + .font(.caption) Picker("Baud Rate", selection: $baudRate ) { ForEach(SerialBaudRates.allCases) { sbr in @@ -168,6 +174,8 @@ struct SerialConfig: View { } } .pickerStyle(DefaultPickerStyle()) + Text("The amount of time to wait before we consider your packet as done.") + .font(.caption) Picker("Mode", selection: $mode ) { ForEach(SerialModeTypes.allCases) { smt in @@ -207,8 +215,48 @@ struct SerialConfig: View { } } .pickerStyle(DefaultPickerStyle()) + Text("Set the GPIO pins for RXD and TXD.") + .font(.caption) } } + + Button { + + isPresentingSaveConfirm = true + + } label: { + + Label("Save", systemImage: "square.and.arrow.down") + } + .disabled(bleManager.connectedPeripheral == nil || !hasChanges) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .confirmationDialog( + + "Are you sure?", + isPresented: $isPresentingSaveConfirm + ) { + Button("Save Range Test Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var sc = ModuleConfig.SerialConfig() + sc.enabled = enabled + //sc.save = save + //sc.sender = sender ? 1 : 0 + + if bleManager.saveSerialModuleConfig(config: sc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) { + + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + + } else { + + } + } + } + .navigationTitle("Serial Config") .navigationBarItems(trailing: diff --git a/Meshtastic/Views/Settings/Config/Module/StoreForwaredConfig.swift b/Meshtastic/Views/Settings/Config/Module/StoreForwaredConfig.swift new file mode 100644 index 00000000..32f18fd2 --- /dev/null +++ b/Meshtastic/Views/Settings/Config/Module/StoreForwaredConfig.swift @@ -0,0 +1,8 @@ +// +// StoreForwaredConfig.swift +// Meshtastic +// +// Created by Garth Vander Houwen on 6/28/22. +// + +import Foundation diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 94ee8d03..ae44be5e 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -192,20 +192,16 @@ struct PositionConfig: View { Label("Smart Position Broadcast", systemImage: "location.fill.viewfinder") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - if !smartPositionEnabled { - Picker("Position Broadcast Interval", selection: $positionBroadcastSeconds) { - ForEach(PositionBroadcastIntervals.allCases) { at in - Text(at.description) - } + Picker("Position Broadcast Interval", selection: $positionBroadcastSeconds) { + ForEach(PositionBroadcastIntervals.allCases) { at in + Text(at.description) } - .pickerStyle(DefaultPickerStyle()) - - Text("We should send our position this often (but only if it has changed significantly)") - .font(.caption) - .listRowSeparator(.visible) - } + .pickerStyle(DefaultPickerStyle()) + + Text("We should send our position this often (but only if it has changed significantly)") + .font(.caption) } Section(header: Text("Position Flags - Non Functional")) { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 17c1d11e..8f6947fa 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -17,7 +17,7 @@ struct Settings: View { sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], animation: .default) - private var nodes: FetchedResults + private var nodes: FetchedResults var body: some View { NavigationView { @@ -34,6 +34,14 @@ struct Settings: View { .symbolRenderingMode(.hierarchical) Text("App Settings") } + Text("Apple app specific settings and features, app username, share position with mesh options, map and keyboard type, mesh log.") + .font(.caption) + .fixedSize(horizontal: false, vertical: true) + + } + + Section("Radio Configuration") { + NavigationLink { ShareChannel() } label: { @@ -41,13 +49,9 @@ struct Settings: View { .symbolRenderingMode(.hierarchical) Text("Share Channel QR Code") } - } - - Section("Radio Configuration") { Text("Radio config views will be be enabled when there is a connected node. Save buttons will be enabled when there are config changes to save.") .font(.caption) - .listRowSeparator(.visible) .fixedSize(horizontal: false, vertical: true) NavigationLink { @@ -110,15 +114,16 @@ struct Settings: View { } Section("Module Configuration - Non Functional interaction preview.") { -// NavigationLink { -// PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) -// } label: { -// -// Image(systemName: "list.bullet.rectangle.fill") -// .symbolRenderingMode(.hierarchical) -// -// Text("Canned Messages") -// } + NavigationLink { + PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) + } label: { + + Image(systemName: "list.bullet.rectangle.fill") + .symbolRenderingMode(.hierarchical) + + Text("Canned Messages") + } + .disabled(bleManager.connectedPeripheral == nil) NavigationLink { ExternalNotificationConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) @@ -131,15 +136,17 @@ struct Settings: View { } NavigationLink { - RangeTestConfig() + RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) } label: { Image(systemName: "point.3.connected.trianglepath.dotted") .symbolRenderingMode(.hierarchical) - Text("Range Test") + Text("Range Test (ESP32 Only)") } - //.disabled(!(nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) || bleManager.connectedPeripheral == nil) + .disabled(!(bleManager.connectedPeripheral == nil)) + //nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) || + // nodes.first(where: { $0.num == connectedNodeNum })!.rangeTestConfig != nil) NavigationLink { SerialConfig() @@ -170,6 +177,18 @@ struct Settings: View { } .listStyle(GroupedListStyle()) .navigationTitle("Settings") + .onAppear { + + // + + // let connectedNode = + +// if nodes.first(where: { $0.num == (bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0) })!.rangeTestConfig == nil { +// +// // We have no Range Test Config, go get it +// self.bleManager.getModuleConfig(configType: AdminMessage.ModuleConfigType.rangetestConfig, destNum: bleManager.connectedPeripheral.num, wantResponse: tr) +// } + } } } } diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index a580da78..1def1199 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -68,16 +68,18 @@ struct UserConfig: View { } .keyboardType(.default) .disableAutocorrection(true) - .listRowSeparator(.visible) + Text("Long name can me 40 bytes long.") + .font(.caption) HStack { Label("Short Name", systemImage: "circlebadge.fill") TextField("Long Name", text: $shortName) .foregroundColor(.gray) } - .keyboardType(.default) + .keyboardType(.asciiCapable) .disableAutocorrection(true) - .listRowSeparator(.visible) + Text("The short name is used in maps and messaging and will be appended to the last 4 of the device MAC address to set the device's BLE Name. It can be up to 5 bytes long.") + .font(.caption) }