mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #136 from meshtastic/1_3_30_upgrades
1 3 30 upgrades
This commit is contained in:
commit
f01cf6c8e1
12 changed files with 113 additions and 62 deletions
|
|
@ -1214,6 +1214,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
dataMessage.wantResponse = wantResponse
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
|
|
|
|||
|
|
@ -243,10 +243,16 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data LoRaConfigEntity: \(nsError)")
|
||||
}
|
||||
} else {
|
||||
|
||||
print("💥 No Nodes found in core data matching connected node number \(nodeNum)")
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Fetching node for core data LoRaConfigEntity failed: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1312,43 +1318,45 @@ func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObj
|
|||
|
||||
if meshLogging { MeshLogger.log("🕸️ ROUTING PACKET received for RequestID: \(packet.decoded.requestID) Error: \(errorExplanation)") }
|
||||
|
||||
if routingMessage.errorReason == Routing.Error.none {
|
||||
|
||||
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
|
||||
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
|
||||
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
|
||||
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
|
||||
|
||||
do {
|
||||
do {
|
||||
|
||||
let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity]
|
||||
let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity]
|
||||
|
||||
if fetchedMessage?.count ?? 0 > 0 {
|
||||
|
||||
if fetchedMessage?.count ?? 0 > 0 {
|
||||
fetchedMessage![0].ackError = Int32(routingMessage.errorReason.rawValue)
|
||||
|
||||
if routingMessage.errorReason == Routing.Error.none {
|
||||
|
||||
fetchedMessage![0].receivedACK = true
|
||||
fetchedMessage![0].ackSNR = packet.rxSnr
|
||||
fetchedMessage![0].ackTimestamp = Int32(packet.rxTime)
|
||||
fetchedMessage![0].objectWillChange.send()
|
||||
fetchedMessage![0].fromUser?.objectWillChange.send()
|
||||
fetchedMessage![0].toUser?.objectWillChange.send()
|
||||
|
||||
} else {
|
||||
|
||||
return
|
||||
}
|
||||
fetchedMessage![0].ackSNR = packet.rxSnr
|
||||
fetchedMessage![0].ackTimestamp = Int32(packet.rxTime)
|
||||
fetchedMessage![0].objectWillChange.send()
|
||||
|
||||
try context.save()
|
||||
|
||||
if meshLogging {
|
||||
MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)")
|
||||
}
|
||||
} else {
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)")
|
||||
return
|
||||
}
|
||||
|
||||
try context.save()
|
||||
|
||||
if meshLogging {
|
||||
MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1422,7 +1430,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging:
|
|||
|
||||
if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) {
|
||||
|
||||
if meshLogging { MeshLogger.log("💬 Message received for text message app \(messageText)") }
|
||||
if meshLogging { MeshLogger.log("💬 Message received for text message app") }
|
||||
|
||||
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
|
||||
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
|
||||
|
|
@ -1496,7 +1504,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging:
|
|||
]
|
||||
|
||||
manager.schedule()
|
||||
if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
|
||||
if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown")") }
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
|
|
@ -194,6 +195,7 @@
|
|||
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="apHidden" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="apMode" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
|
|
@ -205,7 +207,7 @@
|
|||
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
|
||||
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="230"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="314"/>
|
||||
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
|
|
@ -215,6 +217,6 @@
|
|||
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="134"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
|
||||
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
|
||||
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="134"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -527,6 +527,10 @@ struct Config {
|
|||
/// If set, the node AP will broadcast as a hidden SSID
|
||||
var apHidden: Bool = false
|
||||
|
||||
///
|
||||
/// If set, wifi is enabled. Previously done through setting ssid and psk
|
||||
var enabled: Bool = false
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -1381,6 +1385,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
2: .same(proto: "psk"),
|
||||
3: .standard(proto: "ap_mode"),
|
||||
4: .standard(proto: "ap_hidden"),
|
||||
5: .same(proto: "enabled"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1393,6 +1398,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
case 2: try { try decoder.decodeSingularStringField(value: &self.psk) }()
|
||||
case 3: try { try decoder.decodeSingularBoolField(value: &self.apMode) }()
|
||||
case 4: try { try decoder.decodeSingularBoolField(value: &self.apHidden) }()
|
||||
case 5: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1411,6 +1417,9 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if self.apHidden != false {
|
||||
try visitor.visitSingularBoolField(value: self.apHidden, fieldNumber: 4)
|
||||
}
|
||||
if self.enabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 5)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -1419,6 +1428,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if lhs.psk != rhs.psk {return false}
|
||||
if lhs.apMode != rhs.apMode {return false}
|
||||
if lhs.apHidden != rhs.apHidden {return false}
|
||||
if lhs.enabled != rhs.enabled {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
|
||||
case m5Stack // = 44
|
||||
|
||||
///
|
||||
/// B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
||||
case stationG1 // = 45
|
||||
|
||||
///
|
||||
/// 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
|
||||
|
|
@ -166,6 +170,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 42: self = .nrf52840Pca10059
|
||||
case 43: self = .drDev
|
||||
case 44: self = .m5Stack
|
||||
case 45: self = .stationG1
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -198,6 +203,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .nrf52840Pca10059: return 42
|
||||
case .drDev: return 43
|
||||
case .m5Stack: return 44
|
||||
case .stationG1: return 45
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -235,6 +241,7 @@ extension HardwareModel: CaseIterable {
|
|||
.nrf52840Pca10059,
|
||||
.drDev,
|
||||
.m5Stack,
|
||||
.stationG1,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -2176,6 +2183,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
42: .same(proto: "NRF52840_PCA10059"),
|
||||
43: .same(proto: "DR_DEV"),
|
||||
44: .same(proto: "M5STACK"),
|
||||
45: .same(proto: "STATION_G1"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ struct Connect: View {
|
|||
@State var isPreferredRadio: Bool = false
|
||||
|
||||
@State var firmwareVersion = "0.0.0"
|
||||
@State var minimumVersion = "1.3.28"
|
||||
@State var minimumVersion = "1.3.30"
|
||||
@State var invalidVersion = false
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,9 @@ struct UserMessageList: View {
|
|||
Text("Unknown Age").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
} else if message.ackError > 0 {
|
||||
|
||||
Text("Ack Failure")
|
||||
}
|
||||
if message.ackSNR != 0 {
|
||||
VStack {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct NodeList: View {
|
|||
@State var initialLoad: Bool = true
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
|
||||
sortDescriptors: [NSSortDescriptor(key: "user.shortName", ascending: true)],
|
||||
animation: .default)
|
||||
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
|
@ -61,7 +61,7 @@ struct NodeList: View {
|
|||
if UIDevice.current.userInterfaceIdiom == .pad { Text(node.user?.longName ?? "Unknown").font(.headline)
|
||||
.offset(x: -15)
|
||||
} else {
|
||||
Text(node.user?.longName ?? "Unknown").font(.title).offset(x: -15)
|
||||
Text(node.user?.longName ?? "Unknown").font(.title2).offset(x: -15)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 10)
|
||||
|
|
@ -106,16 +106,16 @@ struct NodeList: View {
|
|||
|
||||
if initialLoad {
|
||||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
self.bleManager.context = context
|
||||
self.initialLoad = false
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
if nodes.count > 0 {
|
||||
selection = "0"
|
||||
}
|
||||
}
|
||||
// if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//
|
||||
// if nodes.count > 0 {
|
||||
// selection = "0"
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ struct TelemetryLog: View {
|
|||
|
||||
Spacer()
|
||||
Text("Device Metrics")
|
||||
.font(.title)
|
||||
.font(.title3)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +234,6 @@ struct TelemetryLog: View {
|
|||
} else if tel.metricsType == 1 {
|
||||
|
||||
// Environment Metrics
|
||||
|
||||
let tempReadingType = (!(node.telemetryConfig?.environmentDisplayFahrenheit ?? true)) ? "°C" : "°F"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -376,9 +376,12 @@ struct CannedMessagesConfig: View {
|
|||
// RAK Rotary Encoder
|
||||
updown1Enabled = true
|
||||
rotary1Enabled = false
|
||||
inputbrokerEventCw = InputEventChars.keyUp.rawValue
|
||||
inputbrokerEventCcw = InputEventChars.keyDown.rawValue
|
||||
inputbrokerEventPress = InputEventChars.keySelect.rawValue
|
||||
inputbrokerPinA = 4
|
||||
inputbrokerPinB = 10
|
||||
inputbrokerPinPress = 3
|
||||
inputbrokerEventCw = InputEventChars.keyNone.rawValue
|
||||
inputbrokerEventCcw = InputEventChars.keyNone.rawValue
|
||||
inputbrokerEventPress = InputEventChars.keyNone.rawValue
|
||||
|
||||
} else if newPreset == 2 {
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ struct ExternalNotificationConfig: View {
|
|||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges || !(node!.myInfo?.hasWifi ?? false))
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ struct WiFiConfig: View {
|
|||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges: Bool = false
|
||||
|
||||
@State var enabled = false
|
||||
|
||||
@State var ssid = ""
|
||||
@State var password = ""
|
||||
|
|
@ -28,16 +30,21 @@ struct WiFiConfig: View {
|
|||
|
||||
VStack {
|
||||
|
||||
Text("Enabling WiFi will disable bluetooth, only one connection method works at a time. Saving these settings will disconnect your device from the app.")
|
||||
.font(.title3)
|
||||
.padding()
|
||||
|
||||
Form {
|
||||
|
||||
Section(header: Text("SSID & Password")) {
|
||||
Text("Enabling WiFi will disable the bluetooth connection to the app.")
|
||||
.font(.title3)
|
||||
|
||||
Section(header: Text("Options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
|
||||
Label("Enable", systemImage: "wifi")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
HStack {
|
||||
Label("SSID", systemImage: "wifi")
|
||||
Label("SSID", systemImage: "network")
|
||||
TextField("SSID", text: $ssid)
|
||||
.foregroundColor(.gray)
|
||||
.onChange(of: ssid, perform: { value in
|
||||
|
|
@ -87,24 +94,25 @@ struct WiFiConfig: View {
|
|||
.disableAutocorrection(true)
|
||||
|
||||
}
|
||||
Section(header: Text("AP Settings")) {
|
||||
Section(header: Text("Sofware Access Point")) {
|
||||
|
||||
Text("WiFi uses client mode by default, if Software Access Point(AP) is on the SSID and password will be used to access the AP at meshtastic.local.")
|
||||
.font(.caption)
|
||||
|
||||
Toggle(isOn: $apMode) {
|
||||
|
||||
Label("Soft AP Mode", systemImage: "wifi")
|
||||
Label("Soft AP Mode", systemImage: "externaldrive.fill.badge.wifi")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("If set the software access point mode will be activated.")
|
||||
.font(.caption)
|
||||
|
||||
Toggle(isOn: $apHidden) {
|
||||
if apMode {
|
||||
|
||||
Toggle(isOn: $apHidden) {
|
||||
|
||||
Label("Hidden AP", systemImage: "eye.slash")
|
||||
Label("Hidden SSID", systemImage: "eye.slash")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("If set the SSID for the AP will be hidden.")
|
||||
.font(.caption)
|
||||
|
||||
}
|
||||
}
|
||||
.disabled(!(node != nil && node!.myInfo?.hasWifi ?? false))
|
||||
|
|
@ -130,6 +138,7 @@ struct WiFiConfig: View {
|
|||
Button("Save WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var wifi = Config.WiFiConfig()
|
||||
wifi.enabled = self.enabled
|
||||
wifi.ssid = self.ssid
|
||||
wifi.psk = self.password
|
||||
wifi.apMode = self.apMode
|
||||
|
|
@ -162,6 +171,7 @@ struct WiFiConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.enabled = (node!.wiFiConfig?.enabled ?? false)
|
||||
self.ssid = node!.wiFiConfig?.ssid ?? ""
|
||||
self.password = node!.wiFiConfig?.password ?? ""
|
||||
self.apMode = (node!.wiFiConfig?.apMode ?? false)
|
||||
|
|
@ -171,6 +181,13 @@ struct WiFiConfig: View {
|
|||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
||||
if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: ssid) { newSsid in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue