diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index c5f1370d..50e4621a 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -232,6 +232,7 @@ DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeshtasticTests.swift; sourceTree = ""; }; DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = ""; }; DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = ""; }; + DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV4.xcdatamodel; sourceTree = ""; }; DDF924C926FBB953009FE055 /* ConnectedDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectedDevice.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1209,11 +1210,12 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */, DDCDC69A29467643004C1DDA /* MeshtasticDataModelV3.xcdatamodel */, DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DDCDC69A29467643004C1DDA /* MeshtasticDataModelV3.xcdatamodel */; + currentVersion = DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index f947ecbf..469a5073 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1320,6 +1320,10 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM do { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity] for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { + if channel.index == newMessage.channel { + context.refresh(channel, mergeChanges: true) + } + if channel.index == newMessage.channel && !channel.mute { // Create an iOS Notification for the received private channel message and schedule it immediately let manager = LocalNotificationManager() diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index d57dfe2f..a8bccd83 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV3.xcdatamodel + MeshtasticDataModelV4.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV4.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV4.xcdatamodel/contents new file mode 100644 index 00000000..7bb559e9 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV4.xcdatamodel/contents @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Protobufs/module_config.pb.swift b/Meshtastic/Protobufs/module_config.pb.swift index 3f0709a2..19fa66f7 100644 --- a/Meshtastic/Protobufs/module_config.pb.swift +++ b/Meshtastic/Protobufs/module_config.pb.swift @@ -481,33 +481,71 @@ struct ModuleConfig { // methods supported on all messages. /// - /// Preferences for the ExternalNotificationModule + /// Enable the ExternalNotificationModule var enabled: Bool = false /// - /// TODO: REPLACE + /// When using in On/Off mode, keep the output on for this many + /// milliseconds. Default 1000ms (1 second). var outputMs: UInt32 = 0 /// - /// TODO: REPLACE + /// Define the output pin GPIO setting Defaults to + /// EXT_NOTIFY_OUT if set for the board. + /// In standalone devices this pin should drive the LED to match the UI. var output: UInt32 = 0 /// - /// TODO: REPLACE + /// Optional: Define a secondary output pin for a vibra motor + /// This is used in standalone devices to match the UI. + var outputVibra: UInt32 = 0 + + /// + /// Optional: Define a tertiary output pin for an active buzzer + /// This is used in standalone devices to to match the UI. + var outputBuzzer: UInt32 = 0 + + /// + /// IF this is true, the 'output' Pin will be pulled active high, false + /// means active low. var active: Bool = false /// - /// TODO: REPLACE + /// True: Alert when a text message arrives (output) var alertMessage: Bool = false /// - /// TODO: REPLACE + /// True: Alert when a text message arrives (output_vibra) + var alertMessageVibra: Bool = false + + /// + /// True: Alert when a text message arrives (output_buzzer) + var alertMessageBuzzer: Bool = false + + /// + /// True: Alert when the bell character is received (output) var alertBell: Bool = false /// - /// TODO: REPLACE + /// True: Alert when the bell character is received (output_vibra) + var alertBellVibra: Bool = false + + /// + /// True: Alert when the bell character is received (output_buzzer) + var alertBellBuzzer: Bool = false + + /// + /// use a PWM output instead of a simple on/off output. This will ignore + /// the 'output', 'output_ms' and 'active' settings and use the + /// device.buzzer_gpio instead. var usePwm: Bool = false + /// + /// The notification will toggle with 'output_ms' for this time of seconds. + /// Default is 0 which means don't repeat at all. 60 would mean blink + /// and/or beep for 60 seconds + var nagTimeout: UInt32 = 0 + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1248,10 +1286,17 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP 1: .same(proto: "enabled"), 2: .standard(proto: "output_ms"), 3: .same(proto: "output"), + 8: .standard(proto: "output_vibra"), + 9: .standard(proto: "output_buzzer"), 4: .same(proto: "active"), 5: .standard(proto: "alert_message"), + 10: .standard(proto: "alert_message_vibra"), + 11: .standard(proto: "alert_message_buzzer"), 6: .standard(proto: "alert_bell"), + 12: .standard(proto: "alert_bell_vibra"), + 13: .standard(proto: "alert_bell_buzzer"), 7: .standard(proto: "use_pwm"), + 14: .standard(proto: "nag_timeout"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1267,6 +1312,13 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP case 5: try { try decoder.decodeSingularBoolField(value: &self.alertMessage) }() case 6: try { try decoder.decodeSingularBoolField(value: &self.alertBell) }() case 7: try { try decoder.decodeSingularBoolField(value: &self.usePwm) }() + case 8: try { try decoder.decodeSingularUInt32Field(value: &self.outputVibra) }() + case 9: try { try decoder.decodeSingularUInt32Field(value: &self.outputBuzzer) }() + case 10: try { try decoder.decodeSingularBoolField(value: &self.alertMessageVibra) }() + case 11: try { try decoder.decodeSingularBoolField(value: &self.alertMessageBuzzer) }() + case 12: try { try decoder.decodeSingularBoolField(value: &self.alertBellVibra) }() + case 13: try { try decoder.decodeSingularBoolField(value: &self.alertBellBuzzer) }() + case 14: try { try decoder.decodeSingularUInt32Field(value: &self.nagTimeout) }() default: break } } @@ -1294,6 +1346,27 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP if self.usePwm != false { try visitor.visitSingularBoolField(value: self.usePwm, fieldNumber: 7) } + if self.outputVibra != 0 { + try visitor.visitSingularUInt32Field(value: self.outputVibra, fieldNumber: 8) + } + if self.outputBuzzer != 0 { + try visitor.visitSingularUInt32Field(value: self.outputBuzzer, fieldNumber: 9) + } + if self.alertMessageVibra != false { + try visitor.visitSingularBoolField(value: self.alertMessageVibra, fieldNumber: 10) + } + if self.alertMessageBuzzer != false { + try visitor.visitSingularBoolField(value: self.alertMessageBuzzer, fieldNumber: 11) + } + if self.alertBellVibra != false { + try visitor.visitSingularBoolField(value: self.alertBellVibra, fieldNumber: 12) + } + if self.alertBellBuzzer != false { + try visitor.visitSingularBoolField(value: self.alertBellBuzzer, fieldNumber: 13) + } + if self.nagTimeout != 0 { + try visitor.visitSingularUInt32Field(value: self.nagTimeout, fieldNumber: 14) + } try unknownFields.traverse(visitor: &visitor) } @@ -1301,10 +1374,17 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP if lhs.enabled != rhs.enabled {return false} if lhs.outputMs != rhs.outputMs {return false} if lhs.output != rhs.output {return false} + if lhs.outputVibra != rhs.outputVibra {return false} + if lhs.outputBuzzer != rhs.outputBuzzer {return false} if lhs.active != rhs.active {return false} if lhs.alertMessage != rhs.alertMessage {return false} + if lhs.alertMessageVibra != rhs.alertMessageVibra {return false} + if lhs.alertMessageBuzzer != rhs.alertMessageBuzzer {return false} if lhs.alertBell != rhs.alertBell {return false} + if lhs.alertBellVibra != rhs.alertBellVibra {return false} + if lhs.alertBellBuzzer != rhs.alertBellBuzzer {return false} if lhs.usePwm != rhs.usePwm {return false} + if lhs.nagTimeout != rhs.nagTimeout {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 68f407e3..72a81c15 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -319,7 +319,7 @@ struct ChannelMessageList: View { focusedField = nil replyMessageId = 0 if sendPositionWithMessage { - if bleManager.sendPosition(destNum: Int64(channel.index), wantAck: true) { + if bleManager.sendPosition(destNum: Int64(channel.index), wantResponse: false) { print("Location Sent") } } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 6dda3220..391fab03 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -316,7 +316,7 @@ struct UserMessageList: View { focusedField = nil replyMessageId = 0 if sendPositionWithMessage { - if bleManager.sendPosition(destNum: user.num, wantAck: true) { + if bleManager.sendPosition(destNum: user.num, wantResponse: true) { print("Location Sent") } } diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 69896e77..5d6260cc 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -8,6 +8,7 @@ import SwiftUI enum OutputIntervals: Int, CaseIterable, Identifiable { + case unset = 0 case oneSecond = 1000 case twoSeconds = 2000 case threeSeconds = 3000 @@ -23,6 +24,8 @@ enum OutputIntervals: Int, CaseIterable, Identifiable { get { switch self { + case .unset: + return "Unset" case .oneSecond: return "One Second" case .twoSeconds: @@ -85,6 +88,8 @@ struct ExternalNotificationConfig: View { Label("Use PWM Buzzer", systemImage: "light.beacon.max.fill") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Use a PWM Buzzer (RAK buzzer module), disable PWM buzzer to set manual GPIO options.") + .font(.caption) } if !usePWM { Section(header: Text("GPIO")) { @@ -94,7 +99,6 @@ struct ExternalNotificationConfig: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Text("Specifies whether the external circuit is triggered when the device's GPIO is low or high.") .font(.caption) - .listRowSeparator(.visible) Picker("GPIO to monitor", selection: $output) { ForEach(0..<40) { if $0 == 0 {