diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index f76ee93e..14494d8a 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ DD47E3DB26F3901B00029299 /* Channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DA26F3901A00029299 /* Channels.swift */; }; DD47E3DD26F390A000029299 /* Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DC26F390A000029299 /* Messages.swift */; }; DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4A911D2708C65400501B7E /* AppSettings.swift */; }; + DD4DED9027AD2975004BA27E /* cannedmessages.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */; }; DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; }; DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; }; DD539502276DAA6A00AD86B1 /* MapLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD539501276DAA6A00AD86B1 /* MapLocation.swift */; }; @@ -91,6 +92,7 @@ DD47E3DA26F3901A00029299 /* Channels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channels.swift; sourceTree = ""; }; DD47E3DC26F390A000029299 /* Messages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Messages.swift; sourceTree = ""; }; DD4A911D2708C65400501B7E /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; + DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = cannedmessages.pb.swift; sourceTree = ""; }; DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = ""; }; DD539501276DAA6A00AD86B1 /* MapLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLocation.swift; sourceTree = ""; }; DD70A467279C554A003120E4 /* cannedmessages.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = cannedmessages.pb.swift; sourceTree = ""; }; @@ -216,6 +218,7 @@ DDAF8C5626ED07740058C060 /* Protobufs */ = { isa = PBXGroup; children = ( + DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */, DDAF8C6126ED0A230058C060 /* admin.pb.swift */, DD17E5DB277D49D400010EC2 /* apponly.pb.swift */, DD70A467279C554A003120E4 /* cannedmessages.pb.swift */, @@ -531,6 +534,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DD4DED9027AD2975004BA27E /* cannedmessages.pb.swift in Sources */, DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */, DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */, DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */, diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index aa8f1c86..55d15b8a 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -833,8 +833,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet mutablePositions.add(position) - print("💾 Recieved a Position Packet") - if position.coordinate == nil { var newPostions = [PositionEntity]() newPostions.append(position) @@ -855,42 +853,45 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph try context!.save() if meshLoggingEnabled { - MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(fetchedNode[0].num)") + MeshLogger.log("💾 Updated NodeInfo Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)") } - print("💾 Updated NodeInfo SNR and Time from Position Packet For: \(fetchedNode[0].num)") + print("💾 Updated NodeInfo Position Coordinates, SNR and Time from Position App Packet For:: \(fetchedNode[0].num)") } catch { context!.rollback() let nsError = error as NSError - print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)") + print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)") } } catch { - print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP") + print("💥 Error Fetching NodeInfoEntity for POSITION_APP") } - - // + } else if decodedInfo.packet.decoded.portnum == PortNum.environmentalMeasurementApp { + + if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Environmental Measurement App UNHANDLED \(try decodedInfo.packet.jsonString())") } + print("ℹ️ MESH PACKET received for Environmental Measurement App UNHANDLED \(try decodedInfo.packet.jsonString())") + } else if decodedInfo.packet.decoded.portnum == PortNum.storeForwardApp { - if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Store Forward App UNHANDLED \(try decodedInfo.packet.jsonString())") } + if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Store Forward App UNHANDLED \(try decodedInfo.packet.jsonString())") } print("ℹ️ MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") } else if decodedInfo.packet.decoded.portnum == PortNum.adminApp { - if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") } + if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") } print("ℹ️ MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") } else if decodedInfo.packet.decoded.portnum == PortNum.routingApp { - if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") } + if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") } print("ℹ️ MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") } else { - if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") } + if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") } print("ℹ️ MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") } @@ -996,9 +997,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph var meshPacket = MeshPacket() meshPacket.to = UInt32(toUserNum) meshPacket.from = UInt32(fromUserNum) - meshPacket.decoded = dataMessage - meshPacket.decoded.isTapback = isTapback if replyID > 0 { meshPacket.decoded.replyID = UInt32(replyID) diff --git a/MeshtasticClient/Protobufs/environmental_measurement.pb.swift b/MeshtasticClient/Protobufs/environmental_measurement.pb.swift index 73e7ef02..76533a31 100644 --- a/MeshtasticClient/Protobufs/environmental_measurement.pb.swift +++ b/MeshtasticClient/Protobufs/environmental_measurement.pb.swift @@ -31,6 +31,12 @@ struct EnvironmentalMeasurement { var barometricPressure: Float = 0 + var gasResistance: Float = 0 + + var voltage: Float = 0 + + var current: Float = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -44,6 +50,9 @@ extension EnvironmentalMeasurement: SwiftProtobuf.Message, SwiftProtobuf._Messag 1: .same(proto: "temperature"), 2: .standard(proto: "relative_humidity"), 3: .standard(proto: "barometric_pressure"), + 4: .standard(proto: "gas_resistance"), + 5: .same(proto: "voltage"), + 6: .same(proto: "current"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -55,6 +64,9 @@ extension EnvironmentalMeasurement: SwiftProtobuf.Message, SwiftProtobuf._Messag case 1: try { try decoder.decodeSingularFloatField(value: &self.temperature) }() case 2: try { try decoder.decodeSingularFloatField(value: &self.relativeHumidity) }() case 3: try { try decoder.decodeSingularFloatField(value: &self.barometricPressure) }() + case 4: try { try decoder.decodeSingularFloatField(value: &self.gasResistance) }() + case 5: try { try decoder.decodeSingularFloatField(value: &self.voltage) }() + case 6: try { try decoder.decodeSingularFloatField(value: &self.current) }() default: break } } @@ -70,6 +82,15 @@ extension EnvironmentalMeasurement: SwiftProtobuf.Message, SwiftProtobuf._Messag if self.barometricPressure != 0 { try visitor.visitSingularFloatField(value: self.barometricPressure, fieldNumber: 3) } + if self.gasResistance != 0 { + try visitor.visitSingularFloatField(value: self.gasResistance, fieldNumber: 4) + } + if self.voltage != 0 { + try visitor.visitSingularFloatField(value: self.voltage, fieldNumber: 5) + } + if self.current != 0 { + try visitor.visitSingularFloatField(value: self.current, fieldNumber: 6) + } try unknownFields.traverse(visitor: &visitor) } @@ -77,6 +98,9 @@ extension EnvironmentalMeasurement: SwiftProtobuf.Message, SwiftProtobuf._Messag if lhs.temperature != rhs.temperature {return false} if lhs.relativeHumidity != rhs.relativeHumidity {return false} if lhs.barometricPressure != rhs.barometricPressure {return false} + if lhs.gasResistance != rhs.gasResistance {return false} + if lhs.voltage != rhs.voltage {return false} + if lhs.current != rhs.current {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/MeshtasticClient/Protobufs/mesh.pb.swift b/MeshtasticClient/Protobufs/mesh.pb.swift index 632a58a2..9f6370a3 100644 --- a/MeshtasticClient/Protobufs/mesh.pb.swift +++ b/MeshtasticClient/Protobufs/mesh.pb.swift @@ -81,6 +81,14 @@ enum HardwareModel: SwiftProtobuf.Enum { /// /// Custom DIY device based on @NanoVHF schematics: https://github.com/NanoVHF/Meshtastic-DIY/tree/main/Schematics case diyV1 // = 39 + + /// + /// RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ + case rak11200 // = 40 + + /// + /// 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 case UNRECOGNIZED(Int) init() { @@ -109,6 +117,8 @@ enum HardwareModel: SwiftProtobuf.Enum { case 37: self = .portduino case 38: self = .androidSim case 39: self = .diyV1 + case 40: self = .rak11200 + case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } } @@ -135,6 +145,8 @@ enum HardwareModel: SwiftProtobuf.Enum { case .portduino: return 37 case .androidSim: return 38 case .diyV1: return 39 + case .rak11200: return 40 + case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } } @@ -166,6 +178,8 @@ extension HardwareModel: CaseIterable { .portduino, .androidSim, .diyV1, + .rak11200, + .privateHw, ] } @@ -2062,6 +2076,8 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 37: .same(proto: "PORTDUINO"), 38: .same(proto: "ANDROID_SIM"), 39: .same(proto: "DIY_V1"), + 40: .same(proto: "RAK11200"), + 255: .same(proto: "PRIVATE_HW"), ] } diff --git a/MeshtasticClient/Protobufs/radioconfig.pb.swift b/MeshtasticClient/Protobufs/radioconfig.pb.swift index 38e33cdf..5ea36075 100644 --- a/MeshtasticClient/Protobufs/radioconfig.pb.swift +++ b/MeshtasticClient/Protobufs/radioconfig.pb.swift @@ -1244,6 +1244,25 @@ struct RadioConfig { set {_uniqueStorage()._cannedMessagePluginSendBell = newValue} } + /// + /// Whether to send encrypted or decrypted packets to MQTT. + /// This parameter is only honoured if you also set mqtt_server + /// (the default official mqtt.meshtastic.org server can handle encrypted packets) + /// + /// Decrypted packets may be useful for external systems that want to consume meshtastic packets + var mqttEncryptionEnabled: Bool { + get {return _storage._mqttEncryptionEnabled} + set {_uniqueStorage()._mqttEncryptionEnabled = newValue} + } + + /// + /// Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) + /// Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. + var adcMultiplierOverride: Float { + get {return _storage._adcMultiplierOverride} + set {_uniqueStorage()._adcMultiplierOverride = newValue} + } + var unknownFields = SwiftProtobuf.UnknownStorage() enum EnvironmentalMeasurementSensorType: SwiftProtobuf.Enum { @@ -1255,6 +1274,7 @@ struct RadioConfig { case dht22 // = 4 case bme280 // = 5 case bme680 // = 6 + case mcp9808 // = 7 case UNRECOGNIZED(Int) init() { @@ -1270,6 +1290,7 @@ struct RadioConfig { case 4: self = .dht22 case 5: self = .bme280 case 6: self = .bme680 + case 7: self = .mcp9808 default: self = .UNRECOGNIZED(rawValue) } } @@ -1283,6 +1304,7 @@ struct RadioConfig { case .dht22: return 4 case .bme280: return 5 case .bme680: return 6 + case .mcp9808: return 7 case .UNRECOGNIZED(let i): return i } } @@ -1311,6 +1333,7 @@ extension RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType: CaseIt .dht22, .bme280, .bme680, + .mcp9808, ] } @@ -1528,6 +1551,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes 171: .standard(proto: "canned_message_plugin_allow_input_source"), 172: .standard(proto: "canned_message_plugin_messages"), 173: .standard(proto: "canned_message_plugin_send_bell"), + 174: .standard(proto: "mqtt_encryption_enabled"), + 175: .standard(proto: "adc_multiplier_override"), ] fileprivate class _StorageClass { @@ -1612,6 +1637,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes var _cannedMessagePluginAllowInputSource: String = String() var _cannedMessagePluginMessages: String = String() var _cannedMessagePluginSendBell: Bool = false + var _mqttEncryptionEnabled: Bool = false + var _adcMultiplierOverride: Float = 0 static let defaultInstance = _StorageClass() @@ -1699,6 +1726,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes _cannedMessagePluginAllowInputSource = source._cannedMessagePluginAllowInputSource _cannedMessagePluginMessages = source._cannedMessagePluginMessages _cannedMessagePluginSendBell = source._cannedMessagePluginSendBell + _mqttEncryptionEnabled = source._mqttEncryptionEnabled + _adcMultiplierOverride = source._adcMultiplierOverride } } @@ -1798,6 +1827,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes case 171: try { try decoder.decodeSingularStringField(value: &_storage._cannedMessagePluginAllowInputSource) }() case 172: try { try decoder.decodeSingularStringField(value: &_storage._cannedMessagePluginMessages) }() case 173: try { try decoder.decodeSingularBoolField(value: &_storage._cannedMessagePluginSendBell) }() + case 174: try { try decoder.decodeSingularBoolField(value: &_storage._mqttEncryptionEnabled) }() + case 175: try { try decoder.decodeSingularFloatField(value: &_storage._adcMultiplierOverride) }() default: break } } @@ -2049,6 +2080,12 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._cannedMessagePluginSendBell != false { try visitor.visitSingularBoolField(value: _storage._cannedMessagePluginSendBell, fieldNumber: 173) } + if _storage._mqttEncryptionEnabled != false { + try visitor.visitSingularBoolField(value: _storage._mqttEncryptionEnabled, fieldNumber: 174) + } + if _storage._adcMultiplierOverride != 0 { + try visitor.visitSingularFloatField(value: _storage._adcMultiplierOverride, fieldNumber: 175) + } } try unknownFields.traverse(visitor: &visitor) } @@ -2139,6 +2176,8 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes if _storage._cannedMessagePluginAllowInputSource != rhs_storage._cannedMessagePluginAllowInputSource {return false} if _storage._cannedMessagePluginMessages != rhs_storage._cannedMessagePluginMessages {return false} if _storage._cannedMessagePluginSendBell != rhs_storage._cannedMessagePluginSendBell {return false} + if _storage._mqttEncryptionEnabled != rhs_storage._mqttEncryptionEnabled {return false} + if _storage._adcMultiplierOverride != rhs_storage._adcMultiplierOverride {return false} return true } if !storagesAreEqual {return false} @@ -2157,5 +2196,6 @@ extension RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType: SwiftP 4: .same(proto: "DHT22"), 5: .same(proto: "BME280"), 6: .same(proto: "BME680"), + 7: .same(proto: "MCP9808"), ] } diff --git a/MeshtasticClient/Views/Messages/UserMessageList.swift b/MeshtasticClient/Views/Messages/UserMessageList.swift index 50b241e5..c97fcc99 100644 --- a/MeshtasticClient/Views/Messages/UserMessageList.swift +++ b/MeshtasticClient/Views/Messages/UserMessageList.swift @@ -62,7 +62,7 @@ struct UserMessageList: View { let messageReply = allMessages.first(where: { $0.messageId == message.replyID }) HStack { - + Text(String(message.messageId)) Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.blue).font(.caption2) .padding(10) .overlay( @@ -212,6 +212,27 @@ struct UserMessageList: View { Text("Copy") Image(systemName: "doc.on.doc") } + Menu("Message Details") { + + VStack { + + let time = Int32(message.messageTimestamp) + let messageDate = Date(timeIntervalSince1970: TimeInterval(time)) + + if time != 0 { + Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray) + } else { + Text("Unknown").font(.caption2).foregroundColor(.gray) + } + + } + + VStack { + + Text("Received ACK: \(message.receivedACK ? "✔️" : "")") + + } + } Divider() Button(role: .destructive, action: { self.showDeleteMessageAlert = true @@ -261,16 +282,23 @@ struct UserMessageList: View { let time = Int32(message.messageTimestamp) let messageDate = Date(timeIntervalSince1970: TimeInterval(time)) + let showUntil = Date().addingTimeInterval(3600) if time != 0 { - Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray) - Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray) + // Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray) + // Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray) } else { - Text("Unknown").font(.caption2).foregroundColor(.gray) + // Text("Unknown").font(.caption2).foregroundColor(.gray) + } + + if messageDate <= showUntil && message.receivedACK { + + Text("Delivered").font(.caption2).foregroundColor(.gray) } } - .padding(4) + } + .padding(.bottom) .id(allMessages.firstIndex(of: message)) if !currentUser { diff --git a/gen_protos.sh b/gen_protos.sh index 0f34f5e6..d43133ae 100755 --- a/gen_protos.sh +++ b/gen_protos.sh @@ -14,7 +14,7 @@ fi pdir=$(realpath "../Meshtastic-protobufs") sdir=$(realpath "./MeshtasticClient/Protobufs") -echo "pdir:$pdir sdir:$sdir" +echo "pdir:$pdir sdir:$sdir pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto deviceonly.proto environmental_measurement.proto mesh.proto mqtt.proto portnums.proto radioconfig.proto remote_hardware.proto storeforward.proto" for pf in $pfiles do