From 91ce572accc45e4e9c338480a3918fd266739da8 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 17 Feb 2023 10:35:37 -0800 Subject: [PATCH] Update protobufs, show alert and clear database on change of preferred peripheral --- Meshtastic.xcodeproj/project.pbxproj | 8 +- Meshtastic/Helpers/BLEManager.swift | 6 + .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 2 +- .../contents | 300 ++++++++++++++++++ .../Protobufs/meshtastic/admin.pb.swift | 10 + .../Protobufs/meshtastic/config.pb.swift | 11 + .../meshtastic/device_metadata.pb.swift | 157 --------- Meshtastic/Protobufs/meshtastic/mesh.pb.swift | 172 ++++++++++ Meshtastic/Views/Bluetooth/Connect.swift | 55 +++- Meshtastic/Views/ContentView.swift | 2 +- .../Views/Map/Custom/MapViewSwiftUI.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 5 +- Meshtastic/Views/Settings/AppSettings.swift | 4 - 14 files changed, 547 insertions(+), 189 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV8.xcdatamodel/contents delete mode 100644 Meshtastic/Protobufs/meshtastic/device_metadata.pb.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 6337fbda..27c1dcce 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -50,7 +50,6 @@ DD5E5208298EE33B00D21B61 /* rtttl.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51F6298EE33B00D21B61 /* rtttl.pb.swift */; }; DD5E5209298EE33B00D21B61 /* module_config.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51F7298EE33B00D21B61 /* module_config.pb.swift */; }; DD5E520A298EE33B00D21B61 /* channel.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51F8298EE33B00D21B61 /* channel.pb.swift */; }; - DD5E520B298EE33B00D21B61 /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51F9298EE33B00D21B61 /* device_metadata.pb.swift */; }; DD5E520C298EE33B00D21B61 /* portnums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51FA298EE33B00D21B61 /* portnums.pb.swift */; }; DD5E520D298EE33B00D21B61 /* storeforward.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51FB298EE33B00D21B61 /* storeforward.pb.swift */; }; DD5E520E298EE33B00D21B61 /* mqtt.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5E51FC298EE33B00D21B61 /* mqtt.pb.swift */; }; @@ -183,7 +182,6 @@ DD5E51F6298EE33B00D21B61 /* rtttl.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = rtttl.pb.swift; sourceTree = ""; }; DD5E51F7298EE33B00D21B61 /* module_config.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = module_config.pb.swift; sourceTree = ""; }; DD5E51F8298EE33B00D21B61 /* channel.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = channel.pb.swift; sourceTree = ""; }; - DD5E51F9298EE33B00D21B61 /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = ""; }; DD5E51FA298EE33B00D21B61 /* portnums.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = portnums.pb.swift; sourceTree = ""; }; DD5E51FB298EE33B00D21B61 /* storeforward.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = storeforward.pb.swift; sourceTree = ""; }; DD5E51FC298EE33B00D21B61 /* mqtt.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mqtt.pb.swift; sourceTree = ""; }; @@ -237,6 +235,7 @@ DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfigEnums.swift; sourceTree = ""; }; DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayEnums.swift; sourceTree = ""; }; DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = ""; }; + DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV8.xcdatamodel; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; @@ -363,7 +362,6 @@ DD5E51F6298EE33B00D21B61 /* rtttl.pb.swift */, DD5E51F7298EE33B00D21B61 /* module_config.pb.swift */, DD5E51F8298EE33B00D21B61 /* channel.pb.swift */, - DD5E51F9298EE33B00D21B61 /* device_metadata.pb.swift */, DD5E51FA298EE33B00D21B61 /* portnums.pb.swift */, DD5E51FB298EE33B00D21B61 /* storeforward.pb.swift */, DD5E51FC298EE33B00D21B61 /* mqtt.pb.swift */, @@ -847,7 +845,6 @@ DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, DD8ED9C52898D51F00B3B0AB /* NetworkConfig.swift in Sources */, DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */, - DD5E520B298EE33B00D21B61 /* device_metadata.pb.swift in Sources */, DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */, DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */, @@ -1280,6 +1277,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */, DD5E51CC2986643400D21B61 /* MeshtasticDataModelV7.xcdatamodel */, DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */, DD457BC4295D5E35004BCE4D /* MeshtasticDataModelV5.xcdatamodel */, @@ -1288,7 +1286,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DD5E51CC2986643400D21B61 /* MeshtasticDataModelV7.xcdatamodel */; + currentVersion = DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 8f43c58d..4bad7d2e 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -498,6 +498,12 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } } } + // Device Metadata + if decodedInfo.metadata.firmwareVersion.count > 0 && !invalidVersion { + nowKnown = true + deviceMetadataPacket(metadata: decodedInfo.metadata, fromNum: connectedPeripheral.num, context: context!) + } + // Log any other unknownApp calls if !nowKnown { MeshLogger.log("🕸️ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") } diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index aab08b69..25c323b3 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV7.xcdatamodel + MeshtasticDataModelV8.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents index 35e5b8ef..cae65994 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV7.xcdatamodel/contents @@ -1,5 +1,5 @@ - + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV8.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV8.xcdatamodel/contents new file mode 100644 index 00000000..1945a68a --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV8.xcdatamodel/contents @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Protobufs/meshtastic/admin.pb.swift b/Meshtastic/Protobufs/meshtastic/admin.pb.swift index 1cda1982..eb4aafba 100644 --- a/Meshtastic/Protobufs/meshtastic/admin.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/admin.pb.swift @@ -794,6 +794,10 @@ struct HamParameters { /// Ensure your radio is capable of operating of the selected frequency before setting this. var frequency: Float = 0 + /// + /// Optional short name of user + var shortName: String = String() + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1335,6 +1339,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa 1: .standard(proto: "call_sign"), 2: .standard(proto: "tx_power"), 3: .same(proto: "frequency"), + 4: .standard(proto: "short_name"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1346,6 +1351,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa case 1: try { try decoder.decodeSingularStringField(value: &self.callSign) }() case 2: try { try decoder.decodeSingularInt32Field(value: &self.txPower) }() case 3: try { try decoder.decodeSingularFloatField(value: &self.frequency) }() + case 4: try { try decoder.decodeSingularStringField(value: &self.shortName) }() default: break } } @@ -1361,6 +1367,9 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa if self.frequency != 0 { try visitor.visitSingularFloatField(value: self.frequency, fieldNumber: 3) } + if !self.shortName.isEmpty { + try visitor.visitSingularStringField(value: self.shortName, fieldNumber: 4) + } try unknownFields.traverse(visitor: &visitor) } @@ -1368,6 +1377,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa if lhs.callSign != rhs.callSign {return false} if lhs.txPower != rhs.txPower {return false} if lhs.frequency != rhs.frequency {return false} + if lhs.shortName != rhs.shortName {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/config.pb.swift b/Meshtastic/Protobufs/meshtastic/config.pb.swift index 0013ae82..c6bfb474 100644 --- a/Meshtastic/Protobufs/meshtastic/config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/config.pb.swift @@ -172,6 +172,11 @@ struct Config { /// Sets the role of node var rebroadcastMode: Config.DeviceConfig.RebroadcastMode = .all + /// + /// Send our nodeinfo this often + /// Defaults to 900 Seconds (15 minutes) + var nodeInfoBroadcastSecs: UInt32 = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -1561,6 +1566,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl 4: .standard(proto: "button_gpio"), 5: .standard(proto: "buzzer_gpio"), 6: .standard(proto: "rebroadcast_mode"), + 7: .standard(proto: "node_info_broadcast_secs"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1575,6 +1581,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl case 4: try { try decoder.decodeSingularUInt32Field(value: &self.buttonGpio) }() case 5: try { try decoder.decodeSingularUInt32Field(value: &self.buzzerGpio) }() case 6: try { try decoder.decodeSingularEnumField(value: &self.rebroadcastMode) }() + case 7: try { try decoder.decodeSingularUInt32Field(value: &self.nodeInfoBroadcastSecs) }() default: break } } @@ -1599,6 +1606,9 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl if self.rebroadcastMode != .all { try visitor.visitSingularEnumField(value: self.rebroadcastMode, fieldNumber: 6) } + if self.nodeInfoBroadcastSecs != 0 { + try visitor.visitSingularUInt32Field(value: self.nodeInfoBroadcastSecs, fieldNumber: 7) + } try unknownFields.traverse(visitor: &visitor) } @@ -1609,6 +1619,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl if lhs.buttonGpio != rhs.buttonGpio {return false} if lhs.buzzerGpio != rhs.buzzerGpio {return false} if lhs.rebroadcastMode != rhs.rebroadcastMode {return false} + if lhs.nodeInfoBroadcastSecs != rhs.nodeInfoBroadcastSecs {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/device_metadata.pb.swift b/Meshtastic/Protobufs/meshtastic/device_metadata.pb.swift deleted file mode 100644 index 4c930c43..00000000 --- a/Meshtastic/Protobufs/meshtastic/device_metadata.pb.swift +++ /dev/null @@ -1,157 +0,0 @@ -// DO NOT EDIT. -// swift-format-ignore-file -// -// Generated by the Swift generator plugin for the protocol buffer compiler. -// Source: meshtastic/device_metadata.proto -// -// For information on using the generated types, please see the documentation: -// https://github.com/apple/swift-protobuf/ - -import Foundation -import SwiftProtobuf - -// If the compiler emits an error on this type, it is because this file -// was generated by a version of the `protoc` Swift plug-in that is -// incompatible with the version of SwiftProtobuf to which you are linking. -// Please ensure that you are building against the same version of the API -// that was used to generate this file. -fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { - struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} - typealias Version = _2 -} - -/// -/// Device metadata response -struct DeviceMetadata { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - /// - /// Device firmware version string - var firmwareVersion: String = String() - - /// - /// Device state version - var deviceStateVersion: UInt32 = 0 - - /// - /// Indicates whether the device can shutdown CPU natively or via power management chip - var canShutdown: Bool = false - - /// - /// Indicates that the device has native wifi capability - var hasWifi_p: Bool = false - - /// - /// Indicates that the device has native bluetooth capability - var hasBluetooth_p: Bool = false - - /// - /// Indicates that the device has an ethernet peripheral - var hasEthernet_p: Bool = false - - /// - /// Indicates that the device's role in the mesh - var role: Config.DeviceConfig.Role = .client - - /// - /// Indicates the device's current enabled position flags - var positionFlags: UInt32 = 0 - - /// - /// Device hardware model - var hwModel: HardwareModel = .unset - - var unknownFields = SwiftProtobuf.UnknownStorage() - - init() {} -} - -#if swift(>=5.5) && canImport(_Concurrency) -extension DeviceMetadata: @unchecked Sendable {} -#endif // swift(>=5.5) && canImport(_Concurrency) - -// MARK: - Code below here is support for the SwiftProtobuf runtime. - -fileprivate let _protobuf_package = "meshtastic" - -extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = _protobuf_package + ".DeviceMetadata" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "firmware_version"), - 2: .standard(proto: "device_state_version"), - 3: .same(proto: "canShutdown"), - 4: .same(proto: "hasWifi"), - 5: .same(proto: "hasBluetooth"), - 6: .same(proto: "hasEthernet"), - 7: .same(proto: "role"), - 8: .standard(proto: "position_flags"), - 9: .standard(proto: "hw_model"), - ] - - mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.firmwareVersion) }() - case 2: try { try decoder.decodeSingularUInt32Field(value: &self.deviceStateVersion) }() - case 3: try { try decoder.decodeSingularBoolField(value: &self.canShutdown) }() - case 4: try { try decoder.decodeSingularBoolField(value: &self.hasWifi_p) }() - case 5: try { try decoder.decodeSingularBoolField(value: &self.hasBluetooth_p) }() - case 6: try { try decoder.decodeSingularBoolField(value: &self.hasEthernet_p) }() - case 7: try { try decoder.decodeSingularEnumField(value: &self.role) }() - case 8: try { try decoder.decodeSingularUInt32Field(value: &self.positionFlags) }() - case 9: try { try decoder.decodeSingularEnumField(value: &self.hwModel) }() - default: break - } - } - } - - func traverse(visitor: inout V) throws { - if !self.firmwareVersion.isEmpty { - try visitor.visitSingularStringField(value: self.firmwareVersion, fieldNumber: 1) - } - if self.deviceStateVersion != 0 { - try visitor.visitSingularUInt32Field(value: self.deviceStateVersion, fieldNumber: 2) - } - if self.canShutdown != false { - try visitor.visitSingularBoolField(value: self.canShutdown, fieldNumber: 3) - } - if self.hasWifi_p != false { - try visitor.visitSingularBoolField(value: self.hasWifi_p, fieldNumber: 4) - } - if self.hasBluetooth_p != false { - try visitor.visitSingularBoolField(value: self.hasBluetooth_p, fieldNumber: 5) - } - if self.hasEthernet_p != false { - try visitor.visitSingularBoolField(value: self.hasEthernet_p, fieldNumber: 6) - } - if self.role != .client { - try visitor.visitSingularEnumField(value: self.role, fieldNumber: 7) - } - if self.positionFlags != 0 { - try visitor.visitSingularUInt32Field(value: self.positionFlags, fieldNumber: 8) - } - if self.hwModel != .unset { - try visitor.visitSingularEnumField(value: self.hwModel, fieldNumber: 9) - } - try unknownFields.traverse(visitor: &visitor) - } - - static func ==(lhs: DeviceMetadata, rhs: DeviceMetadata) -> Bool { - if lhs.firmwareVersion != rhs.firmwareVersion {return false} - if lhs.deviceStateVersion != rhs.deviceStateVersion {return false} - if lhs.canShutdown != rhs.canShutdown {return false} - if lhs.hasWifi_p != rhs.hasWifi_p {return false} - if lhs.hasBluetooth_p != rhs.hasBluetooth_p {return false} - if lhs.hasEthernet_p != rhs.hasEthernet_p {return false} - if lhs.role != rhs.role {return false} - if lhs.positionFlags != rhs.positionFlags {return false} - if lhs.hwModel != rhs.hwModel {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} diff --git a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift index 0f474574..12b53077 100644 --- a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift @@ -158,6 +158,10 @@ enum HardwareModel: SwiftProtobuf.Enum { /// New BETAFPV ELRS Micro TX Module 2.4G with ESP32 CPU case betafpv2400Tx // = 45 + /// + /// BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU + case betafpv900NanoTx // = 46 + /// /// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. case privateHw // = 255 @@ -201,6 +205,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case 43: self = .heltecV3 case 44: self = .heltecWslV3 case 45: self = .betafpv2400Tx + case 46: self = .betafpv900NanoTx case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } @@ -240,6 +245,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case .heltecV3: return 43 case .heltecWslV3: return 44 case .betafpv2400Tx: return 45 + case .betafpv900NanoTx: return 46 case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } @@ -284,6 +290,7 @@ extension HardwareModel: CaseIterable { .heltecV3, .heltecWslV3, .betafpv2400Tx, + .betafpv900NanoTx, .privateHw, ] } @@ -1949,6 +1956,16 @@ struct FromRadio { set {_uniqueStorage()._payloadVariant = .xmodemPacket(newValue)} } + /// + /// Device metadata message + var metadata: DeviceMetadata { + get { + if case .metadata(let v)? = _storage._payloadVariant {return v} + return DeviceMetadata() + } + set {_uniqueStorage()._payloadVariant = .metadata(newValue)} + } + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -1995,6 +2012,9 @@ struct FromRadio { /// /// File Transfer Chunk case xmodemPacket(XModem) + /// + /// Device metadata message + case metadata(DeviceMetadata) #if !swift(>=4.1) static func ==(lhs: FromRadio.OneOf_PayloadVariant, rhs: FromRadio.OneOf_PayloadVariant) -> Bool { @@ -2046,6 +2066,10 @@ struct FromRadio { guard case .xmodemPacket(let l) = lhs, case .xmodemPacket(let r) = rhs else { preconditionFailure() } return l == r }() + case (.metadata, .metadata): return { + guard case .metadata(let l) = lhs, case .metadata(let r) = rhs else { preconditionFailure() } + return l == r + }() default: return false } } @@ -2192,6 +2216,54 @@ struct Compressed { init() {} } +/// +/// Device metadata response +struct DeviceMetadata { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Device firmware version string + var firmwareVersion: String = String() + + /// + /// Device state version + var deviceStateVersion: UInt32 = 0 + + /// + /// Indicates whether the device can shutdown CPU natively or via power management chip + var canShutdown: Bool = false + + /// + /// Indicates that the device has native wifi capability + var hasWifi_p: Bool = false + + /// + /// Indicates that the device has native bluetooth capability + var hasBluetooth_p: Bool = false + + /// + /// Indicates that the device has an ethernet peripheral + var hasEthernet_p: Bool = false + + /// + /// Indicates that the device's role in the mesh + var role: Config.DeviceConfig.Role = .client + + /// + /// Indicates the device's current enabled position flags + var positionFlags: UInt32 = 0 + + /// + /// Device hardware model + var hwModel: HardwareModel = .unset + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + #if swift(>=5.5) && canImport(_Concurrency) extension HardwareModel: @unchecked Sendable {} extension Constants: @unchecked Sendable {} @@ -2220,6 +2292,7 @@ extension FromRadio.OneOf_PayloadVariant: @unchecked Sendable {} extension ToRadio: @unchecked Sendable {} extension ToRadio.OneOf_PayloadVariant: @unchecked Sendable {} extension Compressed: @unchecked Sendable {} +extension DeviceMetadata: @unchecked Sendable {} #endif // swift(>=5.5) && canImport(_Concurrency) // MARK: - Code below here is support for the SwiftProtobuf runtime. @@ -2260,6 +2333,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 43: .same(proto: "HELTEC_V3"), 44: .same(proto: "HELTEC_WSL_V3"), 45: .same(proto: "BETAFPV_2400_TX"), + 46: .same(proto: "BETAFPV_900_NANO_TX"), 255: .same(proto: "PRIVATE_HW"), ] } @@ -3401,6 +3475,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation 10: .same(proto: "channel"), 11: .same(proto: "queueStatus"), 12: .same(proto: "xmodemPacket"), + 13: .same(proto: "metadata"), ] fileprivate class _StorageClass { @@ -3566,6 +3641,19 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation _storage._payloadVariant = .xmodemPacket(v) } }() + case 13: try { + var v: DeviceMetadata? + var hadOneofValue = false + if let current = _storage._payloadVariant { + hadOneofValue = true + if case .metadata(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._payloadVariant = .metadata(v) + } + }() default: break } } @@ -3626,6 +3714,10 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation guard case .xmodemPacket(let v)? = _storage._payloadVariant else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 12) }() + case .metadata?: try { + guard case .metadata(let v)? = _storage._payloadVariant else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 13) + }() case nil: break } } @@ -3781,3 +3873,83 @@ extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio return true } } + +extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".DeviceMetadata" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "firmware_version"), + 2: .standard(proto: "device_state_version"), + 3: .same(proto: "canShutdown"), + 4: .same(proto: "hasWifi"), + 5: .same(proto: "hasBluetooth"), + 6: .same(proto: "hasEthernet"), + 7: .same(proto: "role"), + 8: .standard(proto: "position_flags"), + 9: .standard(proto: "hw_model"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.firmwareVersion) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.deviceStateVersion) }() + case 3: try { try decoder.decodeSingularBoolField(value: &self.canShutdown) }() + case 4: try { try decoder.decodeSingularBoolField(value: &self.hasWifi_p) }() + case 5: try { try decoder.decodeSingularBoolField(value: &self.hasBluetooth_p) }() + case 6: try { try decoder.decodeSingularBoolField(value: &self.hasEthernet_p) }() + case 7: try { try decoder.decodeSingularEnumField(value: &self.role) }() + case 8: try { try decoder.decodeSingularUInt32Field(value: &self.positionFlags) }() + case 9: try { try decoder.decodeSingularEnumField(value: &self.hwModel) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.firmwareVersion.isEmpty { + try visitor.visitSingularStringField(value: self.firmwareVersion, fieldNumber: 1) + } + if self.deviceStateVersion != 0 { + try visitor.visitSingularUInt32Field(value: self.deviceStateVersion, fieldNumber: 2) + } + if self.canShutdown != false { + try visitor.visitSingularBoolField(value: self.canShutdown, fieldNumber: 3) + } + if self.hasWifi_p != false { + try visitor.visitSingularBoolField(value: self.hasWifi_p, fieldNumber: 4) + } + if self.hasBluetooth_p != false { + try visitor.visitSingularBoolField(value: self.hasBluetooth_p, fieldNumber: 5) + } + if self.hasEthernet_p != false { + try visitor.visitSingularBoolField(value: self.hasEthernet_p, fieldNumber: 6) + } + if self.role != .client { + try visitor.visitSingularEnumField(value: self.role, fieldNumber: 7) + } + if self.positionFlags != 0 { + try visitor.visitSingularUInt32Field(value: self.positionFlags, fieldNumber: 8) + } + if self.hwModel != .unset { + try visitor.visitSingularEnumField(value: self.hwModel, fieldNumber: 9) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: DeviceMetadata, rhs: DeviceMetadata) -> Bool { + if lhs.firmwareVersion != rhs.firmwareVersion {return false} + if lhs.deviceStateVersion != rhs.deviceStateVersion {return false} + if lhs.canShutdown != rhs.canShutdown {return false} + if lhs.hasWifi_p != rhs.hasWifi_p {return false} + if lhs.hasBluetooth_p != rhs.hasBluetooth_p {return false} + if lhs.hasEthernet_p != rhs.hasEthernet_p {return false} + if lhs.role != rhs.role {return false} + if lhs.positionFlags != rhs.positionFlags {return false} + if lhs.hwModel != rhs.hwModel {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 d9e29e1b..7280d277 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -21,6 +21,8 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var isUnsetRegion = false @State var invalidFirmwareVersion = false + @State var isPresentingPreferredPeripherialDialog = false + @State var showDialogForNextPeripherialChange = true var body: some View { @@ -61,23 +63,44 @@ struct Connect: View { Toggle("preferred.radio", isOn: $bleManager.preferredPeripheral) .toggleStyle(SwitchToggleStyle(tint: .accentColor)) .labelsHidden() + .confirmationDialog( + "Are you sure? Switching your preferred peripheral will clear all app data from the phone.", + isPresented: $isPresentingPreferredPeripherialDialog, + titleVisibility: .visible + ) { + Button("Confirm", role: .destructive) { + + if bleManager.preferredPeripheral { + if bleManager.connectedPeripheral != nil { + userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString + userSettings.preferredNodeNum = bleManager.connectedPeripheral!.num + bleManager.preferredPeripheral = true + isPreferredRadio = true + showDialogForNextPeripherialChange = false + } + } else { + + if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId { + userSettings.preferredPeripheralId = "" + userSettings.preferredNodeNum = 0 + bleManager.preferredPeripheral = false + isPreferredRadio = false + } + } + bleManager.disconnectPeripheral() + clearCoreDataDatabase(context: context) + } + Button("Cancel", role: .cancel) { + showDialogForNextPeripherialChange = false + bleManager.preferredPeripheral = !bleManager.preferredPeripheral + } + } .onChange(of: bleManager.preferredPeripheral) { value in - if value { - if bleManager.connectedPeripheral != nil { - userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString - userSettings.preferredNodeNum = bleManager.connectedPeripheral!.num - bleManager.preferredPeripheral = true - isPreferredRadio = true - } - } else { - - if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId { - - userSettings.preferredPeripheralId = "" - userSettings.preferredNodeNum = 0 - bleManager.preferredPeripheral = false - isPreferredRadio = false - } + + if showDialogForNextPeripherialChange { + isPresentingPreferredPeripherialDialog = true + } else { + showDialogForNextPeripherialChange = true } } } diff --git a/Meshtastic/Views/ContentView.swift b/Meshtastic/Views/ContentView.swift index a3f99164..534062c0 100644 --- a/Meshtastic/Views/ContentView.swift +++ b/Meshtastic/Views/ContentView.swift @@ -25,7 +25,7 @@ struct ContentView: View { TabView(selection: $selection) { - if userSettings.preferredNodeNum > 0 { + if userSettings.preferredPeripheralId.count > 0 { Contacts() .tabItem { diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 929536c9..7a887550 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -139,7 +139,7 @@ struct MapViewSwiftUI: UIViewRepresentable { annotationView.markerTintColor = UIColor(.indigo) annotationView.displayPriority = .defaultHigh annotationView.titleVisibility = .adaptive - annotationView.clusteringIdentifier = "nodeGroup" + //annotationView.clusteringIdentifier = "nodeGroup" } annotationView.titleVisibility = .adaptive diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 113633e9..a73b0254 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -351,12 +351,11 @@ struct NodeDetail: View { } } - if (self.bleManager.connectedPeripheral != nil && self.bleManager.connectedPeripheral.num == node.num) - || (self.bleManager.connectedPeripheral != nil && node.metadata != nil) { + if (self.bleManager.connectedPeripheral != nil && node.metadata != nil) { HStack { let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context) - if node.metadata?.canShutdown ?? false { + if node.metadata?.canShutdown ?? false || hwModelString == "RAK4631" {//node.metadata?.hwModel ?? "UNSET" == "RAK4631" { Button(action: { showingShutdownConfirm = true diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index b0802fb9..d8e2cecc 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -13,10 +13,6 @@ struct AppSettings: View { @State private var isPresentingCoreDataResetConfirm = false @State private var preferredDeviceConnected = false - var perferredPeripheral: String { - UserDefaults.standard.object(forKey: "preferredPeripheralName") as? String ?? "" - } - var body: some View { VStack { Form {