From b93b0c7d9138b5c0d66c51ed03147755b5195437 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Sep 2022 21:45:03 -0700 Subject: [PATCH 1/4] Add MQTT config clean up node details clean up core data model update protobufs --- Meshtastic.xcodeproj/project.pbxproj | 16 +- Meshtastic/Helpers/BLEManager.swift | 29 +++ Meshtastic/Helpers/MeshPackets.swift | 8 - .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 233 ++++++++++++++++++ Meshtastic/Protobufs/config.pb.swift | 4 +- Meshtastic/Protobufs/module_config.pb.swift | 10 + Meshtastic/Views/Bluetooth/Connect.swift | 2 +- Meshtastic/Views/Messages/Contacts.swift | 5 + Meshtastic/Views/Nodes/NodeDetail.swift | 23 +- .../Config/Module/CannedMessagesConfig.swift | 41 ++- .../Settings/Config/Module/MQTTConfig.swift | 233 ++++++++++++++++++ .../Views/Settings/Config/WiFiConfig.swift | 16 +- Meshtastic/Views/Settings/Settings.swift | 11 + Meshtastic/Views/Settings/ShareChannel.swift | 22 +- 15 files changed, 601 insertions(+), 54 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 10.xcdatamodel/contents create mode 100644 Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 7f433614..1955a4fa 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; }; DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */; }; DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; }; + DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2160AE28C5552500C17253 /* MQTTConfig.swift */; }; DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553562855B02500E55709 /* LoRaConfig.swift */; }; DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; }; @@ -121,6 +122,8 @@ DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = ""; }; DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = storeforward.pb.swift; sourceTree = ""; }; DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; + DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 10.xcdatamodel"; sourceTree = ""; }; + DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = ""; }; DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; DD2553562855B02500E55709 /* LoRaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaConfig.swift; sourceTree = ""; }; DD2553582855B52700E55709 /* PositionConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfig.swift; sourceTree = ""; }; @@ -260,6 +263,13 @@ path = Custom; sourceTree = ""; }; + DD2160AC28C5019400C17253 /* Messages */ = { + isa = PBXGroup; + children = ( + ); + path = Messages; + sourceTree = ""; + }; DD47E3CA26F0E50300029299 /* Nodes */ = { isa = PBXGroup; children = ( @@ -318,6 +328,7 @@ DD41582928585C32009B0E59 /* RangeTestConfig.swift */, DD6193782863875F00E59241 /* SerialConfig.swift */, DD415827285859C4009B0E59 /* TelemetryConfig.swift */, + DD2160AE28C5552500C17253 /* MQTTConfig.swift */, ); path = Module; sourceTree = ""; @@ -488,6 +499,7 @@ DDC2E18D26CE25CB0042C5E4 /* Helpers */ = { isa = PBXGroup; children = ( + DD2160AC28C5019400C17253 /* Messages */, DD47E3D526F17ED900029299 /* CircleText.swift */, DD47E3D826F3093800029299 /* MessageBubble.swift */, DD90860B26F684AF00DC5189 /* BatteryIcon.swift */, @@ -705,6 +717,7 @@ DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */, DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */, DD35018B2852FC79000FC853 /* UserSettings.swift in Sources */, + DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */, DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */, DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */, DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */, @@ -1141,6 +1154,7 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */, DD5929A528C0F292003DB21D /* MeshtasticDataModel v 9.xcdatamodel */, DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */, DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */, @@ -1151,7 +1165,7 @@ DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DD5929A528C0F292003DB21D /* MeshtasticDataModel v 9.xcdatamodel */; + currentVersion = DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index b04f5cb6..b736f0be 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1357,6 +1357,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return 0 } + public func saveMQTTConfig(config: ModuleConfig.MQTTConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { + + var adminPacket = AdminMessage() + adminPacket.setModuleConfig.mqtt = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 50ece7f8..905e6a35 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -497,7 +497,6 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont newWiFiConfig.password = config.wifi.psk newWiFiConfig.mode = Int32(config.wifi.mode.rawValue) } - newWiFiConfig.num = fetchedNode[0].num fetchedNode[0].wiFiConfig = newWiFiConfig } else { @@ -596,8 +595,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newCannedMessageConfig.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) newCannedMessageConfig.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) } - - newCannedMessageConfig.num = nodeNum fetchedNode[0].cannedMessageConfig = newCannedMessageConfig } else { @@ -628,7 +625,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) } - fetchedNode[0].cannedMessageConfig?.num = nodeNum } do { @@ -703,8 +699,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newExternalNotificationConfig.output = Int32(config.externalNotification.output) newExternalNotificationConfig.outputMilliseconds = Int32(config.externalNotification.outputMs) } - - newExternalNotificationConfig.num = nodeNum fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig } else { @@ -727,7 +721,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj fetchedNode[0].externalNotificationConfig?.output = Int32(config.externalNotification.output) fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.externalNotification.outputMs) } - fetchedNode[0].externalNotificationConfig?.num = nodeNum } do { @@ -789,7 +782,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newRangeTestConfig.enabled = config.rangeTest.enabled newRangeTestConfig.save = config.rangeTest.save } - newRangeTestConfig.num = nodeNum fetchedNode[0].rangeTestConfig = newRangeTestConfig } else { diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 59abb4ee..8b0ce3b2 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 9.xcdatamodel + MeshtasticDataModel v 10.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 10.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 10.xcdatamodel/contents new file mode 100644 index 00000000..fa053770 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 10.xcdatamodel/contents @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index 8ee51d30..a0d820a0 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -732,8 +732,8 @@ struct Config { var region: Config.LoRaConfig.RegionCode = .unset /// - /// Overrides HOPS_RELIABLE and sets the maximum number of hops. This can't be greater than 7. - /// 0 for default of 3 + /// Maximum number of hops. This can't be greater than 7. + /// Default of 3 var hopLimit: UInt32 = 0 /// diff --git a/Meshtastic/Protobufs/module_config.pb.swift b/Meshtastic/Protobufs/module_config.pb.swift index 0d6337a8..44f61043 100644 --- a/Meshtastic/Protobufs/module_config.pb.swift +++ b/Meshtastic/Protobufs/module_config.pb.swift @@ -205,6 +205,10 @@ struct ModuleConfig { /// Decrypted packets may be useful for external systems that want to consume meshtastic packets var encryptionEnabled: Bool = false + /// + /// Whether to send / consume json packets on MQTT + var jsonEnabled: Bool = false + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -850,6 +854,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message 3: .same(proto: "username"), 4: .same(proto: "password"), 5: .standard(proto: "encryption_enabled"), + 6: .standard(proto: "json_enabled"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -863,6 +868,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message case 3: try { try decoder.decodeSingularStringField(value: &self.username) }() case 4: try { try decoder.decodeSingularStringField(value: &self.password) }() case 5: try { try decoder.decodeSingularBoolField(value: &self.encryptionEnabled) }() + case 6: try { try decoder.decodeSingularBoolField(value: &self.jsonEnabled) }() default: break } } @@ -884,6 +890,9 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message if self.encryptionEnabled != false { try visitor.visitSingularBoolField(value: self.encryptionEnabled, fieldNumber: 5) } + if self.jsonEnabled != false { + try visitor.visitSingularBoolField(value: self.jsonEnabled, fieldNumber: 6) + } try unknownFields.traverse(visitor: &visitor) } @@ -893,6 +902,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message if lhs.username != rhs.username {return false} if lhs.password != rhs.password {return false} if lhs.encryptionEnabled != rhs.encryptionEnabled {return false} + if lhs.jsonEnabled != rhs.jsonEnabled {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 21a46f62..21f96bcf 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -20,7 +20,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.39" + @State var minimumVersion = "1.3.40" @State var invalidVersion = false var body: some View { diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift index c27485a3..76160113 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -23,6 +23,11 @@ struct Contacts: View { var body: some View { NavigationView { + + // Display Contact for Primary Channel + // Display Contacts for DM's on the Primary Channel + // Display Contacts for the rest of the non admin channels + List(users) { (user: UserEntity) in diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 23d53eca..80b9ccbb 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -64,17 +64,21 @@ struct NodeDetail: View { ) } .ignoresSafeArea(.all, edges: [.leading, .trailing]) - .frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.75) + .frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.6) } } Text("Sats: \(mostRecent.satsInView)").offset( y:-40) } else { - Image(node.user?.hwModel ?? "UNSET") - .resizable() - .aspectRatio(contentMode: .fit) - .cornerRadius(10) - .frame(width: bounds.size.width, height: bounds.size.height / 2) + HStack { + Image(node.user?.hwModel ?? "UNSET") + .resizable() + .aspectRatio(contentMode: .fit) + .cornerRadius(10) + .frame(width: bounds.size.width, height: bounds.size.height / 2.3) + .padding([.top], 40) + } + .offset( y:-40) } ScrollView { @@ -168,6 +172,7 @@ struct NodeDetail: View { Image(hwModelString) .resizable() + .aspectRatio(contentMode: .fit) .frame(width: 90, height: 90) .cornerRadius(5) @@ -429,16 +434,16 @@ struct NodeDetail: View { } } .listStyle(GroupedListStyle()) - .frame(minHeight:170) + .frame(minHeight: 170) .padding(0) } - .offset( y: (node.myInfo!.hasGps ? 0 : -40)) + .offset( y: (node.myInfo?.hasGps ?? false ? 0 : -40)) } .edgesIgnoringSafeArea([.leading, .trailing]) } } .navigationTitle((node.user != nil) ? String(node.user!.longName ?? "Unknown") : "Unknown") - .navigationBarTitleDisplayMode(.automatic) + .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: ZStack { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index eba68209..2f7e8908 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -128,7 +128,7 @@ struct CannedMessagesConfig: View { /// Generate input event on Press of this kind. @State var inputbrokerEventPress = 0 - @State var messagesPart1 = "" + @State var messages = "" var body: some View { @@ -160,10 +160,34 @@ struct CannedMessagesConfig: View { .padding(.bottom, 10) } - Section(header: Text("Messages")) { - - TextEditor(text: $messagesPart1) + + HStack { + Label("Messages", systemImage: "message.fill") + TextField("Messages seperate with |", text: $messages) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: messages, perform: { value in + + let totalBytes = messages.utf8.count + + // Only mess with the value if it is too big + if totalBytes > 198 { + + let firstNBytes = Data(messages.utf8.prefix(198)) + + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + + // Set the shortName back to the last place where it was the right size + messages = maxBytesString + } + } + hasMessagesChanges = true + }) + .foregroundColor(.gray) } + .keyboardType(.default) + Section(header: Text("Control Type")) { @@ -331,7 +355,7 @@ struct CannedMessagesConfig: View { if hasMessagesChanges { - let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messagesPart1, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messages, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -465,13 +489,6 @@ struct CannedMessagesConfig: View { if newKeyPress != node!.cannedMessageConfig!.inputbrokerEventPress { hasChanges = true } } } - .onChange(of: messagesPart1) { newMessagesChanges in - - if node != nil && node!.cannedMessageConfig != nil { - - hasMessagesChanges = true - } - } .navigationViewStyle(StackNavigationViewStyle()) } } diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift new file mode 100644 index 00000000..b21624a7 --- /dev/null +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -0,0 +1,233 @@ +// +// MQTT.swift +// Meshtastic +// +// Copyright (c) Garth Vander Houwen 9/4/22. +// +import SwiftUI + +struct MQTTConfig: 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: Bool = false + + @State var enabled = false + @State var address = "" + @State var username = "" + @State var password = "" + @State var encryptionEnabled = false + @State var jsonEnabled = false + + var body: some View { + + VStack { + + Form { + + Text("WiFi must also be enabled for MQTT to work. You can set uplink and downlink for each channel.") + .font(.title3) + + Section(header: Text("Options")) { + + Toggle(isOn: $enabled) { + + Label("Enabled", systemImage: "dot.radiowaves.right") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $encryptionEnabled) { + + Label("Encryption Enabled", systemImage: "lock.icloud") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $jsonEnabled) { + + Label("JSON Enabled", systemImage: "ellipsis.curlybraces") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + } + Section(header: Text("Custom Server")) { + + HStack { + Label("Address", systemImage: "server.rack") + TextField("Server Address", text: $username) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: address, perform: { value in + + let totalBytes = address.utf8.count + + // Only mess with the value if it is too big + if totalBytes > 30 { + + let firstNBytes = Data(username.utf8.prefix(30)) + + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + + // Set the shortName back to the last place where it was the right size + address = maxBytesString + } + } + hasChanges = true + }) + .foregroundColor(.gray) + } + .keyboardType(.default) + + HStack { + Label("Username", systemImage: "person.text.rectangle") + TextField("Server Username", text: $username) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: username, perform: { value in + + let totalBytes = username.utf8.count + + // Only mess with the value if it is too big + if totalBytes > 30 { + + let firstNBytes = Data(username.utf8.prefix(30)) + + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + + // Set the shortName back to the last place where it was the right size + username = maxBytesString + } + } + hasChanges = true + }) + .foregroundColor(.gray) + } + .keyboardType(.default) + + + HStack { + Label("Password", systemImage: "wallet.pass") + TextField("Server Password", text: $password) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: password, perform: { value in + + let totalBytes = password.utf8.count + + // Only mess with the value if it is too big + if totalBytes > 30 { + + let firstNBytes = Data(password.utf8.prefix(30)) + + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + + // Set the shortName back to the last place where it was the right size + password = maxBytesString + } + } + hasChanges = true + }) + .foregroundColor(.gray) + } + .keyboardType(.default) + } + } + .disabled(!(node != nil && node!.myInfo?.hasWifi ?? false)) + + 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 WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var mqtt = ModuleConfig.MQTTConfig() + mqtt.enabled = self.enabled + mqtt.address = self.address + mqtt.username = self.username + mqtt.password = self.password + mqtt.encryptionEnabled = self.encryptionEnabled + mqtt.jsonEnabled = self.jsonEnabled + + let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: node!.user!, toUser: node!.user!) + + if adminMessageId > 0 { + + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + self.hasChanges = false + + } else { + + } + } + } + } + .navigationTitle("MQTT Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + }) + .onAppear { + + if self.initialLoad{ + + self.bleManager.context = context + + self.enabled = (node!.mqttConfig?.enabled ?? false) + self.address = node!.mqttConfig?.address ?? "" + self.username = node!.mqttConfig?.username ?? "" + self.password = node!.mqttConfig?.password ?? "" + self.encryptionEnabled = (node!.mqttConfig?.encryptionEnabled ?? false) + self.jsonEnabled = (node!.mqttConfig?.jsonEnabled ?? false) + + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: enabled) { newEnabled in + + if node != nil && node!.wiFiConfig != nil { + + if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true } + } + } + .onChange(of: encryptionEnabled) { newEncryptionEnabled in + + if node != nil && node!.wiFiConfig != nil { + + if newEncryptionEnabled != node!.mqttConfig!.encryptionEnabled { hasChanges = true } + } + } + .onChange(of: jsonEnabled) { newJsonEnabled in + + if node != nil && node!.wiFiConfig != nil { + + if newJsonEnabled != node!.mqttConfig!.jsonEnabled { hasChanges = true } + } + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index 1378be55..b3b48db7 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -72,6 +72,7 @@ struct WiFiConfig: View { ssid = maxBytesString } } + hasChanges = true }) .foregroundColor(.gray) } @@ -99,6 +100,7 @@ struct WiFiConfig: View { password = maxBytesString } } + hasChanges = true }) .foregroundColor(.gray) } @@ -176,20 +178,6 @@ struct WiFiConfig: View { if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true } } } - .onChange(of: ssid) { newSsid in - - if node != nil && node!.wiFiConfig != nil { - - if newSsid != node!.wiFiConfig!.ssid { hasChanges = true } - } - } - .onChange(of: password) { newPassword in - - if node != nil && node!.wiFiConfig != nil { - - if newPassword != node!.wiFiConfig!.password { hasChanges = true } - } - } .onChange(of: mode) { newMode in if node != nil && node!.wiFiConfig != nil { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index db8d7702..2aa04051 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -151,6 +151,17 @@ struct Settings: View { } .disabled(bleManager.connectedPeripheral == nil) + NavigationLink { + MQTTConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + } label: { + + Image(systemName: "dot.radiowaves.right") + .symbolRenderingMode(.hierarchical) + + Text("MQTT (ESP32 Only)") + } + .disabled(bleManager.connectedPeripheral == nil) + NavigationLink { RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) } label: { diff --git a/Meshtastic/Views/Settings/ShareChannel.swift b/Meshtastic/Views/Settings/ShareChannel.swift index d2eb08e0..a1d15e26 100644 --- a/Meshtastic/Views/Settings/ShareChannel.swift +++ b/Meshtastic/Views/Settings/ShareChannel.swift @@ -62,15 +62,25 @@ struct ShareChannel: View { .resizable() .scaledToFit() .frame( - minWidth: smallest * 0.9, - maxWidth: smallest * 0.9, - minHeight: smallest * 0.9, - maxHeight: smallest * 0.9, + minWidth: smallest * 0.8, + maxWidth: smallest * 0.8, + minHeight: smallest * 0.8, + maxHeight: smallest * 0.8, alignment: .center ) Spacer() - Text("Channel Name (Long/Slow)").font(.title) - Text(String(node!.myInfo!.maxChannels)) + HStack { + + let preset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset)) + + Text("Modem Preset \(preset!.description)").font(.title) + } + Spacer() + HStack { + + Text("Number of Channels").font(.title) + Text(String(node!.myInfo!.maxChannels)).font(.title) + } Spacer() } .frame(width: bounds.size.width, height: bounds.size.height) From c554e4c8c5aeb2c7792d65e77b92a473a63e6341 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Sep 2022 22:24:37 -0700 Subject: [PATCH 2/4] Add details link bool to node list to stop the user being taken back to node details when data comes in, should be able to remove when the iOS 16 upgrade happens --- Meshtastic/Views/Nodes/NodeList.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 62b2657c..b6e646d4 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -120,6 +120,7 @@ struct NodeList: View { } .padding([.leading, .top, .bottom]) } + .isDetailLink(false) } } } From 8ed501d379881ed969da7839b68be361b1b6bc74 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Sep 2022 22:35:16 -0700 Subject: [PATCH 3/4] Clean up share channel view a bit --- Meshtastic/Views/Settings/ShareChannel.swift | 21 ++++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Meshtastic/Views/Settings/ShareChannel.swift b/Meshtastic/Views/Settings/ShareChannel.swift index a1d15e26..b9e0ae82 100644 --- a/Meshtastic/Views/Settings/ShareChannel.swift +++ b/Meshtastic/Views/Settings/ShareChannel.swift @@ -68,20 +68,19 @@ struct ShareChannel: View { maxHeight: smallest * 0.8, alignment: .center ) - Spacer() + + if node?.loRaConfig != nil { + + HStack { + + let preset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset)) + Text("Modem Preset \(preset!.description)").font(.title3) + } + } HStack { - let preset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset)) - - Text("Modem Preset \(preset!.description)").font(.title) + Text("Number of Channels: \(node!.myInfo!.maxChannels)").font(.title2) } - Spacer() - HStack { - - Text("Number of Channels").font(.title) - Text(String(node!.myInfo!.maxChannels)).font(.title) - } - Spacer() } .frame(width: bounds.size.width, height: bounds.size.height) } From 8e06a917b38205a6ea260578bd7584be1369aab9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 4 Sep 2022 22:39:06 -0700 Subject: [PATCH 4/4] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 1955a4fa..82e9e383 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -949,7 +949,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.39; + MARKETING_VERSION = 1.3.40; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -981,7 +981,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.39; + MARKETING_VERSION = 1.3.40; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES;