diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 2e3a87f1..b112b121 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -61,6 +61,7 @@ DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; }; DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; }; DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */; }; + DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */; }; DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */; }; DDA6B2EB28420A7B003E8C16 /* NodeAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6B2EA28420A7B003E8C16 /* NodeAnnotation.swift */; }; DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5226EB1DF10058C060 /* BLEManager.swift */; }; @@ -79,7 +80,7 @@ DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */; }; DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */; }; DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */; }; - DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */; }; + DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */; }; DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */; }; DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; }; @@ -178,6 +179,9 @@ DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = ""; }; DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreDataSample.xcdatamodel; sourceTree = ""; }; DDA1C48528D77310009933EC /* MeshtasticDataModel v 12.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 12.xcdatamodel"; sourceTree = ""; }; + DDA1C48C28DB4839009933EC /* MeshtasticDataModel v 13.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 13.xcdatamodel"; sourceTree = ""; }; + DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelRoles.swift; sourceTree = ""; }; + DDA1C48F28DC3658009933EC /* MeshtasticDataModel v 14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 14.xcdatamodel"; sourceTree = ""; }; DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshPackets.swift; sourceTree = ""; }; DDA6B2EA28420A7B003E8C16 /* NodeAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeAnnotation.swift; sourceTree = ""; }; DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = ""; }; @@ -198,7 +202,7 @@ DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = ""; }; DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRoles.swift; sourceTree = ""; }; DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfigEnums.swift; sourceTree = ""; }; - DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenIntervals.swift; sourceTree = ""; }; + DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayEnums.swift; sourceTree = ""; }; DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = ""; }; DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiModes.swift; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -350,10 +354,11 @@ DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */, DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */, DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */, - DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */, + DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */, DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */, DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */, DD1925B828CDA93900720036 /* SerialConfigEnums.swift */, + DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */, ); path = Enums; sourceTree = ""; @@ -745,6 +750,7 @@ DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */, DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */, + DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */, DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, @@ -759,7 +765,7 @@ DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */, DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */, DD4033C228B286B70096A444 /* Onboarding.swift in Sources */, - DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */, + DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */, DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */, @@ -1156,6 +1162,8 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DDA1C48F28DC3658009933EC /* MeshtasticDataModel v 14.xcdatamodel */, + DDA1C48C28DB4839009933EC /* MeshtasticDataModel v 13.xcdatamodel */, DDA1C48528D77310009933EC /* MeshtasticDataModel v 12.xcdatamodel */, DD1925B528CD591B00720036 /* MeshtasticDataModel v 11.xcdatamodel */, DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */, @@ -1169,7 +1177,7 @@ DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DDA1C48528D77310009933EC /* MeshtasticDataModel v 12.xcdatamodel */; + currentVersion = DDA1C48F28DC3658009933EC /* MeshtasticDataModel v 14.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Enums/ChannelRoles.swift b/Meshtastic/Enums/ChannelRoles.swift new file mode 100644 index 00000000..ac5ad574 --- /dev/null +++ b/Meshtastic/Enums/ChannelRoles.swift @@ -0,0 +1,41 @@ +// +// ChannelRoles.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 9/21/22. +// + +// Default of 0 is Client +enum ChannelRoles: Int, CaseIterable, Identifiable { + + case disabled = 0 + case primary = 1 + case secondary = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + case .disabled: + return "Disabled" + case .primary: + return "Primary" + case .secondary: + return "Secondary" + } + } + } + func protoEnumValue() -> Channel.Role { + + switch self { + + case .disabled: + return Channel.Role.disabled + case .primary: + return Channel.Role.primary + case .secondary: + return Channel.Role.secondary + } + } +} diff --git a/Meshtastic/Enums/ScreenIntervals.swift b/Meshtastic/Enums/DisplayEnums.swift similarity index 72% rename from Meshtastic/Enums/ScreenIntervals.swift rename to Meshtastic/Enums/DisplayEnums.swift index ce14dc9b..c9c30bd0 100644 --- a/Meshtastic/Enums/ScreenIntervals.swift +++ b/Meshtastic/Enums/DisplayEnums.swift @@ -7,7 +7,34 @@ import Foundation -// Default of 0 is One Minute +enum ScreenUnits: Int, CaseIterable, Identifiable { + + case metric = 0 + case imperial = 1 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .metric: + return "Metric" + case .imperial: + return "Imperial" + } + } + } + func protoEnumValue() -> Config.DisplayConfig.DisplayUnits { + + switch self { + + case .metric: + return Config.DisplayConfig.DisplayUnits.metric + case .imperial: + return Config.DisplayConfig.DisplayUnits.imperial + } + } +} + enum ScreenOnIntervals: Int, CaseIterable, Identifiable { case oneMinute = 60 diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 109c3340..5ced286b 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -463,7 +463,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if errorCode == 15 { // CBATTErrorDomain Code=15 "Encryption is insufficient." // BLE Pin connection error - lastConnectionError = "🚫 BLE \(e.localizedDescription) This may be a Meshtastic Firmware bug affecting BLE 4.0 devices." + lastConnectionError = "🚫 BLE \(e.localizedDescription) Please try connecting again and check the PIN carefully." if meshLoggingEnabled { MeshLogger.log("🚫 BLE \(e.localizedDescription) Please try connecting again. You may need to forget the device under Settings > General > Bluetooth.") } self.centralManager?.cancelPeripheralConnection(peripheral) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index cf1fa2d8..9fc23784 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1039,8 +1039,16 @@ func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManage newTelemetries.append(telemetry) fetchedNode[0].telemetries? = NSOrderedSet(array: newTelemetries) } + if nodeInfoMessage.hasUser { + + fetchedNode[0].user!.userId = nodeInfoMessage.user.id + fetchedNode[0].user!.num = Int64(nodeInfoMessage.num) + fetchedNode[0].user!.longName = nodeInfoMessage.user.longName + fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName + fetchedNode[0].user!.macaddr = nodeInfoMessage.user.macaddr + fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased() + } } - } do { @@ -1066,6 +1074,72 @@ func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManage func adminAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) { if let channelMessage = try? Channel(serializedData: packet.decoded.payload) { + + let fetchedMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") + fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(packet.from)) + + do { + + let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as! [MyInfoEntity] + + if fetchedMyInfo.count == 1 { + + // Update + if fetchedMyInfo[0].channels?.count ?? 0 >= 1 { + + let newChannel = ChannelEntity(context: context) + newChannel.index = Int32(channelMessage.settings.channelNum) + newChannel.uplinkEnabled = channelMessage.settings.uplinkEnabled + newChannel.downlinkEnabled = channelMessage.settings.downlinkEnabled + newChannel.name = channelMessage.settings.name + newChannel.role = Int32(channelMessage.role.rawValue) + + let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet + + let currentChannel = fetchedMyInfo[0].channels!.first(where: { ($0 as! ChannelEntity).index == channelMessage.index }) + + if currentChannel != nil { + + //mutableChannels.remove(currentChannel!) + } + mutableChannels.add(newChannel) + fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet + + + } else { + + if channelMessage.index == 0 { + + let newChannel = ChannelEntity(context: context) + newChannel.index = channelMessage.index + newChannel.uplinkEnabled = channelMessage.settings.uplinkEnabled + newChannel.downlinkEnabled = channelMessage.settings.downlinkEnabled + newChannel.name = "Primary" + + var newChannels = [ChannelEntity]() + newChannels.append(newChannel) + fetchedMyInfo[0].channels! = NSOrderedSet(array: newChannels) + } + } + + } else { + print("💥 Trying to save a channel to a MyInfo that does not exist: \(packet.from)") + } + + try context.save() + + if meshLogging { + + MeshLogger.log("💾 Updated MyInfo channel \(channelMessage.index) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)") + } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Saving MyInfo Channel from ADMIN_APP \(nsError)") + } if meshLogging { MeshLogger.log("ℹ️ Channel Message received for Admin App \(try! channelMessage.jsonString())") } } diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 8c5ea2ac..79a36dbd 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 12.xcdatamodel + MeshtasticDataModel v 14.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 13.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 13.xcdatamodel/contents new file mode 100644 index 00000000..c58b6134 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 13.xcdatamodel/contents @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 14.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 14.xcdatamodel/contents new file mode 100644 index 00000000..a7c26afb --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 14.xcdatamodel/contents @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 0994a1a4..daa6aacb 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -95,10 +95,11 @@ struct BluetoothConfig: View { .padding() .confirmationDialog( - "Are you sure?", - isPresented: $isPresentingSaveConfirm + "Are you sure you want to save?", + isPresented: $isPresentingSaveConfirm, + titleVisibility: .visible ) { - Button("Save Bluetooth Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + Button("Save Config for \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")") { var bc = Config.BluetoothConfig() bc.enabled = enabled @@ -117,6 +118,10 @@ struct BluetoothConfig: View { } } + + } message: { + + Text("After bluetooth config saves the node will reboot.") } } .navigationTitle("Bluetooth (BLE) Config") diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index e03b0cdd..eca8960a 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -74,8 +74,9 @@ struct DeviceConfig: View { .padding() .confirmationDialog( - "Are you sure?", - isPresented: $isPresentingSaveConfirm + "Are you sure you want to save?", + isPresented: $isPresentingSaveConfirm, + titleVisibility: .visible ) { Button("Save Device Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { @@ -95,7 +96,11 @@ struct DeviceConfig: View { } else { } - } + } + } + message: { + + Text("After device config saves the node will reboot.") } Button("Factory Reset", role: .destructive) { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index d86e4846..1ddeed80 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -80,10 +80,11 @@ struct LoRaConfig: View { .padding() .confirmationDialog( - "Are you sure?", - isPresented: $isPresentingSaveConfirm + "Are you sure you want to save?", + isPresented: $isPresentingSaveConfirm, + titleVisibility: .visible ) { - Button("Save LoRa Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + Button("Save Config for \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")") { var lc = Config.LoRaConfig() lc.hopLimit = UInt32(hopLimit) @@ -104,8 +105,11 @@ struct LoRaConfig: View { } } + + } message: { + + Text("After LoRa config saves the node will reboot.") } - } .navigationTitle("LoRa Config") .navigationBarItems(trailing: diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index db25c040..a80e58e0 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -120,10 +120,11 @@ struct NetworkConfig: View { .padding() .confirmationDialog( - "Are you sure?", - isPresented: $isPresentingSaveConfirm + "Are you sure you want to save?", + isPresented: $isPresentingSaveConfirm, + titleVisibility: .visible ) { - Button("Save Network Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + Button("Save Config for \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")") { var network = Config.NetworkConfig() network.wifiEnabled = self.wifiEnabled @@ -143,6 +144,9 @@ struct NetworkConfig: View { } } + } message: { + + Text("After network config saves the node will reboot.") } } .navigationTitle("Network Config") diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 3bbdd6c8..d750603e 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -11,16 +11,16 @@ struct PositionFlags: OptionSet { let rawValue: Int - static let posAltitude = PositionFlags(rawValue: 1) - static let posAltMsl = PositionFlags(rawValue: 2) - static let posGeoSep = PositionFlags(rawValue: 4) - static let posDop = PositionFlags(rawValue: 8) - static let posHvdop = PositionFlags(rawValue: 16) - static let posSatsinview = PositionFlags(rawValue: 32) - static let posSeqNos = PositionFlags(rawValue: 64) - static let posTimestamp = PositionFlags(rawValue: 128) - static let posSpeed = PositionFlags(rawValue: 256) - static let posHeading = PositionFlags(rawValue: 512) + static let Altitude = PositionFlags(rawValue: 1) + static let AltitudeMsl = PositionFlags(rawValue: 2) + static let GeoSep = PositionFlags(rawValue: 4) + static let Dop = PositionFlags(rawValue: 8) + static let Hvdop = PositionFlags(rawValue: 16) + static let Satsinview = PositionFlags(rawValue: 32) + static let SeqNos = PositionFlags(rawValue: 64) + static let Timestamp = PositionFlags(rawValue: 128) + static let Speed = PositionFlags(rawValue: 256) + static let Heading = PositionFlags(rawValue: 512) } struct PositionConfig: View { @@ -44,29 +44,29 @@ struct PositionConfig: View { /// Position Flags /// Altitude value - 1 - @State var includePosAltitude = false + @State var includeAltitude = false /// Altitude value is MSL - 2 - @State var includePosAltMsl = false + @State var includeAltitudeMsl = false /// Include geoidal separation - 4 - @State var includePosGeoSep = false + @State var includeGeoSep = false /// Include the DOP value ; PDOP used by default, see below - 8 - @State var includePosDop = false + @State var includeDop = false /// If POS_DOP set, send separate HDOP / VDOP values instead of PDOP - 16 - @State var includePosHvdop = false + @State var includeHvdop = false /// Include number of "satellites in view" - 32 - @State var includePosSatsinview = false + @State var includeSatsinview = false /// Include a sequence number incremented per packet - 64 - @State var includePosSeqNos = false + @State var includeSeqNos = false /// Include positional timestamp (from GPS solution) - 128 - @State var includePosTimestamp = false + @State var includeTimestamp = false /// Include positional heading - 256 /// Intended for use with vehicle not walking speeds /// walking speeds are likely to be error prone like the compass - @State var includePosSpeed = false + @State var includeSpeed = false /// Include positional speed - 512 /// Intended for use with vehicle not walking speeds /// walking speeds are likely to be error prone like the compass - @State var includePosHeading = false + @State var includeHeading = false var body: some View { @@ -144,43 +144,43 @@ struct PositionConfig: View { .font(.caption) .listRowSeparator(.visible) - Toggle(isOn: $includePosAltitude) { + Toggle(isOn: $includeAltitude) { Label("Altitude", systemImage: "arrow.up") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosAltMsl) { + Toggle(isOn: $includeAltitudeMsl) { Label("Altitude is Mean Sea Level", systemImage: "arrow.up.to.line.compact") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosSatsinview) { + Toggle(isOn: $includeSatsinview) { Label("Number of satellites", systemImage: "skew") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosSeqNos) { //64 + Toggle(isOn: $includeSeqNos) { //64 Label("Sequence number", systemImage: "number") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosTimestamp) { //128 + Toggle(isOn: $includeTimestamp) { //128 Label("Timestamp", systemImage: "clock") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosHeading) { //128 + Toggle(isOn: $includeHeading) { //128 Label("Vehicle heading", systemImage: "location.circle") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosSpeed) { //128 + Toggle(isOn: $includeSpeed) { //128 Label("Vehicle speed", systemImage: "speedometer") } @@ -188,19 +188,19 @@ struct PositionConfig: View { } Section(header: Text("Advanced Position Flags")) { - Toggle(isOn: $includePosGeoSep) { + Toggle(isOn: $includeGeoSep) { Text("Geoidal Seperation") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosDop) { + Toggle(isOn: $includeDop) { Text("Dilution of precision (DOP) PDOP used by default") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle(isOn: $includePosHvdop) { + Toggle(isOn: $includeHvdop) { Text("If DOP is set use, HDOP / VDOP values instead of PDOP") } @@ -240,16 +240,16 @@ struct PositionConfig: View { var pf : PositionFlags = [] - if includePosAltitude { pf.insert(.posAltitude) } - if includePosAltMsl { pf.insert(.posAltMsl) } - if includePosGeoSep { pf.insert(.posGeoSep) } - if includePosDop { pf.insert(.posDop) } - if includePosHvdop { pf.insert(.posHvdop) } - if includePosSatsinview { pf.insert(.posSatsinview) } - if includePosSeqNos { pf.insert(.posSeqNos) } - if includePosTimestamp { pf.insert(.posTimestamp) } - if includePosSpeed { pf.insert(.posSpeed) } - if includePosHeading { pf.insert(.posHeading) } + if includeAltitude { pf.insert(.Altitude) } + if includeAltitudeMsl { pf.insert(.AltitudeMsl) } + if includeGeoSep { pf.insert(.GeoSep) } + if includeDop { pf.insert(.Dop) } + if includeHvdop { pf.insert(.Hvdop) } + if includeSatsinview { pf.insert(.Satsinview) } + if includeSeqNos { pf.insert(.SeqNos) } + if includeTimestamp { pf.insert(.Timestamp) } + if includeSpeed { pf.insert(.Speed) } + if includeHeading { pf.insert(.Heading) } pc.positionFlags = UInt32(pf.rawValue) @@ -287,60 +287,73 @@ struct PositionConfig: View { self.smartPositionEnabled = node!.positionConfig?.smartPositionEnabled ?? true self.deviceGpsEnabled = node!.positionConfig?.deviceGpsEnabled ?? true self.fixedPosition = node!.positionConfig?.fixedPosition ?? false - self.gpsUpdateInterval = Int(node!.positionConfig?.gpsUpdateInterval ?? 0) - self.gpsAttemptTime = Int(node!.positionConfig?.gpsAttemptTime ?? 0) - self.positionBroadcastSeconds = Int(node!.positionConfig?.positionBroadcastSeconds ?? 0) + self.gpsUpdateInterval = Int(node!.positionConfig?.gpsUpdateInterval ?? 30) + self.gpsAttemptTime = Int(node!.positionConfig?.gpsAttemptTime ?? 30) + self.positionBroadcastSeconds = Int(node!.positionConfig?.positionBroadcastSeconds ?? 900) self.positionFlags = Int(node!.positionConfig?.positionFlags ?? 3) let pf = PositionFlags(rawValue: self.positionFlags) - if pf.contains(.posAltitude) { self.includePosAltitude = true } else { self.includePosAltitude = false } - if pf.contains(.posAltMsl) { self.includePosAltMsl = true } else { self.includePosAltMsl = false } - if pf.contains(.posGeoSep) { self.includePosGeoSep = true } else { self.includePosGeoSep = false } - if pf.contains(.posDop) { self.includePosDop = true } else { self.includePosDop = false } - if pf.contains(.posHvdop) { self.includePosHvdop = true } else { self.includePosHvdop = false } - if pf.contains(.posSatsinview) { self.includePosSatsinview = true } else { self.includePosSatsinview = false } - if pf.contains(.posSeqNos) { self.includePosSeqNos = true } else { self.includePosSeqNos = false } - if pf.contains(.posTimestamp) { self.includePosTimestamp = true } else { self.includePosTimestamp = false } - if pf.contains(.posSpeed) { self.includePosSpeed = true } else { self.includePosSpeed = false } - if pf.contains(.posHeading) { self.includePosHeading = true } else { self.includePosHeading = false } + if pf.contains(.Altitude) { self.includeAltitude = true } else { self.includeAltitude = false } + if pf.contains(.AltitudeMsl) { self.includeAltitudeMsl = true } else { self.includeAltitudeMsl = false } + if pf.contains(.GeoSep) { self.includeGeoSep = true } else { self.includeGeoSep = false } + if pf.contains(.Dop) { self.includeDop = true } else { self.includeDop = false } + if pf.contains(.Hvdop) { self.includeHvdop = true } else { self.includeHvdop = false } + if pf.contains(.Satsinview) { self.includeSatsinview = true } else { self.includeSatsinview = false } + if pf.contains(.SeqNos) { self.includeSeqNos = true } else { self.includeSeqNos = false } + if pf.contains(.Timestamp) { self.includeTimestamp = true } else { self.includeTimestamp = false } + if pf.contains(.Speed) { self.includeSpeed = true } else { self.includeSpeed = false } + if pf.contains(.Heading) { self.includeHeading = true } else { self.includeHeading = false } self.hasChanges = false self.initialLoad = false } } - .onChange(of: smartPositionEnabled) { newSmartPosition in - - if node != nil && node!.positionConfig != nil { - - if newSmartPosition != node!.positionConfig!.smartPositionEnabled { hasChanges = true } - } - } - .onChange(of: positionBroadcastSeconds) { newPositionBroadcastSeconds in - - if node != nil && node!.positionConfig != nil { - - if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true } - } - } .onChange(of: deviceGpsEnabled) { newDeviceGps in - + if node != nil && node!.positionConfig != nil { - + if newDeviceGps != node!.positionConfig!.deviceGpsEnabled { hasChanges = true } } } - .onChange(of: fixedPosition) { newFixed in - + .onChange(of: gpsAttemptTime) { newGpsAttemptTime in + if node != nil && node!.positionConfig != nil { - + + if newGpsAttemptTime != node!.positionConfig!.gpsAttemptTime { hasChanges = true } + } + } + .onChange(of: gpsUpdateInterval) { newGpsUpdateInterval in + + if node != nil && node!.positionConfig != nil { + + if newGpsUpdateInterval != node!.positionConfig!.gpsUpdateInterval { hasChanges = true } + } + } + .onChange(of: smartPositionEnabled) { newSmartPositionEnabled in + + if node != nil && node!.positionConfig != nil { + + if newSmartPositionEnabled != node!.positionConfig!.smartPositionEnabled { hasChanges = true } + } + } + .onChange(of: fixedPosition) { newFixed in + + if node != nil && node!.positionConfig != nil { + if newFixed != node!.positionConfig!.fixedPosition { hasChanges = true } } } - .onChange(of: includePosAltitude || includePosAltMsl || includePosGeoSep || includePosDop || includePosHvdop || includePosSatsinview || includePosSeqNos || includePosTimestamp || includePosSpeed || includePosHeading) { newFlags in - - hasChanges = true + .onChange(of: positionBroadcastSeconds) { newPositionBroadcastSeconds in + + if node != nil && node!.positionConfig != nil { + + if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true } + } + } + .onChange(of: includeAltitude || includeAltitudeMsl || includeGeoSep || includeDop || includeHvdop || includeSatsinview || includeSeqNos || includeTimestamp || includeSpeed || includeHeading) { newFlags in + // hasChanges = true } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/ShareChannel.swift b/Meshtastic/Views/Settings/ShareChannel.swift index 0badf12e..9ad31411 100644 --- a/Meshtastic/Views/Settings/ShareChannel.swift +++ b/Meshtastic/Views/Settings/ShareChannel.swift @@ -69,7 +69,7 @@ struct ShareChannel: View { alignment: .center ) - if node!.loRaConfig != nil { + if node != nil && node!.loRaConfig != nil { HStack { @@ -77,9 +77,18 @@ struct ShareChannel: View { Text("Modem Preset \(preset!.description)").font(.title3) } } - HStack { + VStack { Text("Number of Channels: \(node!.myInfo!.maxChannels)").font(.title2) + + ForEach(node!.myInfo!.channels?.array.sorted(by: { ($0 as! ChannelEntity).index < ($1 as! ChannelEntity).index }) as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in + + VStack { + + + Text("Channel: \(channel.index) Name: \(channel.name ?? "")") + } + } } } .frame(width: bounds.size.width, height: bounds.size.height) @@ -96,15 +105,7 @@ struct ShareChannel: View { .onAppear { self.bleManager.context = context - let i: UInt32 = 1; -// while i < 9 { // this should actually loop over MyNodeInfo.maxChannels to get all channels - print("requesting channel",i) - let resp = self.bleManager.getChannel(channelIndex: i, wantResponse: true) - print("resp from getChannel", resp) -// i+=1; -// } } - } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index 3747c71c..ec563943 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -104,8 +104,9 @@ struct UserConfig: View { .padding() .confirmationDialog( - "Are you sure?", - isPresented: $isPresentingSaveConfirm + "Are you sure you want to save?", + isPresented: $isPresentingSaveConfirm, + titleVisibility: .visible ) { Button("Save User Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { @@ -120,7 +121,11 @@ struct UserConfig: View { hasChanges = false } } + } message: { + + Text("After user config saves the node will reboot.") } + } Spacer() }