diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index d287f783..8a8b8ba3 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -193,6 +193,7 @@ /* Begin PBXFileReference section */ A65FA974296876BF00A97686 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = ""; }; + DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = ""; }; DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = ""; }; DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfigEnums.swift; sourceTree = ""; }; DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = ""; }; @@ -1306,7 +1307,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.11; + MARKETING_VERSION = 2.1.14; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1340,7 +1341,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.11; + MARKETING_VERSION = 2.1.14; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1587,6 +1588,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */, DDB75A1F2A10766D006ED576 /* MeshtasticDataModelV13.xcdatamodel */, DDB759E12A04B264006ED576 /* MeshtasticDataModelV12.xcdatamodel */, DDDEE5E229DBE43E00A8E078 /* MeshtasticDataModelV11.xcdatamodel */, @@ -1601,7 +1603,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DDB75A1F2A10766D006ED576 /* MeshtasticDataModelV13.xcdatamodel */; + currentVersion = DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 8bef3fcc..517456df 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -423,7 +423,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { if myInfo != nil { connectedPeripheral.num = myInfo!.myNodeNum - connectedPeripheral.firmwareVersion = myInfo?.firmwareVersion ?? "unknown".localized connectedPeripheral.name = myInfo?.bleName ?? "unknown".localized connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized } @@ -445,12 +444,12 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { } } // Channels - if decodedInfo.channel.isInitialized { + if decodedInfo.channel.isInitialized && connectedPeripheral != nil { nowKnown = true channelPacket(channel: decodedInfo.channel, fromNum: connectedPeripheral.num, context: context!) } // Config - if decodedInfo.config.isInitialized && !invalidVersion { + if decodedInfo.config.isInitialized && !invalidVersion && connectedPeripheral != nil { nowKnown = true localConfig(config: decodedInfo.config, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName) @@ -472,6 +471,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { if decodedInfo.metadata.firmwareVersion.count > 0 && !invalidVersion { nowKnown = true deviceMetadataPacket(metadata: decodedInfo.metadata, fromNum: connectedPeripheral.num, context: context!) + connectedPeripheral.firmwareVersion = decodedInfo.metadata.firmwareVersion ?? "unknown".localized } // Log any other unknownApp calls if !nowKnown { MeshLogger.log("🕸️ MESH PACKET received for Unknown App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") } diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index ba393038..d24e5bb0 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -89,17 +89,8 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO let myInfoEntity = MyInfoEntity(context: context) myInfoEntity.peripheralId = peripheralId myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum) - myInfoEntity.hasGps = myInfo.hasGps_p - myInfoEntity.hasWifi = myInfo.hasWifi_p - myInfoEntity.bitrate = myInfo.bitrate - // Swift does strings weird, this does work to get the version without the github hash - let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".") - var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))] - version = version.dropLast() - myInfoEntity.firmwareVersion = String(version) - myInfoEntity.messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) + myInfoEntity.rebootCount = Int32(myInfo.rebootCount) myInfoEntity.minAppVersion = Int32(bitPattern: myInfo.minAppVersion) - myInfoEntity.maxChannels = Int32(bitPattern: myInfo.maxChannels) do { try context.save() print("💾 Saved a new myInfo for node number: \(String(myInfo.myNodeNum))") @@ -113,15 +104,8 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO fetchedMyInfo[0].peripheralId = peripheralId fetchedMyInfo[0].myNodeNum = Int64(myInfo.myNodeNum) - fetchedMyInfo[0].hasGps = myInfo.hasGps_p - fetchedMyInfo[0].bitrate = myInfo.bitrate - let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")// .lastIndex(of: ".", offsetBy: -1) - var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))] - version = version.dropLast() - fetchedMyInfo[0].firmwareVersion = String(version) - fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec) + fetchedMyInfo[0].rebootCount = Int32(myInfo.rebootCount) fetchedMyInfo[0].minAppVersion = Int32(bitPattern: myInfo.minAppVersion) - fetchedMyInfo[0].maxChannels = Int32(bitPattern: myInfo.maxChannels) do { try context.save() @@ -214,6 +198,11 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS newMetadata.hasEthernet = metadata.hasEthernet_p newMetadata.role = Int32(metadata.role.rawValue) newMetadata.positionFlags = Int32(metadata.positionFlags) + // Swift does strings weird, this does work to get the version without the github hash + let lastDotIndex = metadata.firmwareVersion.lastIndex(of: ".") + var version = metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: metadata.firmwareVersion))] + version = version.dropLast() + newMetadata.firmwareVersion = String(version) fetchedNode[0].metadata = newMetadata do { @@ -272,7 +261,6 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje newUser.num = Int64(nodeInfo.num) newUser.longName = nodeInfo.user.longName newUser.shortName = nodeInfo.user.shortName - newUser.macaddr = nodeInfo.user.macaddr newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() newNode.user = newUser } @@ -329,7 +317,6 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].user!.num = Int64(nodeInfo.num) fetchedNode[0].user!.longName = nodeInfo.user.longName fetchedNode[0].user!.shortName = nodeInfo.user.shortName - fetchedNode[0].user!.macaddr = nodeInfo.user.macaddr fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased() } diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 17b97ce6..9235346e 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV13.xcdatamodel + MeshtasticDataModelV14.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV14.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV14.xcdatamodel/contents new file mode 100644 index 00000000..200820dd --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV14.xcdatamodel/contents @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index f28fb5d5..2430d577 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -129,7 +129,6 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) newUser.num = Int64(packet.from) newUser.longName = newUserMessage.longName newUser.shortName = newUserMessage.shortName - newUser.macaddr = newUserMessage.macaddr newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased() newNode.user = newUser } @@ -159,7 +158,6 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) fetchedNode[0].user!.num = Int64(nodeInfoMessage.num) fetchedNode[0].user!.longName = nodeInfoMessage.user.longName fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName - fetchedNode[0].user!.macaddr = nodeInfoMessage.user.macaddr fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased() } } diff --git a/Meshtastic/Protobufs/meshtastic/config.pb.swift b/Meshtastic/Protobufs/meshtastic/config.pb.swift index 32949e89..b8d6d267 100644 --- a/Meshtastic/Protobufs/meshtastic/config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/config.pb.swift @@ -533,6 +533,10 @@ struct Config { /// 0 for default of 10 seconds var minWakeSecs: UInt32 = 0 + /// + /// I2C address of INA_2XX to use for reading device battery voltage + var deviceBatteryInaAddress: UInt32 = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1805,6 +1809,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple 6: .standard(proto: "sds_secs"), 7: .standard(proto: "ls_secs"), 8: .standard(proto: "min_wake_secs"), + 9: .standard(proto: "device_battery_ina_address"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1821,6 +1826,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple case 6: try { try decoder.decodeSingularUInt32Field(value: &self.sdsSecs) }() case 7: try { try decoder.decodeSingularUInt32Field(value: &self.lsSecs) }() case 8: try { try decoder.decodeSingularUInt32Field(value: &self.minWakeSecs) }() + case 9: try { try decoder.decodeSingularUInt32Field(value: &self.deviceBatteryInaAddress) }() default: break } } @@ -1851,6 +1857,9 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.minWakeSecs != 0 { try visitor.visitSingularUInt32Field(value: self.minWakeSecs, fieldNumber: 8) } + if self.deviceBatteryInaAddress != 0 { + try visitor.visitSingularUInt32Field(value: self.deviceBatteryInaAddress, fieldNumber: 9) + } try unknownFields.traverse(visitor: &visitor) } @@ -1863,6 +1872,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if lhs.sdsSecs != rhs.sdsSecs {return false} if lhs.lsSecs != rhs.lsSecs {return false} if lhs.minWakeSecs != rhs.minWakeSecs {return false} + if lhs.deviceBatteryInaAddress != rhs.deviceBatteryInaAddress {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift index 6a9448c3..5d931be9 100644 --- a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift @@ -109,7 +109,8 @@ struct DeviceState { mutating func clearOwner() {_uniqueStorage()._owner = nil} /// - /// TODO: REPLACE + /// Deprecated in 2.1.x + /// Old node_db. See NodeInfoLite node_db_lite var nodeDb: [NodeInfo] { get {return _storage._nodeDb} set {_uniqueStorage()._nodeDb = newValue} @@ -179,6 +180,13 @@ struct DeviceState { set {_uniqueStorage()._nodeRemoteHardwarePins = newValue} } + /// + /// New lite version of NodeDB to decrease + var nodeDbLite: [NodeInfoLite] { + get {return _storage._nodeDbLite} + set {_uniqueStorage()._nodeDbLite = newValue} + } + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -186,6 +194,118 @@ struct DeviceState { fileprivate var _storage = _StorageClass.defaultInstance } +struct NodeInfoLite { + // 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. + + /// + /// The node number + var num: UInt32 { + get {return _storage._num} + set {_uniqueStorage()._num = newValue} + } + + /// + /// The user info for this node + var user: User { + get {return _storage._user ?? User()} + set {_uniqueStorage()._user = newValue} + } + /// Returns true if `user` has been explicitly set. + var hasUser: Bool {return _storage._user != nil} + /// Clears the value of `user`. Subsequent reads from it will return its default value. + mutating func clearUser() {_uniqueStorage()._user = nil} + + /// + /// This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. + /// Position.time now indicates the last time we received a POSITION from that node. + var position: PositionLite { + get {return _storage._position ?? PositionLite()} + set {_uniqueStorage()._position = newValue} + } + /// Returns true if `position` has been explicitly set. + var hasPosition: Bool {return _storage._position != nil} + /// Clears the value of `position`. Subsequent reads from it will return its default value. + mutating func clearPosition() {_uniqueStorage()._position = nil} + + /// + /// Returns the Signal-to-noise ratio (SNR) of the last received message, + /// as measured by the receiver. Return SNR of the last received message in dB + var snr: Float { + get {return _storage._snr} + set {_uniqueStorage()._snr = newValue} + } + + /// + /// Set to indicate the last time we received a packet from this node + var lastHeard: UInt32 { + get {return _storage._lastHeard} + set {_uniqueStorage()._lastHeard = newValue} + } + + /// + /// The latest device metrics for the node. + var deviceMetrics: DeviceMetrics { + get {return _storage._deviceMetrics ?? DeviceMetrics()} + set {_uniqueStorage()._deviceMetrics = newValue} + } + /// Returns true if `deviceMetrics` has been explicitly set. + var hasDeviceMetrics: Bool {return _storage._deviceMetrics != nil} + /// Clears the value of `deviceMetrics`. Subsequent reads from it will return its default value. + mutating func clearDeviceMetrics() {_uniqueStorage()._deviceMetrics = nil} + + /// + /// local channel index we heard that node on. Only populated if its not the default channel. + var channel: UInt32 { + get {return _storage._channel} + set {_uniqueStorage()._channel = newValue} + } + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _storage = _StorageClass.defaultInstance +} + +/// +/// Position with static location information only for NodeDBLite +struct PositionLite { + // 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. + + /// + /// The new preferred location encoding, multiply by 1e-7 to get degrees + /// in floating point + var latitudeI: Int32 = 0 + + /// + /// TODO: REPLACE + var longitudeI: Int32 = 0 + + /// + /// In meters above MSL (but see issue #359) + var altitude: Int32 = 0 + + /// + /// This is usually not sent over the mesh (to save space), but it is sent + /// from the phone so that the local device can set its RTC If it is sent over + /// the mesh (because there are devices on the mesh without GPS), it will only + /// be sent by devices which has a hardware GPS clock. + /// seconds since 1970 + var time: UInt32 = 0 + + /// + /// TODO: REPLACE + var locationSource: Position.LocSource = .locUnset + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + /// /// The on-disk saved channels struct ChannelFile { @@ -302,6 +422,8 @@ struct NodeRemoteHardwarePin { #if swift(>=5.5) && canImport(_Concurrency) extension ScreenFonts: @unchecked Sendable {} extension DeviceState: @unchecked Sendable {} +extension NodeInfoLite: @unchecked Sendable {} +extension PositionLite: @unchecked Sendable {} extension ChannelFile: @unchecked Sendable {} extension OEMStore: @unchecked Sendable {} extension NodeRemoteHardwarePin: @unchecked Sendable {} @@ -332,6 +454,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 11: .standard(proto: "did_gps_reset"), 12: .standard(proto: "rx_waypoint"), 13: .standard(proto: "node_remote_hardware_pins"), + 14: .standard(proto: "node_db_lite"), ] fileprivate class _StorageClass { @@ -345,6 +468,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _didGpsReset: Bool = false var _rxWaypoint: MeshPacket? = nil var _nodeRemoteHardwarePins: [NodeRemoteHardwarePin] = [] + var _nodeDbLite: [NodeInfoLite] = [] static let defaultInstance = _StorageClass() @@ -361,6 +485,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati _didGpsReset = source._didGpsReset _rxWaypoint = source._rxWaypoint _nodeRemoteHardwarePins = source._nodeRemoteHardwarePins + _nodeDbLite = source._nodeDbLite } } @@ -389,6 +514,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }() case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }() case 13: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeRemoteHardwarePins) }() + case 14: try { try decoder.decodeRepeatedMessageField(value: &_storage._nodeDbLite) }() default: break } } @@ -431,6 +557,9 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if !_storage._nodeRemoteHardwarePins.isEmpty { try visitor.visitRepeatedMessageField(value: _storage._nodeRemoteHardwarePins, fieldNumber: 13) } + if !_storage._nodeDbLite.isEmpty { + try visitor.visitRepeatedMessageField(value: _storage._nodeDbLite, fieldNumber: 14) + } } try unknownFields.traverse(visitor: &visitor) } @@ -450,6 +579,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if _storage._didGpsReset != rhs_storage._didGpsReset {return false} if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false} if _storage._nodeRemoteHardwarePins != rhs_storage._nodeRemoteHardwarePins {return false} + if _storage._nodeDbLite != rhs_storage._nodeDbLite {return false} return true } if !storagesAreEqual {return false} @@ -459,6 +589,178 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati } } +extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".NodeInfoLite" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "num"), + 2: .same(proto: "user"), + 3: .same(proto: "position"), + 4: .same(proto: "snr"), + 5: .standard(proto: "last_heard"), + 6: .standard(proto: "device_metrics"), + 7: .same(proto: "channel"), + ] + + fileprivate class _StorageClass { + var _num: UInt32 = 0 + var _user: User? = nil + var _position: PositionLite? = nil + var _snr: Float = 0 + var _lastHeard: UInt32 = 0 + var _deviceMetrics: DeviceMetrics? = nil + var _channel: UInt32 = 0 + + static let defaultInstance = _StorageClass() + + private init() {} + + init(copying source: _StorageClass) { + _num = source._num + _user = source._user + _position = source._position + _snr = source._snr + _lastHeard = source._lastHeard + _deviceMetrics = source._deviceMetrics + _channel = source._channel + } + } + + fileprivate mutating func _uniqueStorage() -> _StorageClass { + if !isKnownUniquelyReferenced(&_storage) { + _storage = _StorageClass(copying: _storage) + } + return _storage + } + + mutating func decodeMessage(decoder: inout D) throws { + _ = _uniqueStorage() + try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + 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.decodeSingularUInt32Field(value: &_storage._num) }() + case 2: try { try decoder.decodeSingularMessageField(value: &_storage._user) }() + case 3: try { try decoder.decodeSingularMessageField(value: &_storage._position) }() + case 4: try { try decoder.decodeSingularFloatField(value: &_storage._snr) }() + case 5: try { try decoder.decodeSingularFixed32Field(value: &_storage._lastHeard) }() + case 6: try { try decoder.decodeSingularMessageField(value: &_storage._deviceMetrics) }() + case 7: try { try decoder.decodeSingularUInt32Field(value: &_storage._channel) }() + default: break + } + } + } + } + + func traverse(visitor: inout V) throws { + try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if _storage._num != 0 { + try visitor.visitSingularUInt32Field(value: _storage._num, fieldNumber: 1) + } + try { if let v = _storage._user { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try { if let v = _storage._position { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() + if _storage._snr != 0 { + try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4) + } + if _storage._lastHeard != 0 { + try visitor.visitSingularFixed32Field(value: _storage._lastHeard, fieldNumber: 5) + } + try { if let v = _storage._deviceMetrics { + try visitor.visitSingularMessageField(value: v, fieldNumber: 6) + } }() + if _storage._channel != 0 { + try visitor.visitSingularUInt32Field(value: _storage._channel, fieldNumber: 7) + } + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: NodeInfoLite, rhs: NodeInfoLite) -> Bool { + if lhs._storage !== rhs._storage { + let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in + let _storage = _args.0 + let rhs_storage = _args.1 + if _storage._num != rhs_storage._num {return false} + if _storage._user != rhs_storage._user {return false} + if _storage._position != rhs_storage._position {return false} + if _storage._snr != rhs_storage._snr {return false} + if _storage._lastHeard != rhs_storage._lastHeard {return false} + if _storage._deviceMetrics != rhs_storage._deviceMetrics {return false} + if _storage._channel != rhs_storage._channel {return false} + return true + } + if !storagesAreEqual {return false} + } + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension PositionLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".PositionLite" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "latitude_i"), + 2: .standard(proto: "longitude_i"), + 3: .same(proto: "altitude"), + 4: .same(proto: "time"), + 5: .standard(proto: "location_source"), + ] + + 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.decodeSingularSFixed32Field(value: &self.latitudeI) }() + case 2: try { try decoder.decodeSingularSFixed32Field(value: &self.longitudeI) }() + case 3: try { try decoder.decodeSingularInt32Field(value: &self.altitude) }() + case 4: try { try decoder.decodeSingularFixed32Field(value: &self.time) }() + case 5: try { try decoder.decodeSingularEnumField(value: &self.locationSource) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.latitudeI != 0 { + try visitor.visitSingularSFixed32Field(value: self.latitudeI, fieldNumber: 1) + } + if self.longitudeI != 0 { + try visitor.visitSingularSFixed32Field(value: self.longitudeI, fieldNumber: 2) + } + if self.altitude != 0 { + try visitor.visitSingularInt32Field(value: self.altitude, fieldNumber: 3) + } + if self.time != 0 { + try visitor.visitSingularFixed32Field(value: self.time, fieldNumber: 4) + } + if self.locationSource != .locUnset { + try visitor.visitSingularEnumField(value: self.locationSource, fieldNumber: 5) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: PositionLite, rhs: PositionLite) -> Bool { + if lhs.latitudeI != rhs.latitudeI {return false} + if lhs.longitudeI != rhs.longitudeI {return false} + if lhs.altitude != rhs.altitude {return false} + if lhs.time != rhs.time {return false} + if lhs.locationSource != rhs.locationSource {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".ChannelFile" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ diff --git a/Meshtastic/Protobufs/meshtastic/localonly.pb.swift b/Meshtastic/Protobufs/meshtastic/localonly.pb.swift index 7f16ac02..219e3728 100644 --- a/Meshtastic/Protobufs/meshtastic/localonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/localonly.pb.swift @@ -222,6 +222,17 @@ struct LocalModuleConfig { /// Clears the value of `remoteHardware`. Subsequent reads from it will return its default value. mutating func clearRemoteHardware() {_uniqueStorage()._remoteHardware = nil} + /// + /// The part of the config that is specific to the Neighbor Info module + var neighborInfo: ModuleConfig.NeighborInfoConfig { + get {return _storage._neighborInfo ?? ModuleConfig.NeighborInfoConfig()} + set {_uniqueStorage()._neighborInfo = newValue} + } + /// Returns true if `neighborInfo` has been explicitly set. + var hasNeighborInfo: Bool {return _storage._neighborInfo != nil} + /// Clears the value of `neighborInfo`. Subsequent reads from it will return its default value. + mutating func clearNeighborInfo() {_uniqueStorage()._neighborInfo = nil} + /// /// A version integer used to invalidate old save files when we make /// incompatible changes This integer is set at build time and is private to @@ -383,6 +394,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem 7: .standard(proto: "canned_message"), 9: .same(proto: "audio"), 10: .standard(proto: "remote_hardware"), + 11: .standard(proto: "neighbor_info"), 8: .same(proto: "version"), ] @@ -396,6 +408,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem var _cannedMessage: ModuleConfig.CannedMessageConfig? = nil var _audio: ModuleConfig.AudioConfig? = nil var _remoteHardware: ModuleConfig.RemoteHardwareConfig? = nil + var _neighborInfo: ModuleConfig.NeighborInfoConfig? = nil var _version: UInt32 = 0 static let defaultInstance = _StorageClass() @@ -412,6 +425,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem _cannedMessage = source._cannedMessage _audio = source._audio _remoteHardware = source._remoteHardware + _neighborInfo = source._neighborInfo _version = source._version } } @@ -441,6 +455,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }() case 9: try { try decoder.decodeSingularMessageField(value: &_storage._audio) }() case 10: try { try decoder.decodeSingularMessageField(value: &_storage._remoteHardware) }() + case 11: try { try decoder.decodeSingularMessageField(value: &_storage._neighborInfo) }() default: break } } @@ -483,6 +498,9 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem try { if let v = _storage._remoteHardware { try visitor.visitSingularMessageField(value: v, fieldNumber: 10) } }() + try { if let v = _storage._neighborInfo { + try visitor.visitSingularMessageField(value: v, fieldNumber: 11) + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -501,6 +519,7 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if _storage._cannedMessage != rhs_storage._cannedMessage {return false} if _storage._audio != rhs_storage._audio {return false} if _storage._remoteHardware != rhs_storage._remoteHardware {return false} + if _storage._neighborInfo != rhs_storage._neighborInfo {return false} if _storage._version != rhs_storage._version {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift index 841396e0..e68d65a1 100644 --- a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift @@ -106,6 +106,10 @@ enum HardwareModel: SwiftProtobuf.Enum { /// B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station case stationG1 // = 25 + /// + /// RAK11310 (RP2040 + SX1262) + case rak11310 // = 26 + /// /// --------------------------------------------------------------------------- /// Less common/prototype boards listed here (needs one more byte over the air) @@ -168,6 +172,10 @@ enum HardwareModel: SwiftProtobuf.Enum { /// BetaFPV ExpressLRS "Nano" TX Module 900MHz with ESP32 CPU case betafpv900NanoTx // = 46 + /// + /// Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module + case rpiPico // = 47 + /// /// ------------------------------------------------------------------------------------------------------------------------------------------ /// 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. @@ -200,6 +208,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case 16: self = .tloraT3S3 case 17: self = .nanoG1Explorer case 25: self = .stationG1 + case 26: self = .rak11310 case 32: self = .loraRelayV1 case 33: self = .nrf52840Dk case 34: self = .ppr @@ -215,6 +224,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case 44: self = .heltecWslV3 case 45: self = .betafpv2400Tx case 46: self = .betafpv900NanoTx + case 47: self = .rpiPico case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } @@ -241,6 +251,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case .tloraT3S3: return 16 case .nanoG1Explorer: return 17 case .stationG1: return 25 + case .rak11310: return 26 case .loraRelayV1: return 32 case .nrf52840Dk: return 33 case .ppr: return 34 @@ -256,6 +267,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case .heltecWslV3: return 44 case .betafpv2400Tx: return 45 case .betafpv900NanoTx: return 46 + case .rpiPico: return 47 case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } @@ -287,6 +299,7 @@ extension HardwareModel: CaseIterable { .tloraT3S3, .nanoG1Explorer, .stationG1, + .rak11310, .loraRelayV1, .nrf52840Dk, .ppr, @@ -302,6 +315,7 @@ extension HardwareModel: CaseIterable { .heltecWslV3, .betafpv2400Tx, .betafpv900NanoTx, + .rpiPico, .privateHw, ] } @@ -841,6 +855,7 @@ struct User { var shortName: String = String() /// + /// Deprecated in Meshtastic 2.1.x /// This is the addr of the radio. /// Not populated by the phone, but added by the esp32 when broadcasting var macaddr: Data = Data() @@ -1615,15 +1630,18 @@ struct MyNodeInfo { var myNodeNum: UInt32 = 0 /// + /// Deprecated in 2.1.x (Source from device_metadata) /// Note: This flag merely means we detected a hardware GPS in our node. /// Not the same as UserPreferences.location_sharing var hasGps_p: Bool = false /// + /// Deprecated in 2.1.x /// The maximum number of 'software' channels that can be set on this node. var maxChannels: UInt32 = 0 /// + /// Deprecated in 2.1.x (Source from device_metadata) /// 0.0.5 etc... var firmwareVersion: String = String() @@ -1651,10 +1669,12 @@ struct MyNodeInfo { var rebootCount: UInt32 = 0 /// + /// Deprecated in 2.1.x /// Calculated bitrate of the current channel (in Bytes Per Second) var bitrate: Float = 0 /// + /// Deprecated in 2.1.x /// How long before we consider a message abandoned and we can clear our /// caches of any messages in flight Normally quite large to handle the worst case /// message delivery time, 5 minutes. @@ -1667,22 +1687,27 @@ struct MyNodeInfo { var minAppVersion: UInt32 = 0 /// + /// Deprecated in 2.1.x (Only used on device to keep track of utilization) /// 24 time windows of 1hr each with the airtime transmitted out of the device per hour. var airPeriodTx: [UInt32] = [] /// + /// Deprecated in 2.1.x (Only used on device to keep track of utilization) /// 24 time windows of 1hr each with the airtime of valid packets for your mesh. var airPeriodRx: [UInt32] = [] /// + /// Deprecated in 2.1.x (Source from DeviceMetadata instead) /// Is the device wifi capable? var hasWifi_p: Bool = false /// + /// Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads) /// Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). var channelUtilization: Float = 0 /// + /// Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads) /// Percent of airtime for transmission used within the last hour. var airUtilTx: Float = 0 @@ -2385,6 +2410,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 16: .same(proto: "TLORA_T3_S3"), 17: .same(proto: "NANO_G1_EXPLORER"), 25: .same(proto: "STATION_G1"), + 26: .same(proto: "RAK11310"), 32: .same(proto: "LORA_RELAY_V1"), 33: .same(proto: "NRF52840DK"), 34: .same(proto: "PPR"), @@ -2400,6 +2426,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 44: .same(proto: "HELTEC_WSL_V3"), 45: .same(proto: "BETAFPV_2400_TX"), 46: .same(proto: "BETAFPV_900_NANO_TX"), + 47: .same(proto: "RPI_PICO"), 255: .same(proto: "PRIVATE_HW"), ] } diff --git a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift index 5df77866..58c173fa 100644 --- a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift @@ -174,6 +174,16 @@ struct ModuleConfig { set {payloadVariant = .remoteHardware(newValue)} } + /// + /// TODO: REPLACE + var neighborInfo: ModuleConfig.NeighborInfoConfig { + get { + if case .neighborInfo(let v)? = payloadVariant {return v} + return ModuleConfig.NeighborInfoConfig() + } + set {payloadVariant = .neighborInfo(newValue)} + } + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -206,6 +216,9 @@ struct ModuleConfig { /// /// TODO: REPLACE case remoteHardware(ModuleConfig.RemoteHardwareConfig) + /// + /// TODO: REPLACE + case neighborInfo(ModuleConfig.NeighborInfoConfig) #if !swift(>=4.1) static func ==(lhs: ModuleConfig.OneOf_PayloadVariant, rhs: ModuleConfig.OneOf_PayloadVariant) -> Bool { @@ -249,6 +262,10 @@ struct ModuleConfig { guard case .remoteHardware(let l) = lhs, case .remoteHardware(let r) = rhs else { preconditionFailure() } return l == r }() + case (.neighborInfo, .neighborInfo): return { + guard case .neighborInfo(let l) = lhs, case .neighborInfo(let r) = rhs else { preconditionFailure() } + return l == r + }() default: return false } } @@ -333,6 +350,27 @@ struct ModuleConfig { init() {} } + /// + /// NeighborInfoModule Config + struct NeighborInfoConfig { + // 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. + + /// + /// Whether the Module is enabled + var enabled: Bool = false + + /// + /// Interval in seconds of how often we should try to send our + /// Neighbor Info to the mesh + var updateInterval: UInt32 = 0 + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + } + /// /// Audio Config for codec2 voice struct AudioConfig { @@ -433,7 +471,6 @@ struct ModuleConfig { /// /// Preferences for the SerialModule - /// FIXME - Move this out of UserPreferences and into a section for module configuration. var enabled: Bool = false /// @@ -441,15 +478,15 @@ struct ModuleConfig { var echo: Bool = false /// - /// TODO: REPLACE + /// RX pin (should match Arduino gpio pin number) var rxd: UInt32 = 0 /// - /// TODO: REPLACE + /// TX pin (should match Arduino gpio pin number) var txd: UInt32 = 0 /// - /// TODO: REPLACE + /// Serial baud rate var baud: ModuleConfig.SerialConfig.Serial_Baud = .baudDefault /// @@ -457,9 +494,15 @@ struct ModuleConfig { var timeout: UInt32 = 0 /// - /// TODO: REPLACE + /// Mode for serial module operation var mode: ModuleConfig.SerialConfig.Serial_Mode = .default + /// + /// Overrides the platform's defacto Serial port instance to use with Serial module config settings + /// This is currently only usable in output modes like NMEA / CalTopo and may behave strangely or not work at all in other modes + /// Existing logging over the Serial Console will still be present + var overrideConsoleSerialPort: Bool = false + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -543,6 +586,9 @@ struct ModuleConfig { case proto // = 2 case textmsg // = 3 case nmea // = 4 + + /// NMEA messages specifically tailored for CalTopo + case caltopo // = 5 case UNRECOGNIZED(Int) init() { @@ -556,6 +602,7 @@ struct ModuleConfig { case 2: self = .proto case 3: self = .textmsg case 4: self = .nmea + case 5: self = .caltopo default: self = .UNRECOGNIZED(rawValue) } } @@ -567,6 +614,7 @@ struct ModuleConfig { case .proto: return 2 case .textmsg: return 3 case .nmea: return 4 + case .caltopo: return 5 case .UNRECOGNIZED(let i): return i } } @@ -933,6 +981,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable { .proto, .textmsg, .nmea, + .caltopo, ] } @@ -982,6 +1031,7 @@ extension ModuleConfig: @unchecked Sendable {} extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {} extension ModuleConfig.MQTTConfig: @unchecked Sendable {} extension ModuleConfig.RemoteHardwareConfig: @unchecked Sendable {} +extension ModuleConfig.NeighborInfoConfig: @unchecked Sendable {} extension ModuleConfig.AudioConfig: @unchecked Sendable {} extension ModuleConfig.AudioConfig.Audio_Baud: @unchecked Sendable {} extension ModuleConfig.SerialConfig: @unchecked Sendable {} @@ -1020,6 +1070,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 7: .standard(proto: "canned_message"), 8: .same(proto: "audio"), 9: .standard(proto: "remote_hardware"), + 10: .standard(proto: "neighbor_info"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1145,6 +1196,19 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat self.payloadVariant = .remoteHardware(v) } }() + case 10: try { + var v: ModuleConfig.NeighborInfoConfig? + var hadOneofValue = false + if let current = self.payloadVariant { + hadOneofValue = true + if case .neighborInfo(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.payloadVariant = .neighborInfo(v) + } + }() default: break } } @@ -1192,6 +1256,10 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .remoteHardware(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 9) }() + case .neighborInfo?: try { + guard case .neighborInfo(let v)? = self.payloadVariant else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 10) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) @@ -1322,6 +1390,44 @@ extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobu } } +extension ModuleConfig.NeighborInfoConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = ModuleConfig.protoMessageName + ".NeighborInfoConfig" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "enabled"), + 2: .standard(proto: "update_interval"), + ] + + 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.decodeSingularBoolField(value: &self.enabled) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.updateInterval) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.enabled != false { + try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1) + } + if self.updateInterval != 0 { + try visitor.visitSingularUInt32Field(value: self.updateInterval, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: ModuleConfig.NeighborInfoConfig, rhs: ModuleConfig.NeighborInfoConfig) -> Bool { + if lhs.enabled != rhs.enabled {return false} + if lhs.updateInterval != rhs.updateInterval {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension ModuleConfig.AudioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = ModuleConfig.protoMessageName + ".AudioConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -1414,6 +1520,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa 5: .same(proto: "baud"), 6: .same(proto: "timeout"), 7: .same(proto: "mode"), + 8: .standard(proto: "override_console_serial_port"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1429,6 +1536,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa case 5: try { try decoder.decodeSingularEnumField(value: &self.baud) }() case 6: try { try decoder.decodeSingularUInt32Field(value: &self.timeout) }() case 7: try { try decoder.decodeSingularEnumField(value: &self.mode) }() + case 8: try { try decoder.decodeSingularBoolField(value: &self.overrideConsoleSerialPort) }() default: break } } @@ -1456,6 +1564,9 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa if self.mode != .default { try visitor.visitSingularEnumField(value: self.mode, fieldNumber: 7) } + if self.overrideConsoleSerialPort != false { + try visitor.visitSingularBoolField(value: self.overrideConsoleSerialPort, fieldNumber: 8) + } try unknownFields.traverse(visitor: &visitor) } @@ -1467,6 +1578,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa if lhs.baud != rhs.baud {return false} if lhs.timeout != rhs.timeout {return false} if lhs.mode != rhs.mode {return false} + if lhs.overrideConsoleSerialPort != rhs.overrideConsoleSerialPort {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -1500,6 +1612,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProvidi 2: .same(proto: "PROTO"), 3: .same(proto: "TEXTMSG"), 4: .same(proto: "NMEA"), + 5: .same(proto: "CALTOPO"), ] } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 70fb6101..fd11b1c4 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -60,7 +60,7 @@ struct Connect: View { Text("ble.name").font(.callout)+Text(": \(bleManager.connectedPeripheral.peripheral.name ?? "unknown".localized)") .font(.callout).foregroundColor(Color.gray) if node != nil { - Text("firmware.version").font(.callout)+Text(": \(node?.myInfo?.firmwareVersion ?? "unknown".localized)") + Text("firmware.version").font(.callout)+Text(": \(node?.metadata?.firmwareVersion ?? "unknown".localized)") .font(.callout).foregroundColor(Color.gray) } if bleManager.isSubscribed { @@ -110,8 +110,6 @@ struct Connect: View { Text("Num: \(String(node!.num))") Text("Short Name: \(node?.user?.shortName ?? "????")") Text("Long Name: \(node?.user?.longName ?? "unknown".localized)") - Text("Max Channels: \(String(node?.myInfo?.maxChannels ?? 0))") - Text("Bitrate: \(String(format: "%.2f", node?.myInfo?.bitrate ?? 0.00))") Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)") } } diff --git a/Meshtastic/Views/Helpers/Node/NodeInfoView.swift b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift index 701adb38..528aa263 100644 --- a/Meshtastic/Views/Helpers/Node/NodeInfoView.swift +++ b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift @@ -35,7 +35,7 @@ struct NodeInfoView: View { if node.user != nil { Image(hwModelString) .resizable() - .aspectRatio(contentMode: .fill) + .aspectRatio(contentMode: .fit) .frame(width: 100, height: 100) .cornerRadius(5) @@ -104,20 +104,6 @@ struct NodeInfoView: View { Text(String(node.num)).font(.title).foregroundColor(.gray) } Divider() - VStack { - HStack { - Image(systemName: "globe") - .font(.title) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("MAC Address: ").font(.title) - - } - Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address")) - .font(.title) - .foregroundColor(.gray) - } - Divider() VStack { HStack { Image(systemName: "clock.badge.checkmark.fill") @@ -147,6 +133,7 @@ struct NodeInfoView: View { VStack { Image(node.user!.hwModel ?? "unset".localized) .resizable() + .aspectRatio(contentMode: .fit) .frame(width: 75, height: 75) .cornerRadius(5) Text(String(node.user!.hwModel ?? "unset".localized)) @@ -208,16 +195,6 @@ struct NodeInfoView: View { } } Divider() - HStack { - Image(systemName: "globe") - .font(.headline) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("MAC Address: ") - Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address")).foregroundColor(.gray) - } - .padding([.bottom], 10) - Divider() } VStack { diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index edf84dfc..2ad25e52 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -182,7 +182,7 @@ struct UserMessageList: View { if message.realACK { Text("\(ackErrorVal?.display ?? "Empty Ack Error")").font(.caption2).foregroundColor(.gray) } else { - Text("Implicit ACK from Unknown Node").font(.caption2).foregroundColor(.orange) + Text("Implicit ACK from another node").font(.caption2).foregroundColor(.orange) } } else if currentUser && message.ackError == 0 { // Empty Error diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 2856554d..0e5f13fe 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -27,7 +27,10 @@ struct EnvironmentMetricsLog: View { let chartData = environmentMetrics .filter { $0.time != nil && $0.time! >= oneWeekAgo! } .sorted { $0.time! < $1.time! } - + let locale = NSLocale.current as NSLocale + let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey")) + var format: UnitTemperature = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? .fahrenheit : .celsius + NavigationStack { if chartData.count > 0 { @@ -69,7 +72,7 @@ struct EnvironmentMetricsLog: View { .chartXAxis(content: { AxisMarks(position: .top) }) - .chartYScale(domain: 0...125) + .chartYScale(domain: format == .celsius ? -20...55 : 0...125) .chartForegroundStyleScale([ "Temperature" : .clear ]) diff --git a/Meshtastic/Views/Settings/AdminMessageList.swift b/Meshtastic/Views/Settings/AdminMessageList.swift index 585e1e60..ea9a8951 100644 --- a/Meshtastic/Views/Settings/AdminMessageList.swift +++ b/Meshtastic/Views/Settings/AdminMessageList.swift @@ -23,6 +23,7 @@ struct AdminMessageList: View { var body: some View { let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmssa", options: 0, locale: Locale.current) let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mm:ss a") + List { if user != nil { @@ -47,7 +48,7 @@ struct AdminMessageList: View { .foregroundColor(am.receivedACK ? .gray : .red) .font(.caption2) } else { - Text("Implicit ACK from Unknown Node") + Text("Implicit ACK from another node") .foregroundColor(.orange) .font(.caption2) } diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 3fa24d14..e0a6b5cd 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -91,7 +91,7 @@ struct Settings: View { let connectedNode = nodes.first(where: { $0.num == connectedNodeNum }) connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? 0 : 0) - if connectedNode != nil && node?.metadata == nil { + if connectedNode != nil && connectedNode?.user != nil && connectedNode?.myInfo != nil && node?.user != nil && node?.metadata == nil { let adminMessageId = bleManager.requestDeviceMetadata(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode!.myInfo!.adminIndex, context: context) if adminMessageId > 0 {