From 4318794c084be229a07a40e6520375b533166385 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 9 Oct 2022 18:32:21 -0700 Subject: [PATCH 01/16] Get share channels loading from real channel data --- Meshtastic/Helpers/BLEManager.swift | 81 +++++------ Meshtastic/Helpers/MeshPackets.swift | 137 ++++++++++-------- Meshtastic/MeshtasticApp.swift | 32 ++-- Meshtastic/Protobufs/admin.pb.swift | 74 +++++----- Meshtastic/Protobufs/config.pb.swift | 2 +- Meshtastic/Protobufs/mesh.pb.swift | 35 +++++ Meshtastic/Protobufs/telemetry.pb.swift | 16 ++ Meshtastic/Views/Settings/ShareChannels.swift | 45 +++--- 8 files changed, 239 insertions(+), 183 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index ae52b23d..bdc28320 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -459,7 +459,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph MeshLogger.log("ℹ️ Requesting Device Metadata for \(connectedPeripheral!.peripheral.name ?? "Unknown")") var adminPacket = AdminMessage() - adminPacket.getDeviceMetadataRequest = 0 + adminPacket.getDeviceMetadataRequest = true var meshPacket: MeshPacket = MeshPacket() meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + public func getChannel(channelIndex: UInt32, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Bool { var adminPacket = AdminMessage() adminPacket.getChannelRequest = channelIndex var meshPacket: MeshPacket = MeshPacket() - meshPacket.to = UInt32(nodeNum) + meshPacket.to = UInt32(toUser.num) meshPacket.from = 0 //UInt32(cnodeNum) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. NodeInfoEntity? { + + print(try! channel.jsonString()) + + if channel.isInitialized { + + let fetchedMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") + fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum) + + 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(channel.index) + newChannel.uplinkEnabled = channel.settings.uplinkEnabled + newChannel.downlinkEnabled = channel.settings.downlinkEnabled + newChannel.name = channel.settings.name + newChannel.role = Int32(channel.role.rawValue) + + let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet + + mutableChannels.add(newChannel) + fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet + + } else { + + let newChannel = ChannelEntity(context: context) + newChannel.index = Int32(channel.index) + newChannel.uplinkEnabled = channel.settings.uplinkEnabled + newChannel.downlinkEnabled = channel.settings.downlinkEnabled + newChannel.name = channel.settings.name + newChannel.role = Int32(channel.role.rawValue) + + 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: \(fromNum)") + } + + try context.save() + + if meshLogging { + + MeshLogger.log("💾 Updated MyInfo channel \(channel.settings.channelNum + 1) 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)") + } + } + //} + + + + + return nil + +} + func nodeInfoPacket (nodeInfo: NodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> NodeInfoEntity? { let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -1012,6 +1084,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, meshLogging: Bool, context: NSManagedOb return nil } + func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) { let fetchNodeInfoAppRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -1076,71 +1149,7 @@ 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) { - - if channelMessage.hasSettings { - - 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 - - mutableChannels.add(newChannel) - fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet - - } else { - - 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) - - 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.settings.channelNum + 1) 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)") - } - } - } else { - print(try! packet.decoded.jsonString()) - } } diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 6dce5257..539b11f1 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -11,8 +11,8 @@ struct MeshtasticAppleApp: App { @ObservedObject private var bleManager: BLEManager = BLEManager.shared @ObservedObject private var userSettings: UserSettings = UserSettings() - @State var saveQR = false - @State var channelUrl: URL? + @State var saveChannels = false + @State var incomingUrl: URL? @Environment(\.scenePhase) var scenePhase @@ -25,32 +25,30 @@ struct MeshtasticAppleApp: App { .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in - print("QR Code URL received from the Camera \(userActivity)") - channelUrl = userActivity.webpageURL - if channelUrl!.absoluteString.lowercased().contains("https://meshtastic.org/e/#") { - saveQR = true + print("URL received \(userActivity)") + incomingUrl = userActivity.webpageURL + if incomingUrl!.absoluteString.lowercased().contains("https://meshtastic.org/e/#") { + saveChannels = true + } + if saveChannels { + print("User wants to open Channel Settings URL: \(String(describing: incomingUrl!.relativeString))") } - - print("User wants to open URL: \(String(describing: channelUrl?.relativeString))") - } - .sheet(isPresented: $saveQR) { - - SaveChannelQRCode(channelHash: channelUrl?.absoluteString ?? "Empty Channel URL") + .sheet(isPresented: $saveChannels) { + SaveChannelQRCode(channelHash: incomingUrl?.absoluteString ?? "Empty Channel URL") .presentationDetents([.medium, .large]) .presentationDragIndicator(.visible) } .onOpenURL(perform: { (url) in print("Some sort of URL was received \(url)") - channelUrl = url - + incomingUrl = url if url.absoluteString.lowercased().contains("https://meshtastic.org/e/#") { - saveQR = true - print("User wants to open a Channel Settings URL: \(channelUrl?.absoluteString ?? "No QR Code Link")") + saveChannels = true + print("User wants to open a Channel Settings URL: \(incomingUrl?.absoluteString ?? "No QR Code Link")") } else { - print("User wants to import a MBTILES offline map file: \(channelUrl?.absoluteString ?? "No Tiles link")") + print("User wants to import a MBTILES offline map file: \(incomingUrl?.absoluteString ?? "No Tiles link")") } //we are expecting a .mbtiles map file that contains raster data diff --git a/Meshtastic/Protobufs/admin.pb.swift b/Meshtastic/Protobufs/admin.pb.swift index e43a6564..83169e01 100644 --- a/Meshtastic/Protobufs/admin.pb.swift +++ b/Meshtastic/Protobufs/admin.pb.swift @@ -114,16 +114,6 @@ struct AdminMessage { set {payloadVariant = .getModuleConfigResponse(newValue)} } - /// - /// Send all channels in the response to this message - var getAllChannelRequest: Bool { - get { - if case .getAllChannelRequest(let v)? = payloadVariant {return v} - return false - } - set {payloadVariant = .getAllChannelRequest(newValue)} - } - /// /// Get the Canned Message Module messages in the response to this message. var getCannedMessageModuleMessagesRequest: Bool { @@ -146,10 +136,10 @@ struct AdminMessage { /// /// Request the node to send device metadata (firmware, protobuf version, etc) - var getDeviceMetadataRequest: UInt32 { + var getDeviceMetadataRequest: Bool { get { if case .getDeviceMetadataRequest(let v)? = payloadVariant {return v} - return 0 + return false } set {payloadVariant = .getDeviceMetadataRequest(newValue)} } @@ -261,6 +251,17 @@ struct AdminMessage { set {payloadVariant = .confirmSetRadio(newValue)} } + /// + /// Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) + /// Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. + var rebootOtaSeconds: Int32 { + get { + if case .rebootOtaSeconds(let v)? = payloadVariant {return v} + return 0 + } + set {payloadVariant = .rebootOtaSeconds(newValue)} + } + /// /// This message is only supported for the simulator porduino build. /// If received the simulator will exit successfully. @@ -343,9 +344,6 @@ struct AdminMessage { /// Send the current Config in the response to this message. case getModuleConfigResponse(ModuleConfig) /// - /// Send all channels in the response to this message - case getAllChannelRequest(Bool) - /// /// Get the Canned Message Module messages in the response to this message. case getCannedMessageModuleMessagesRequest(Bool) /// @@ -353,7 +351,7 @@ struct AdminMessage { case getCannedMessageModuleMessagesResponse(String) /// /// Request the node to send device metadata (firmware, protobuf version, etc) - case getDeviceMetadataRequest(UInt32) + case getDeviceMetadataRequest(Bool) /// /// Device metadata response case getDeviceMetadataResponse(DeviceMetadata) @@ -392,6 +390,10 @@ struct AdminMessage { /// TODO: REPLACE case confirmSetRadio(Bool) /// + /// Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) + /// Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. + case rebootOtaSeconds(Int32) + /// /// This message is only supported for the simulator porduino build. /// If received the simulator will exit successfully. case exitSimulator(Bool) @@ -446,10 +448,6 @@ struct AdminMessage { guard case .getModuleConfigResponse(let l) = lhs, case .getModuleConfigResponse(let r) = rhs else { preconditionFailure() } return l == r }() - case (.getAllChannelRequest, .getAllChannelRequest): return { - guard case .getAllChannelRequest(let l) = lhs, case .getAllChannelRequest(let r) = rhs else { preconditionFailure() } - return l == r - }() case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return { guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() } return l == r @@ -502,6 +500,10 @@ struct AdminMessage { guard case .confirmSetRadio(let l) = lhs, case .confirmSetRadio(let r) = rhs else { preconditionFailure() } return l == r }() + case (.rebootOtaSeconds, .rebootOtaSeconds): return { + guard case .rebootOtaSeconds(let l) = lhs, case .rebootOtaSeconds(let r) = rhs else { preconditionFailure() } + return l == r + }() case (.exitSimulator, .exitSimulator): return { guard case .exitSimulator(let l) = lhs, case .exitSimulator(let r) = rhs else { preconditionFailure() } return l == r @@ -713,7 +715,6 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 6: .standard(proto: "get_config_response"), 7: .standard(proto: "get_module_config_request"), 8: .standard(proto: "get_module_config_response"), - 9: .standard(proto: "get_all_channel_request"), 10: .standard(proto: "get_canned_message_module_messages_request"), 11: .standard(proto: "get_canned_message_module_messages_response"), 12: .standard(proto: "get_device_metadata_request"), @@ -727,6 +728,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 65: .standard(proto: "confirm_set_module_config"), 66: .standard(proto: "confirm_set_channel"), 67: .standard(proto: "confirm_set_radio"), + 95: .standard(proto: "reboot_ota_seconds"), 96: .standard(proto: "exit_simulator"), 97: .standard(proto: "reboot_seconds"), 98: .standard(proto: "shutdown_seconds"), @@ -824,14 +826,6 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat self.payloadVariant = .getModuleConfigResponse(v) } }() - case 9: try { - var v: Bool? - try decoder.decodeSingularBoolField(value: &v) - if let v = v { - if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} - self.payloadVariant = .getAllChannelRequest(v) - } - }() case 10: try { var v: Bool? try decoder.decodeSingularBoolField(value: &v) @@ -849,8 +843,8 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat } }() case 12: try { - var v: UInt32? - try decoder.decodeSingularUInt32Field(value: &v) + var v: Bool? + try decoder.decodeSingularBoolField(value: &v) if let v = v { if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} self.payloadVariant = .getDeviceMetadataRequest(v) @@ -961,6 +955,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat self.payloadVariant = .confirmSetRadio(v) } }() + case 95: try { + var v: Int32? + try decoder.decodeSingularInt32Field(value: &v) + if let v = v { + if self.payloadVariant != nil {try decoder.handleConflictingOneOf()} + self.payloadVariant = .rebootOtaSeconds(v) + } + }() case 96: try { var v: Bool? try decoder.decodeSingularBoolField(value: &v) @@ -1044,10 +1046,6 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .getModuleConfigResponse(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 8) }() - case .getAllChannelRequest?: try { - guard case .getAllChannelRequest(let v)? = self.payloadVariant else { preconditionFailure() } - try visitor.visitSingularBoolField(value: v, fieldNumber: 9) - }() case .getCannedMessageModuleMessagesRequest?: try { guard case .getCannedMessageModuleMessagesRequest(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularBoolField(value: v, fieldNumber: 10) @@ -1058,7 +1056,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat }() case .getDeviceMetadataRequest?: try { guard case .getDeviceMetadataRequest(let v)? = self.payloadVariant else { preconditionFailure() } - try visitor.visitSingularUInt32Field(value: v, fieldNumber: 12) + try visitor.visitSingularBoolField(value: v, fieldNumber: 12) }() case .getDeviceMetadataResponse?: try { guard case .getDeviceMetadataResponse(let v)? = self.payloadVariant else { preconditionFailure() } @@ -1100,6 +1098,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .confirmSetRadio(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularBoolField(value: v, fieldNumber: 67) }() + case .rebootOtaSeconds?: try { + guard case .rebootOtaSeconds(let v)? = self.payloadVariant else { preconditionFailure() } + try visitor.visitSingularInt32Field(value: v, fieldNumber: 95) + }() case .exitSimulator?: try { guard case .exitSimulator(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularBoolField(value: v, fieldNumber: 96) diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index b7215ece..97e28992 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -675,7 +675,7 @@ struct Config { /// /// The denominator of the coding rate. - /// ie for 4/8, the value is 8. 5/8 the value is 5. + /// ie for 4/5, the value is 5. 4/8 the value is 8. var codingRate: UInt32 = 0 /// diff --git a/Meshtastic/Protobufs/mesh.pb.swift b/Meshtastic/Protobufs/mesh.pb.swift index 1f674306..a548b47f 100644 --- a/Meshtastic/Protobufs/mesh.pb.swift +++ b/Meshtastic/Protobufs/mesh.pb.swift @@ -1850,6 +1850,16 @@ struct FromRadio { set {_uniqueStorage()._payloadVariant = .moduleConfig(newValue)} } + /// + /// One packet is sent for each channel + var channel: Channel { + get { + if case .channel(let v)? = _storage._payloadVariant {return v} + return Channel() + } + set {_uniqueStorage()._payloadVariant = .channel(newValue)} + } + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -1887,6 +1897,9 @@ struct FromRadio { /// /// Include module config case moduleConfig(ModuleConfig) + /// + /// One packet is sent for each channel + case channel(Channel) #if !swift(>=4.1) static func ==(lhs: FromRadio.OneOf_PayloadVariant, rhs: FromRadio.OneOf_PayloadVariant) -> Bool { @@ -1926,6 +1939,10 @@ struct FromRadio { guard case .moduleConfig(let l) = lhs, case .moduleConfig(let r) = rhs else { preconditionFailure() } return l == r }() + case (.channel, .channel): return { + guard case .channel(let l) = lhs, case .channel(let r) = rhs else { preconditionFailure() } + return l == r + }() default: return false } } @@ -3244,6 +3261,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation 7: .standard(proto: "config_complete_id"), 8: .same(proto: "rebooted"), 9: .same(proto: "moduleConfig"), + 10: .same(proto: "channel"), ] fileprivate class _StorageClass { @@ -3370,6 +3388,19 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation _storage._payloadVariant = .moduleConfig(v) } }() + case 10: try { + var v: Channel? + var hadOneofValue = false + if let current = _storage._payloadVariant { + hadOneofValue = true + if case .channel(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._payloadVariant = .channel(v) + } + }() default: break } } @@ -3418,6 +3449,10 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation guard case .moduleConfig(let v)? = _storage._payloadVariant else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 9) }() + case .channel?: try { + guard case .channel(let v)? = _storage._payloadVariant else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 10) + }() case nil: break } } diff --git a/Meshtastic/Protobufs/telemetry.pb.swift b/Meshtastic/Protobufs/telemetry.pb.swift index 73f89cc0..3e745000 100644 --- a/Meshtastic/Protobufs/telemetry.pb.swift +++ b/Meshtastic/Protobufs/telemetry.pb.swift @@ -60,6 +60,14 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { /// /// High accuracy pressure case lps22 // = 8 + + /// + /// 3-Axis magnetic sensor + case qmc6310 // = 9 + + /// + /// 6-Axis inertial measurement sensor + case qmi8658 // = 10 case UNRECOGNIZED(Int) init() { @@ -77,6 +85,8 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { case 6: self = .bmp280 case 7: self = .shtc3 case 8: self = .lps22 + case 9: self = .qmc6310 + case 10: self = .qmi8658 default: self = .UNRECOGNIZED(rawValue) } } @@ -92,6 +102,8 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { case .bmp280: return 6 case .shtc3: return 7 case .lps22: return 8 + case .qmc6310: return 9 + case .qmi8658: return 10 case .UNRECOGNIZED(let i): return i } } @@ -112,6 +124,8 @@ extension TelemetrySensorType: CaseIterable { .bmp280, .shtc3, .lps22, + .qmc6310, + .qmi8658, ] } @@ -272,6 +286,8 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { 6: .same(proto: "BMP280"), 7: .same(proto: "SHTC3"), 8: .same(proto: "LPS22"), + 9: .same(proto: "QMC6310"), + 10: .same(proto: "QMI8658"), ] } diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 445903e0..631c8a57 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -34,15 +34,17 @@ struct ShareChannels: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings + @State var initialLoad: Bool = true + @State var channels: [ChannelEntity] = [ChannelEntity]() @State var includeChannel0 = true - @State var includeChannel1 = true - @State var includeChannel2 = true + @State var includeChannel1 = false + @State var includeChannel2 = false @State var includeChannel3 = false @State var includeChannel4 = false @State var includeChannel5 = false @State var includeChannel6 = false - @State var includeChannel7 = true + @State var includeChannel7 = false var node: NodeInfoEntity? @@ -76,56 +78,60 @@ struct ShareChannels: View { } Divider() - ForEach(node!.myInfo!.channels?.array.sorted(by: { ($0 as! ChannelEntity).index < ($1 as! ChannelEntity).index }) as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in + ForEach(node!.myInfo!.channels?.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in GridRow { Spacer() if channel.index == 0 { + Toggle("Channel 0 Included", isOn: $includeChannel0) .toggleStyle(.switch) .labelsHidden() .disabled(true) - Text("Primary Channel") + Text((channel.name!.isEmpty ? "Primary Channel" : channel.name) ?? "Primary Channel") } else if channel.index == 1 { Toggle("Channel 1 Included", isOn: $includeChannel1) .toggleStyle(.switch) .labelsHidden() - Text("Public Channel") + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 2 { Toggle("Channel 2 Included", isOn: $includeChannel2) .toggleStyle(.switch) .labelsHidden() + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 3 { Toggle("Channel 3 Included", isOn: $includeChannel3) .toggleStyle(.switch) .labelsHidden() + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 4 { Toggle("Channel 4 Included", isOn: $includeChannel4) .toggleStyle(.switch) .labelsHidden() - .disabled(true) + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 5 { Toggle("Channel 5 Included", isOn: $includeChannel5) .toggleStyle(.switch) .labelsHidden() - .disabled(true) + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 6 { Toggle("Channel 6 Included", isOn: $includeChannel6) .toggleStyle(.switch) .labelsHidden() - .disabled(true) + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 7 { Toggle("Channel 7 Included", isOn: $includeChannel7) .toggleStyle(.switch) .labelsHidden() - Text("Admin Channel") - } - if channel.index > 1 && channel.index < 4{ - Text("Private Chat - \(channel.index)") - } - if channel.index > 3 && channel.index < 7{ - Text("Channel - \(channel.index)") + .disabled(channel.role == 0) + Text((channel.name!.isEmpty ? "Admin Channel" : channel.name) ?? "Admin Channel") } Spacer() } @@ -171,7 +177,12 @@ struct ShareChannels: View { }) .onAppear { - self.bleManager.context = context + if self.initialLoad{ + + self.bleManager.context = context + + self.initialLoad = false + } } } .navigationViewStyle(StackNavigationViewStyle()) From 4ec161411b980b03f58c21ab14beca387ad8dd57 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 07:17:57 -0700 Subject: [PATCH 02/16] Channel import updates --- Meshtastic/Helpers/MeshPackets.swift | 46 ++++------- Meshtastic/Views/Settings/ShareChannels.swift | 81 ++++++++++++++++--- 2 files changed, 83 insertions(+), 44 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 6eeaa779..9ad70170 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -822,9 +822,7 @@ func myInfoPacket (myInfo: MyNodeInfo, meshLogging: Bool, context: NSManagedObje func channelPacket (channel: Channel, fromNum: Int64, meshLogging: Bool, context: NSManagedObjectContext) -> NodeInfoEntity? { - print(try! channel.jsonString()) - - if channel.isInitialized { + if channel.isInitialized && channel.hasSettings { let fetchedMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum) @@ -834,35 +832,18 @@ func channelPacket (channel: Channel, fromNum: Int64, meshLogging: Bool, context let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as! [MyInfoEntity] if fetchedMyInfo.count == 1 { + + let newChannel = ChannelEntity(context: context) + newChannel.index = Int32(channel.index) + newChannel.uplinkEnabled = channel.settings.uplinkEnabled + newChannel.downlinkEnabled = channel.settings.downlinkEnabled + newChannel.name = channel.settings.name + newChannel.role = Int32(channel.role.rawValue) - // Update - if fetchedMyInfo[0].channels?.count ?? 0 >= 1 { - - let newChannel = ChannelEntity(context: context) - newChannel.index = Int32(channel.index) - newChannel.uplinkEnabled = channel.settings.uplinkEnabled - newChannel.downlinkEnabled = channel.settings.downlinkEnabled - newChannel.name = channel.settings.name - newChannel.role = Int32(channel.role.rawValue) - - let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet - - mutableChannels.add(newChannel) - fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet - - } else { - - let newChannel = ChannelEntity(context: context) - newChannel.index = Int32(channel.index) - newChannel.uplinkEnabled = channel.settings.uplinkEnabled - newChannel.downlinkEnabled = channel.settings.downlinkEnabled - newChannel.name = channel.settings.name - newChannel.role = Int32(channel.role.rawValue) - - var newChannels = [ChannelEntity]() - newChannels.append(newChannel) - fetchedMyInfo[0].channels! = NSOrderedSet(array: newChannels) - } + let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet + + mutableChannels.add(newChannel) + fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet } else { print("💥 Trying to save a channel to a MyInfo that does not exist: \(fromNum)") @@ -872,7 +853,8 @@ func channelPacket (channel: Channel, fromNum: Int64, meshLogging: Bool, context if meshLogging { - MeshLogger.log("💾 Updated MyInfo channel \(channel.settings.channelNum + 1) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)") + MeshLogger.log("💾 Updated MyInfo channel \(channel.index) from Channel App Packet For: \(fetchedMyInfo[0].myNodeNum)") + } } catch { diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 631c8a57..e9907486 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -36,7 +36,7 @@ struct ShareChannels: View { @EnvironmentObject var userSettings: UserSettings @State var initialLoad: Bool = true - @State var channels: [ChannelEntity] = [ChannelEntity]() + @State var channelSet: ChannelSet = ChannelSet() @State var includeChannel0 = true @State var includeChannel1 = false @State var includeChannel2 = false @@ -46,6 +46,8 @@ struct ShareChannels: View { @State var includeChannel6 = false @State var includeChannel7 = false + @State var isPresentingHelp = false + var node: NodeInfoEntity? @State private var channelsUrl = "https://meshtastic.org/e/#test" @@ -71,12 +73,16 @@ struct ShareChannels: View { Text("Include") .font(.caption) .fontWeight(.bold) - Text("Name") + .padding(.trailing) + Text("Channel Name") + .font(.caption) + .fontWeight(.bold) + .padding(.trailing) + Text("Encrypted") .font(.caption) .fontWeight(.bold) Spacer() } - Divider() ForEach(node!.myInfo!.channels?.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in @@ -88,7 +94,7 @@ struct ShareChannels: View { .toggleStyle(.switch) .labelsHidden() .disabled(true) - Text((channel.name!.isEmpty ? "Primary Channel" : channel.name) ?? "Primary Channel") + Text((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary") } else if channel.index == 1 { Toggle("Channel 1 Included", isOn: $includeChannel1) @@ -131,7 +137,15 @@ struct ShareChannels: View { .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Admin Channel" : channel.name) ?? "Admin Channel") + Text((channel.name!.isEmpty ? "Admin" : channel.name) ?? "Admin") + } + if channel.role > 0 { + Image(systemName: "lock.fill") + .foregroundColor(.green) + } else { + Image(systemName: "lock.slash") + .foregroundColor(.gray) + } Spacer() } @@ -150,23 +164,65 @@ struct ShareChannels: View { preview: SharePreview("Meshtastic Node \(node?.user?.shortName ?? "????") has shared channels with you", image: Image(uiImage: qrImage)) ) - - - Divider() + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.small) + .padding(.bottom) Image(uiImage: qrImage) .resizable() .scaledToFit() .frame( - minWidth: smallest * 0.7, - maxWidth: smallest * 0.7, - minHeight: smallest * 0.7, - maxHeight: smallest * 0.7, + minWidth: smallest * 0.65, + maxWidth: smallest * 0.65, + minHeight: smallest * 0.65, + maxHeight: smallest * 0.65, alignment: .top ) + Button { + + isPresentingHelp = true + + } label: { + + Label("Help Me!", systemImage: "lifepreserver") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.small) + .padding(.top) + } } + .sheet(isPresented: $isPresentingHelp) { + + VStack { + Text("Meshtastic Channels").font(.title) + Text("A Meshtastic LoRa Mesh network can have up to 8 distinct channels.") + .font(.headline) + .padding(.bottom) + Text("Primary Channel").font(.title2) + Text("The first channel is the Primary channel and is where much of the mesh activity takes place. DM's are only available on the primary channel and it can not be disabled.") + .font(.callout) + .padding([.leading,.trailing,.bottom]) + Text("Admin Channel").font(.title2) + Text("The last channel is the Admin channel and can be used to remotely administer nodes on your mesh, text messages can not be sent over the admin channel.") + .font(.callout) + .padding([.leading,.trailing,.bottom]) + Text("Private Channels").font(.title2) + Text("The other six channels can be used for private group converations. Each of these groups has its own encryption key.") + .font(.callout) + .padding([.leading,.trailing,.bottom]) + Text("From this view your primary channel and mesh settings are always shared in the generated QR code and you can toggle to include your admin channel and any private groups you want the person you are sharing with to have access to.") + .font(.callout) + .padding([.leading,.trailing,.bottom]) + Divider() + } + .padding() + .presentationDetents([.large]) + .presentationDragIndicator(.automatic) + } .navigationTitle("Generate QR Code") .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: @@ -182,6 +238,7 @@ struct ShareChannels: View { self.bleManager.context = context self.initialLoad = false + channelSet = ChannelSet() } } } From 106069f95a0971cc2c27ceb5112d3826be136bc0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 13:40:18 -0700 Subject: [PATCH 03/16] Generate QR Code for channels --- Meshtastic/Helpers/BLEManager.swift | 2 +- Meshtastic/Helpers/MeshPackets.swift | 26 +++++----- .../MeshtasticDataModel.xcdatamodel/contents | 4 +- Meshtastic/Views/Settings/ShareChannels.swift | 47 +++++++++++++++---- 4 files changed, 54 insertions(+), 25 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index bdc28320..6b5058df 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -29,7 +29,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph @Published var connectedPeripheral: Peripheral! @Published var lastConnectionError: String - @Published var minimumVersion = "1.3.41" + @Published var minimumVersion = "1.3.42" @Published var connectedVersion: String @Published var invalidVersion = false @Published var preferredPeripheral = false diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 9ad70170..2792317e 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -194,32 +194,31 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont let newLoRaConfig = LoRaConfigEntity(context: context) newLoRaConfig.regionCode = Int32(config.lora.region.rawValue) - newLoRaConfig.modemPreset = Int32(config.lora.modemPreset.rawValue) - newLoRaConfig.hopLimit = Int32(config.lora.hopLimit) - newLoRaConfig.txPower = Int32(config.lora.txPower) - newLoRaConfig.txEnabled = config.lora.txEnabled newLoRaConfig.usePreset = config.lora.usePreset + newLoRaConfig.modemPreset = Int32(config.lora.modemPreset.rawValue) newLoRaConfig.bandwidth = Int32(config.lora.bandwidth) newLoRaConfig.spreadFactor = Int32(config.lora.spreadFactor) newLoRaConfig.codingRate = Int32(config.lora.codingRate) - newLoRaConfig.spreadFactor = Int32(config.lora.spreadFactor) - newLoRaConfig.frequencyOffset = Int32(config.lora.frequencyOffset) - + newLoRaConfig.frequencyOffset = config.lora.frequencyOffset + newLoRaConfig.hopLimit = Int32(config.lora.hopLimit) + newLoRaConfig.txPower = Int32(config.lora.txPower) + newLoRaConfig.txEnabled = config.lora.txEnabled + newLoRaConfig.channelNum = Int32(config.lora.channelNum) fetchedNode[0].loRaConfig = newLoRaConfig } else { fetchedNode[0].loRaConfig?.regionCode = Int32(config.lora.region.rawValue) - fetchedNode[0].loRaConfig?.modemPreset = Int32(config.lora.modemPreset.rawValue) - fetchedNode[0].loRaConfig?.hopLimit = Int32(config.lora.hopLimit) - fetchedNode[0].loRaConfig?.txPower = Int32(config.lora.txPower) - fetchedNode[0].loRaConfig?.txEnabled = config.lora.txEnabled fetchedNode[0].loRaConfig?.usePreset = config.lora.usePreset + fetchedNode[0].loRaConfig?.modemPreset = Int32(config.lora.modemPreset.rawValue) fetchedNode[0].loRaConfig?.bandwidth = Int32(config.lora.bandwidth) fetchedNode[0].loRaConfig?.spreadFactor = Int32(config.lora.spreadFactor) fetchedNode[0].loRaConfig?.codingRate = Int32(config.lora.codingRate) - fetchedNode[0].loRaConfig?.spreadFactor = Int32(config.lora.spreadFactor) - fetchedNode[0].loRaConfig?.frequencyOffset = Int32(config.lora.frequencyOffset) + fetchedNode[0].loRaConfig?.frequencyOffset = config.lora.frequencyOffset + fetchedNode[0].loRaConfig?.hopLimit = Int32(config.lora.hopLimit) + fetchedNode[0].loRaConfig?.txPower = Int32(config.lora.txPower) + fetchedNode[0].loRaConfig?.txEnabled = config.lora.txEnabled + fetchedNode[0].loRaConfig?.channelNum = Int32(config.lora.channelNum) } do { @@ -239,7 +238,6 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Lora Config") } - } catch { let nsError = error as NSError diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel.xcdatamodel/contents index a34a3211..c79ca23e 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel.xcdatamodel/contents @@ -24,7 +24,7 @@ - + @@ -60,7 +60,7 @@ - + diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index e9907486..d5ac1ae1 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -29,6 +29,8 @@ struct QrCodeImage { return qrImage } } + + struct ShareChannels: View { @Environment(\.managedObjectContext) var context @@ -50,7 +52,7 @@ struct ShareChannels: View { var node: NodeInfoEntity? - @State private var channelsUrl = "https://meshtastic.org/e/#test" + @State private var channelsUrl = "https://meshtastic.org/e/#" var qrCodeImage = QrCodeImage() var body: some View { @@ -93,7 +95,7 @@ struct ShareChannels: View { Toggle("Channel 0 Included", isOn: $includeChannel0) .toggleStyle(.switch) .labelsHidden() - .disabled(true) + .disabled(channel.role == 1) Text((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary") } else if channel.index == 1 { @@ -142,10 +144,9 @@ struct ShareChannels: View { if channel.role > 0 { Image(systemName: "lock.fill") .foregroundColor(.green) - } else { + } else { Image(systemName: "lock.slash") .foregroundColor(.gray) - } Spacer() } @@ -166,8 +167,7 @@ struct ShareChannels: View { ) .buttonStyle(.bordered) .buttonBorderShape(.capsule) - .controlSize(.small) - .padding(.bottom) + .controlSize(.large) Image(uiImage: qrImage) .resizable() @@ -192,7 +192,6 @@ struct ShareChannels: View { .buttonBorderShape(.capsule) .controlSize(.small) .padding(.top) - } } .sheet(isPresented: $isPresentingHelp) { @@ -238,11 +237,43 @@ struct ShareChannels: View { self.bleManager.context = context self.initialLoad = false - channelSet = ChannelSet() + GenerateChannelSet() } } } .navigationViewStyle(StackNavigationViewStyle()) } } + func GenerateChannelSet() { + + var loRaConfig = Config.LoRaConfig() + loRaConfig.region = RegionCodes(rawValue: Int(node!.loRaConfig!.regionCode))!.protoEnumValue() + loRaConfig.modemPreset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset))!.protoEnumValue() + loRaConfig.bandwidth = UInt32(node!.loRaConfig!.bandwidth) + loRaConfig.spreadFactor = UInt32(node!.loRaConfig!.spreadFactor) + loRaConfig.codingRate = UInt32(node!.loRaConfig!.codingRate) + loRaConfig.frequencyOffset = node!.loRaConfig!.frequencyOffset + loRaConfig.hopLimit = UInt32(node!.loRaConfig!.hopLimit) + loRaConfig.txEnabled = node!.loRaConfig!.txEnabled + loRaConfig.txPower = node!.loRaConfig!.txPower + loRaConfig.channelNum = UInt32(node!.loRaConfig!.channelNum) + + channelSet.loraConfig = loRaConfig + + for ch in node!.myInfo!.channels!.array as! [ChannelEntity] { + print(ch) + if ch.role > 0 { + var channelSettings = ChannelSettings() + channelSettings.name = ch.name! + channelSettings.psk = ch.psk ?? Data() + channelSettings.id = UInt32(ch.id) + channelSettings.uplinkEnabled = ch.uplinkEnabled + channelSettings.downlinkEnabled = ch.downlinkEnabled + channelSet.settings.append(channelSettings) + } + } + + let settingsString = try! channelSet.serializedData().base64EncodedString(options: [.endLineWithLineFeed]) + channelsUrl = "https://www.meshtastic.org/e/#" + settingsString.dropLast(2) + } } From d054014a9570354bb6c79a7c89aa8743f95c45df Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 13:41:25 -0700 Subject: [PATCH 04/16] 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 49adb40d..f4874f56 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -964,7 +964,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.41; + MARKETING_VERSION = 1.3.42; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -996,7 +996,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.41; + MARKETING_VERSION = 1.3.42; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From 3464fdb6c784a78f439b296fd428eeaef6279f71 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 13:42:03 -0700 Subject: [PATCH 05/16] 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 f4874f56..298891c0 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -964,7 +964,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.42; + MARKETING_VERSION = 1.3.43; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -996,7 +996,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.42; + MARKETING_VERSION = 1.3.43; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From 183ce7194c6c911edcddb5004b868d6223b2ba88 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 13:50:57 -0700 Subject: [PATCH 06/16] Clean up qr code generator --- Meshtastic/Views/Settings/ShareChannels.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index d5ac1ae1..ab913560 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -261,7 +261,6 @@ struct ShareChannels: View { channelSet.loraConfig = loRaConfig for ch in node!.myInfo!.channels!.array as! [ChannelEntity] { - print(ch) if ch.role > 0 { var channelSettings = ChannelSettings() channelSettings.name = ch.name! @@ -273,7 +272,7 @@ struct ShareChannels: View { } } - let settingsString = try! channelSet.serializedData().base64EncodedString(options: [.endLineWithLineFeed]) + let settingsString = try! channelSet.serializedData().base64EncodedString() channelsUrl = "https://www.meshtastic.org/e/#" + settingsString.dropLast(2) } } From e24c9df7ffde03bd06529d5e83d5026dde965a97 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 14:22:26 -0700 Subject: [PATCH 07/16] Add www to url --- Meshtastic/Views/Settings/ShareChannels.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index ab913560..4d3f86da 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -52,7 +52,7 @@ struct ShareChannels: View { var node: NodeInfoEntity? - @State private var channelsUrl = "https://meshtastic.org/e/#" + @State private var channelsUrl = "https://www.meshtastic.org/e/#" var qrCodeImage = QrCodeImage() var body: some View { From 4df2f4614b129300270930179c4d72bea73cb042 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 15:02:27 -0700 Subject: [PATCH 08/16] Bump required version with proto updates --- Meshtastic/MeshtasticApp.swift | 18 +++++++++++++++--- .../Views/Settings/SaveChannelQRCode.swift | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 539b11f1..25e3cbce 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -13,6 +13,7 @@ struct MeshtasticAppleApp: App { @State var saveChannels = false @State var incomingUrl: URL? + @State var channelSettings: String? @Environment(\.scenePhase) var scenePhase @@ -27,15 +28,23 @@ struct MeshtasticAppleApp: App { print("URL received \(userActivity)") incomingUrl = userActivity.webpageURL - if incomingUrl!.absoluteString.lowercased().contains("https://meshtastic.org/e/#") { + + if incomingUrl!.absoluteString.lowercased().contains("meshtastic.org/e/#") { + + if let components = incomingUrl?.absoluteString.components(separatedBy: "#") { + channelSettings = components.last! + } saveChannels = true + print("User wants to open a Channel Settings URL: \(incomingUrl?.absoluteString ?? "No QR Code Link")") } if saveChannels { print("User wants to open Channel Settings URL: \(String(describing: incomingUrl!.relativeString))") } } .sheet(isPresented: $saveChannels) { - SaveChannelQRCode(channelHash: incomingUrl?.absoluteString ?? "Empty Channel URL") + + let channelSettingsString = incomingUrl?.absoluteString + SaveChannelQRCode(channelHash: channelSettings ?? "Empty Channel URL") .presentationDetents([.medium, .large]) .presentationDragIndicator(.visible) } @@ -44,7 +53,10 @@ struct MeshtasticAppleApp: App { print("Some sort of URL was received \(url)") incomingUrl = url - if url.absoluteString.lowercased().contains("https://meshtastic.org/e/#") { + if url.absoluteString.lowercased().contains("meshtastic.org/e/#") { + if let components = incomingUrl?.absoluteString.components(separatedBy: "#") { + channelSettings = components.last! + } saveChannels = true print("User wants to open a Channel Settings URL: \(incomingUrl?.absoluteString ?? "No QR Code Link")") } else { diff --git a/Meshtastic/Views/Settings/SaveChannelQRCode.swift b/Meshtastic/Views/Settings/SaveChannelQRCode.swift index 4024f861..cfb4e281 100644 --- a/Meshtastic/Views/Settings/SaveChannelQRCode.swift +++ b/Meshtastic/Views/Settings/SaveChannelQRCode.swift @@ -23,7 +23,7 @@ struct SaveChannelQRCode: View { .padding() Text(channelHash) - .font(.title2) + .font(.callout) .padding() Text("This does not work yet.") From 1742ec3b48ebb4151723f73731f92035420ec0cb Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 18:59:33 -0700 Subject: [PATCH 09/16] Finish up share channels view --- Meshtastic/Helpers/BLEManager.swift | 2 +- Meshtastic/Views/Settings/ShareChannels.swift | 45 +++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 6b5058df..2b74a13a 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -29,7 +29,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph @Published var connectedPeripheral: Peripheral! @Published var lastConnectionError: String - @Published var minimumVersion = "1.3.42" + @Published var minimumVersion = "1.3.43" @Published var connectedVersion: String @Published var invalidVersion = false @Published var preferredPeripheral = false diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 4d3f86da..b163106c 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -53,6 +53,7 @@ struct ShareChannels: View { var node: NodeInfoEntity? @State private var channelsUrl = "https://www.meshtastic.org/e/#" + var qrCodeImage = QrCodeImage() var body: some View { @@ -240,11 +241,34 @@ struct ShareChannels: View { GenerateChannelSet() } } + .onChange(of: includeChannel1) { includeCh1 in + GenerateChannelSet() + } + .onChange(of: includeChannel2) { includeCh2 in + GenerateChannelSet() + } + .onChange(of: includeChannel3) { includeCh3 in + GenerateChannelSet() + } + .onChange(of: includeChannel4) { includeCh4 in + GenerateChannelSet() + } + .onChange(of: includeChannel5) { includeCh5 in + GenerateChannelSet() + } + .onChange(of: includeChannel6) { includeCh6 in + GenerateChannelSet() + } + .onChange(of: includeChannel7) { includeCh7 in + GenerateChannelSet() + } } .navigationViewStyle(StackNavigationViewStyle()) } } func GenerateChannelSet() { + + channelSet = ChannelSet() var loRaConfig = Config.LoRaConfig() loRaConfig.region = RegionCodes(rawValue: Int(node!.loRaConfig!.regionCode))!.protoEnumValue() @@ -262,17 +286,22 @@ struct ShareChannels: View { for ch in node!.myInfo!.channels!.array as! [ChannelEntity] { if ch.role > 0 { - var channelSettings = ChannelSettings() - channelSettings.name = ch.name! - channelSettings.psk = ch.psk ?? Data() - channelSettings.id = UInt32(ch.id) - channelSettings.uplinkEnabled = ch.uplinkEnabled - channelSettings.downlinkEnabled = ch.downlinkEnabled - channelSet.settings.append(channelSettings) + + if ch.index == 0 || ch.index == 1 && includeChannel1 || ch.index == 2 && includeChannel2 || ch.index == 3 && includeChannel3 || + ch.index == 4 && includeChannel4 || ch.index == 5 && includeChannel5 || ch.index == 6 && includeChannel6 || ch.index == 7 && includeChannel7 { + + var channelSettings = ChannelSettings() + channelSettings.name = ch.name! + channelSettings.psk = ch.psk ?? Data() + channelSettings.id = UInt32(ch.id) + channelSettings.uplinkEnabled = ch.uplinkEnabled + channelSettings.downlinkEnabled = ch.downlinkEnabled + channelSet.settings.append(channelSettings) + } } } let settingsString = try! channelSet.serializedData().base64EncodedString() - channelsUrl = "https://www.meshtastic.org/e/#" + settingsString.dropLast(2) + channelsUrl = ("https://www.meshtastic.org/e/#" + settingsString.dropLast(2)) } } From d1d5bb653728811bf96a7a4000b77defa3d08868 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 19:24:01 -0700 Subject: [PATCH 10/16] Fix up buttons --- Meshtastic/Views/Settings/ShareChannels.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index b163106c..aaaf0081 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -192,7 +192,6 @@ struct ShareChannels: View { .buttonStyle(.bordered) .buttonBorderShape(.capsule) .controlSize(.small) - .padding(.top) } } .sheet(isPresented: $isPresentingHelp) { From 96aa9a60a6d4fa1c54edb4adcf9fe339ef46766f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 10 Oct 2022 21:21:58 -0700 Subject: [PATCH 11/16] Decode qr code string --- Meshtastic/MeshtasticApp.swift | 42 ++++++++++--------- .../Views/Settings/SaveChannelQRCode.swift | 26 ++++++++++-- Meshtastic/Views/Settings/ShareChannels.swift | 2 +- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 25e3cbce..a4f99ec7 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -42,8 +42,7 @@ struct MeshtasticAppleApp: App { } } .sheet(isPresented: $saveChannels) { - - let channelSettingsString = incomingUrl?.absoluteString + SaveChannelQRCode(channelHash: channelSettings ?? "Empty Channel URL") .presentationDetents([.medium, .large]) .presentationDragIndicator(.visible) @@ -60,6 +59,7 @@ struct MeshtasticAppleApp: App { saveChannels = true print("User wants to open a Channel Settings URL: \(incomingUrl?.absoluteString ?? "No QR Code Link")") } else { + saveChannels = false print("User wants to import a MBTILES offline map file: \(incomingUrl?.absoluteString ?? "No Tiles link")") } @@ -69,26 +69,28 @@ struct MeshtasticAppleApp: App { let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! let destination = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false) - //do we need to delete an old one? - if (fileManager.fileExists(atPath: destination.path)) { - print("ℹ️ Found an old map file. Deleting it") - try? fileManager.removeItem(atPath: destination.path) - } - - do { - try fileManager.copyItem(at: url, to: destination) - } catch { - print("Copy MB Tile file failed. Error: \(error)") - } - - if (fileManager.fileExists(atPath: destination.path)) { - print("ℹ️ Saved the map file") + if !saveChannels { + //do we need to delete an old one? + if (fileManager.fileExists(atPath: destination.path)) { + print("ℹ️ Found an old map file. Deleting it") + try? fileManager.removeItem(atPath: destination.path) + } - //need to tell the map view that it needs to update and try loading the new overlay - UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastUpdatedLocalMapFile") + do { + try fileManager.copyItem(at: url, to: destination) + } catch { + print("Copy MB Tile file failed. Error: \(error)") + } - } else { - print("💥 Didn't save the map file") + if (fileManager.fileExists(atPath: destination.path)) { + print("ℹ️ Saved the map file") + + //need to tell the map view that it needs to update and try loading the new overlay + UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastUpdatedLocalMapFile") + + } else { + print("💥 Didn't save the map file") + } } }) } diff --git a/Meshtastic/Views/Settings/SaveChannelQRCode.swift b/Meshtastic/Views/Settings/SaveChannelQRCode.swift index cfb4e281..b9f1020d 100644 --- a/Meshtastic/Views/Settings/SaveChannelQRCode.swift +++ b/Meshtastic/Views/Settings/SaveChannelQRCode.swift @@ -9,7 +9,7 @@ import SwiftUI struct SaveChannelQRCode: View { var channelHash: String - + var body: some View { VStack { @@ -17,22 +17,40 @@ struct SaveChannelQRCode: View { Text("Save Channel Settings?") .font(.title) - Text("The settings embedded in this QR code will replace the current settings on your radio.") + Text("These settings will replace the current settings on your radio.") .foregroundColor(.gray) .font(.callout) .padding() Text(channelHash) - .font(.callout) + .font(.caption2) .padding() - Text("This does not work yet.") + Text("Error Message") .font(.callout) + .foregroundColor(.red) .padding() Text("Swipe down to dismiss.") .padding() } + .onChange(of: channelHash) { newSettings in + + var decodedString = newSettings + if !decodedString.hasSuffix("==") { + decodedString = decodedString + "==" + } + + if let decodedData = Data(base64Encoded: decodedString) { + decodedString = String(data: decodedData, encoding: .utf8)! + do { + var channelSet: ChannelSet = try ChannelSet(serializedData: decodedData) + print(channelSet) + } catch { + print("Invalid Meshtastic QR Code Link") + } + } + } } } diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index aaaf0081..48000f25 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -286,7 +286,7 @@ struct ShareChannels: View { for ch in node!.myInfo!.channels!.array as! [ChannelEntity] { if ch.role > 0 { - if ch.index == 0 || ch.index == 1 && includeChannel1 || ch.index == 2 && includeChannel2 || ch.index == 3 && includeChannel3 || + if ch.index == 0 && includeChannel0 || ch.index == 1 && includeChannel1 || ch.index == 2 && includeChannel2 || ch.index == 3 && includeChannel3 || ch.index == 4 && includeChannel4 || ch.index == 5 && includeChannel5 || ch.index == 6 && includeChannel6 || ch.index == 7 && includeChannel7 { var channelSettings = ChannelSettings() From 03d4dd2d2bba48ab9958fdb8e9be08137ca82b9c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 11 Oct 2022 04:32:17 -0700 Subject: [PATCH 12/16] Set PSK for each channel --- Meshtastic/Helpers/MeshPackets.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 2792317e..fac17e9d 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -837,6 +837,7 @@ func channelPacket (channel: Channel, fromNum: Int64, meshLogging: Bool, context newChannel.downlinkEnabled = channel.settings.downlinkEnabled newChannel.name = channel.settings.name newChannel.role = Int32(channel.role.rawValue) + newChannel.psk = channel.settings.psk let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet From 0bea6f8fa3330026518a2a4625b4c9df4d821e6e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 11 Oct 2022 05:36:49 -0700 Subject: [PATCH 13/16] handle base64 urls --- Meshtastic/Helpers/Extensions.swift | 18 +++++++++++++++++ .../Views/Settings/SaveChannelQRCode.swift | 5 +---- Meshtastic/Views/Settings/ShareChannels.swift | 20 +++++++++---------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Meshtastic/Helpers/Extensions.swift b/Meshtastic/Helpers/Extensions.swift index 8f6abe9e..1640950f 100644 --- a/Meshtastic/Helpers/Extensions.swift +++ b/Meshtastic/Helpers/Extensions.swift @@ -57,6 +57,24 @@ extension String { return data } + func base64urlToBase64() -> String { + var base64 = self + .replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + if base64.count % 4 != 0 { + base64.append(String(repeating: "=", count: 4 - base64.count % 4)) + } + return base64 + } + + func base64ToBase64url() -> String { + let base64url = self + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + return base64url + } + func image(fontSize:CGFloat = 40, bgColor:UIColor = UIColor.clear, imageSize:CGSize? = nil) -> UIImage? { let font = UIFont.systemFont(ofSize: fontSize) diff --git a/Meshtastic/Views/Settings/SaveChannelQRCode.swift b/Meshtastic/Views/Settings/SaveChannelQRCode.swift index b9f1020d..5ff03068 100644 --- a/Meshtastic/Views/Settings/SaveChannelQRCode.swift +++ b/Meshtastic/Views/Settings/SaveChannelQRCode.swift @@ -37,10 +37,7 @@ struct SaveChannelQRCode: View { } .onChange(of: channelHash) { newSettings in - var decodedString = newSettings - if !decodedString.hasSuffix("==") { - decodedString = decodedString + "==" - } + var decodedString = newSettings.base64urlToBase64() if let decodedData = Data(base64Encoded: decodedString) { decodedString = String(data: decodedData, encoding: .utf8)! diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 48000f25..6d93b643 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -97,50 +97,50 @@ struct ShareChannels: View { .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 1) - Text((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary") + Text((channel.name!.isEmpty ? "primary" : channel.name) ?? "primary") } else if channel.index == 1 { Toggle("Channel 1 Included", isOn: $includeChannel1) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 2 { Toggle("Channel 2 Included", isOn: $includeChannel2) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 3 { Toggle("Channel 3 Included", isOn: $includeChannel3) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 4 { Toggle("Channel 4 Included", isOn: $includeChannel4) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 5 { Toggle("Channel 5 Included", isOn: $includeChannel5) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 6 { Toggle("Channel 6 Included", isOn: $includeChannel6) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } else if channel.index == 7 { Toggle("Channel 7 Included", isOn: $includeChannel7) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "Admin" : channel.name) ?? "Admin") + Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") } if channel.role > 0 { Image(systemName: "lock.fill") @@ -206,7 +206,7 @@ struct ShareChannels: View { .font(.callout) .padding([.leading,.trailing,.bottom]) Text("Admin Channel").font(.title2) - Text("The last channel is the Admin channel and can be used to remotely administer nodes on your mesh, text messages can not be sent over the admin channel.") + Text("A channel with the name 'admin' is the Admin channel and can be used to remotely administer nodes on your mesh, text messages can not be sent over the admin channel.") .font(.callout) .padding([.leading,.trailing,.bottom]) Text("Private Channels").font(.title2) @@ -301,6 +301,6 @@ struct ShareChannels: View { } let settingsString = try! channelSet.serializedData().base64EncodedString() - channelsUrl = ("https://www.meshtastic.org/e/#" + settingsString.dropLast(2)) + channelsUrl = ("https://www.meshtastic.org/e/#" + settingsString.base64ToBase64url()) } } From df57f7768620c19647200fac183cbea072f3ef1d Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 11 Oct 2022 05:38:47 -0700 Subject: [PATCH 14/16] Remove whitespace --- Meshtastic/Helpers/Extensions.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Meshtastic/Helpers/Extensions.swift b/Meshtastic/Helpers/Extensions.swift index 1640950f..de4b4ddf 100644 --- a/Meshtastic/Helpers/Extensions.swift +++ b/Meshtastic/Helpers/Extensions.swift @@ -44,16 +44,13 @@ extension String { var hexadecimal: Data? { var data = Data(capacity: count / 2) - let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in let byteString = (self as NSString).substring(with: match!.range) let num = UInt8(byteString, radix: 16)! data.append(num) } - guard data.count > 0 else { return nil } - return data } @@ -80,7 +77,6 @@ extension String { let font = UIFont.systemFont(ofSize: fontSize) let attributes = [NSAttributedString.Key.font: font] let imageSize = imageSize ?? self.size(withAttributes: attributes) - UIGraphicsBeginImageContextWithOptions(imageSize, false, 0) bgColor.set() let rect = CGRect(origin: .zero, size: imageSize) From 8cf3520cae6b658159135297a428584864346eb3 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 11 Oct 2022 06:02:23 -0700 Subject: [PATCH 15/16] Fix base64 == append --- Meshtastic/Helpers/Extensions.swift | 23 +------------------ Meshtastic/Views/Settings/ShareChannels.swift | 5 ---- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/Meshtastic/Helpers/Extensions.swift b/Meshtastic/Helpers/Extensions.swift index de4b4ddf..67dbb074 100644 --- a/Meshtastic/Helpers/Extensions.swift +++ b/Meshtastic/Helpers/Extensions.swift @@ -2,9 +2,6 @@ import Foundation import SwiftUI extension Data { - var hexDescription: String { - return reduce("") {$0 + String(format: "%02x", $1)} - } var macAddressString: String { let mac: String = reduce("") {$0 + String(format: "%02x:", $1)} return String(mac.dropLast()) @@ -35,31 +32,13 @@ extension Int { } extension String { - - /// Create `Data` from hexadecimal string representation - /// - /// This creates a `Data` object from hex string. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed. - /// - /// - returns: Data represented by this hexadecimal string. - - var hexadecimal: Data? { - var data = Data(capacity: count / 2) - let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) - regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in - let byteString = (self as NSString).substring(with: match!.range) - let num = UInt8(byteString, radix: 16)! - data.append(num) - } - guard data.count > 0 else { return nil } - return data - } func base64urlToBase64() -> String { var base64 = self .replacingOccurrences(of: "-", with: "+") .replacingOccurrences(of: "_", with: "/") if base64.count % 4 != 0 { - base64.append(String(repeating: "=", count: 4 - base64.count % 4)) + base64.append(String(repeating: "==", count: 4 - base64.count % 4)) } return base64 } diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 6d93b643..e7a259ed 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -266,9 +266,7 @@ struct ShareChannels: View { } } func GenerateChannelSet() { - channelSet = ChannelSet() - var loRaConfig = Config.LoRaConfig() loRaConfig.region = RegionCodes(rawValue: Int(node!.loRaConfig!.regionCode))!.protoEnumValue() loRaConfig.modemPreset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset))!.protoEnumValue() @@ -280,9 +278,7 @@ struct ShareChannels: View { loRaConfig.txEnabled = node!.loRaConfig!.txEnabled loRaConfig.txPower = node!.loRaConfig!.txPower loRaConfig.channelNum = UInt32(node!.loRaConfig!.channelNum) - channelSet.loraConfig = loRaConfig - for ch in node!.myInfo!.channels!.array as! [ChannelEntity] { if ch.role > 0 { @@ -299,7 +295,6 @@ struct ShareChannels: View { } } } - let settingsString = try! channelSet.serializedData().base64EncodedString() channelsUrl = ("https://www.meshtastic.org/e/#" + settingsString.base64ToBase64url()) } From b5b6e41776911f2efd13840c36564c4339a12694 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 11 Oct 2022 06:22:16 -0700 Subject: [PATCH 16/16] Whtespace --- Meshtastic/Views/Settings/ShareChannels.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index e7a259ed..cbc3a052 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -92,13 +92,11 @@ struct ShareChannels: View { GridRow { Spacer() if channel.index == 0 { - Toggle("Channel 0 Included", isOn: $includeChannel0) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 1) Text((channel.name!.isEmpty ? "primary" : channel.name) ?? "primary") - } else if channel.index == 1 { Toggle("Channel 1 Included", isOn: $includeChannel1) .toggleStyle(.switch)