mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Handle empty positions, allow nrf52 saving for telemetry
This commit is contained in:
parent
355e1126a2
commit
ddfcea4ace
24 changed files with 865 additions and 674 deletions
|
|
@ -39,6 +39,7 @@
|
|||
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */; };
|
||||
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; };
|
||||
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B85A728009258000ACD6B /* ShareChannel.swift */; };
|
||||
DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* LocationHistory.swift */; };
|
||||
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; };
|
||||
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; };
|
||||
DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FE272476C700F4AB02 /* LogDocument.swift */; };
|
||||
|
|
@ -127,6 +128,7 @@
|
|||
DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = "<group>"; };
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = "<group>"; };
|
||||
DD6B85A728009258000ACD6B /* ShareChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannel.swift; sourceTree = "<group>"; };
|
||||
DD73FD1028750779000852D6 /* LocationHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHistory.swift; sourceTree = "<group>"; };
|
||||
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
|
||||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
|
||||
DD8169FE272476C700F4AB02 /* LogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDocument.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -225,6 +227,7 @@
|
|||
DD47E3CD26F103C600029299 /* NodeList.swift */,
|
||||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */,
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */,
|
||||
DD73FD1028750779000852D6 /* LocationHistory.swift */,
|
||||
);
|
||||
path = Nodes;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -661,6 +664,7 @@
|
|||
DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */,
|
||||
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */,
|
||||
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */,
|
||||
DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */,
|
||||
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */,
|
||||
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */,
|
||||
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -660,7 +660,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessage.messageId = Int64(UInt32.random(in: UInt32(UInt8.max)..<UInt32.max))
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "IN"
|
||||
newMessage.toUser = fetchedUsers.first(where: { $0.num == toUserNum })
|
||||
newMessage.isEmoji = isEmoji
|
||||
newMessage.admin = false
|
||||
|
|
@ -831,8 +830,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = wantResponse
|
||||
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
|
|
@ -848,10 +845,23 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
|
||||
return true
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a Shutdown Admin Message for node: \(String(destNum))") }
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
@ -884,9 +894,23 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a Reboot Admin Message for node: \(String(destNum))") }
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
@ -906,7 +930,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = wantResponse
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
|
|
@ -922,15 +945,28 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a Factory Reset Admin Message for node: \(String(destNum))") }
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
|
||||
public func saveUser(config: User, fromUser: UserEntity?, toUser: UserEntity?, wantResponse: Bool) -> Int64 {
|
||||
|
||||
var newMessageId: Int64 = 0
|
||||
|
||||
|
|
@ -964,18 +1000,17 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved User Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.adminDescription = "Saved User Config for \(toUser!.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new User Config Admin Message for node: \(String(toUser.num))") }
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new User Config Admin Message for node: \(String(toUser!.num))") }
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
} catch {
|
||||
|
|
@ -1024,12 +1059,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Device Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1083,12 +1117,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Display Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1143,12 +1176,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "IN"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved LoRa Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1202,12 +1234,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Position Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1261,12 +1292,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Canned Message Module Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1319,12 +1349,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved External Notification Module Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1341,9 +1370,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return newMessageId
|
||||
|
||||
}
|
||||
|
||||
public func saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
|
||||
|
|
@ -1379,12 +1406,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Range Test Module Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1401,9 +1427,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return newMessageId
|
||||
|
||||
}
|
||||
|
||||
public func saveSerialModuleConfig(config: ModuleConfig.SerialConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
|
||||
|
|
@ -1440,12 +1464,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Serial Module Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1462,7 +1485,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return newMessageId
|
||||
}
|
||||
|
||||
|
|
@ -1499,12 +1521,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessageId = newMessage.messageId
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "OUT"
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = "Saved Telemetry Module Config for \(toUser.longName ?? "Unknown")"
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
newMessage.messagePayload = try! dataMessage.jsonString()
|
||||
newMessage.messagePayload = try! config.jsonString()
|
||||
|
||||
do {
|
||||
|
||||
|
|
@ -1521,8 +1542,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return newMessageId
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -896,15 +896,18 @@ func nodeInfoPacket (nodeInfo: NodeInfo, meshLogging: Bool, context: NSManagedOb
|
|||
newNode.user = newUser
|
||||
}
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
position.longitudeI = nodeInfo.position.longitudeI
|
||||
position.altitude = nodeInfo.position.altitude
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time)))
|
||||
|
||||
var newPostions = [PositionEntity]()
|
||||
newPostions.append(position)
|
||||
newNode.positions? = NSOrderedSet(array: newPostions)
|
||||
if nodeInfo.position.latitudeI > 0 || nodeInfo.position.longitudeI > 0 {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
position.longitudeI = nodeInfo.position.longitudeI
|
||||
position.altitude = nodeInfo.position.altitude
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.position.time)))
|
||||
|
||||
var newPostions = [PositionEntity]()
|
||||
newPostions.append(position)
|
||||
newNode.positions? = NSOrderedSet(array: newPostions)
|
||||
}
|
||||
|
||||
// Look for a MyInfo
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
|
|
@ -1132,54 +1135,59 @@ func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb
|
|||
|
||||
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
|
||||
|
||||
if fetchedNode.count == 1 {
|
||||
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
|
||||
if let positionMessage = try? Position(serializedData: packet.decoded.payload) {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = positionMessage.latitudeI
|
||||
position.longitudeI = positionMessage.longitudeI
|
||||
position.altitude = positionMessage.altitude
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutablePositions.add(position)
|
||||
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
do {
|
||||
if let positionMessage = try? Position(serializedData: packet.decoded.payload) {
|
||||
|
||||
// Don't save empty position packets
|
||||
if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
|
||||
|
||||
try context.save()
|
||||
if fetchedNode.count == 1 {
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = positionMessage.latitudeI
|
||||
position.longitudeI = positionMessage.longitudeI
|
||||
position.altitude = positionMessage.altitude
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
|
||||
if meshLogging {
|
||||
MeshLogger.log("💾 Updated Node Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)")
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutablePositions.add(position)
|
||||
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
|
||||
if meshLogging {
|
||||
MeshLogger.log("💾 Updated Node 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 POSITION_APP \(nsError)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
print("💥 Empty POSITION_APP Packet")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
print("💥 Error Fetching NodeInfoEntity for POSITION_APP")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
|
||||
|
|
@ -1332,7 +1340,6 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging:
|
|||
newMessage.messageId = Int64(packet.id)
|
||||
newMessage.messageTimestamp = Int32(bitPattern: packet.rxTime)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "IN"
|
||||
newMessage.isEmoji = packet.decoded.emoji == 1
|
||||
|
||||
if packet.decoded.replyID > 0 {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@
|
|||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="direction" attributeType="String"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" attributeType="String"/>
|
||||
|
|
@ -195,17 +194,17 @@
|
|||
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="209"/>
|
||||
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<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="104"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="230"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="299"/>
|
||||
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
|
||||
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
|
||||
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="179"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="194"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="215"/>
|
||||
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
|
||||
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="179"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -1,13 +1,10 @@
|
|||
//
|
||||
// Connect.swift
|
||||
// MeshtasticApple
|
||||
// Meshtastic Apple
|
||||
//
|
||||
// Created by Garth Vander Houwen on 8/18/21.
|
||||
// Copyright(c) Garth Vander Houwen 8/18/21.
|
||||
//
|
||||
|
||||
// Abstract:
|
||||
// A view allowing you to interact with nearby meshtastic nodes
|
||||
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
import CoreLocation
|
||||
|
|
@ -20,7 +17,6 @@ struct Connect: View {
|
|||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
@State var initialLoad: Bool = true
|
||||
|
||||
@State var isPreferredRadio: Bool = false
|
||||
|
||||
var body: some View {
|
||||
|
|
@ -32,158 +28,167 @@ struct Connect: View {
|
|||
NavigationView {
|
||||
|
||||
VStack {
|
||||
if bleManager.isSwitchedOn {
|
||||
|
||||
List {
|
||||
List {
|
||||
|
||||
if bleManager.isSwitchedOn {
|
||||
|
||||
if supportedVersion == false {
|
||||
|
||||
|
||||
if supportedVersion == false {
|
||||
|
||||
Section(header: Text("Upgrade your Firmware").font(.title)) {
|
||||
Section(header: Text("Upgrade your Firmware").font(.title)) {
|
||||
|
||||
Text("🚨 1.3 ALPHA PREVIEW this version of the app supports only version \(minimumVersion).").font(.subheadline).foregroundColor(.red)
|
||||
}
|
||||
.textCase(nil)
|
||||
Text("🚨 1.3 ALPHA PREVIEW this version of the app supports only version \(minimumVersion).").font(.subheadline).foregroundColor(.red)
|
||||
}
|
||||
|
||||
if bleManager.lastConnectionError.count > 0 {
|
||||
.textCase(nil)
|
||||
}
|
||||
|
||||
if bleManager.lastConnectionError.count > 0 {
|
||||
|
||||
Section(header: Text("Connection Error").font(.title)) {
|
||||
Section(header: Text("Connection Error").font(.title)) {
|
||||
|
||||
Text(bleManager.lastConnectionError).font(.title3).foregroundColor(.red)
|
||||
}
|
||||
.textCase(nil)
|
||||
Text(bleManager.lastConnectionError).font(.title3).foregroundColor(.red)
|
||||
}
|
||||
.textCase(nil)
|
||||
}
|
||||
|
||||
Section(header: Text("Connected Radio").font(.title)) {
|
||||
Section(header: Text("Connected Radio").font(.title)) {
|
||||
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected {
|
||||
HStack {
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected {
|
||||
HStack {
|
||||
|
||||
Image(systemName: "antenna.radiowaves.left.and.right")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.green)
|
||||
.padding(.trailing)
|
||||
Image(systemName: "antenna.radiowaves.left.and.right")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.green)
|
||||
.padding(.trailing)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
|
||||
Text(bleManager.connectedPeripheral.longName).font(.title2)
|
||||
Text(bleManager.connectedPeripheral.longName).font(.title2)
|
||||
|
||||
}
|
||||
Text("BLE Name: ").font(.caption)+Text(bleManager.connectedPeripheral.peripheral.name ?? "Unknown")
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.firmwareVersion)
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
Text("Bitrate: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.bitrate ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
|
||||
|
||||
Text("Channel Utilization: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.channelUtilization ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
Text("Air Time: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.airTime ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
}
|
||||
if bleManager.connectedPeripheral.subscribed {
|
||||
Text("Properly Subscribed").font(.caption)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
Text("BLE Name: ").font(.caption)+Text(bleManager.connectedPeripheral.peripheral.name ?? "Unknown")
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.firmwareVersion)
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
Text("Bitrate: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.bitrate ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
|
||||
|
||||
Text("Channel Utilization: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.channelUtilization ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
Text("Air Time: ").font(.caption)+Text(String(format: "%.2f", bleManager.connectedPeripheral.airTime ?? 0.00))
|
||||
.font(.caption).foregroundColor(Color.gray)
|
||||
}
|
||||
if bleManager.connectedPeripheral.subscribed {
|
||||
Text("Properly Subscribed").font(.caption)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Text("Preferred").font(.caption2)
|
||||
Text("Radio").font(.caption2)
|
||||
Toggle("Preferred Radio", isOn: $isPreferredRadio)
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.labelsHidden()
|
||||
.onChange(of: isPreferredRadio) { value in
|
||||
if value {
|
||||
Text("Preferred").font(.caption2)
|
||||
Text("Radio").font(.caption2)
|
||||
Toggle("Preferred Radio", isOn: $isPreferredRadio)
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.labelsHidden()
|
||||
.onChange(of: isPreferredRadio) { value in
|
||||
if value {
|
||||
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
|
||||
let deviceName = (bleManager.connectedPeripheral.peripheral.name ?? "")
|
||||
userSettings.preferredPeripheralName = deviceName
|
||||
|
||||
} else {
|
||||
|
||||
userSettings.preferredPeripheralName = bleManager.connectedPeripheral.longName
|
||||
}
|
||||
|
||||
userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString
|
||||
let deviceName = (bleManager.connectedPeripheral.peripheral.name ?? "")
|
||||
userSettings.preferredPeripheralName = deviceName
|
||||
|
||||
} else {
|
||||
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId {
|
||||
|
||||
userSettings.preferredPeripheralId = ""
|
||||
userSettings.preferredPeripheralName = ""
|
||||
userSettings.preferredPeripheralName = bleManager.connectedPeripheral.longName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.swipeActions {
|
||||
|
||||
Button(role: .destructive) {
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
||||
bleManager.disconnectPeripheral()
|
||||
isPreferredRadio = false
|
||||
}
|
||||
} label: {
|
||||
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
||||
}
|
||||
}
|
||||
.padding([.top, .bottom])
|
||||
|
||||
|
||||
} else {
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.red)
|
||||
.padding(.trailing)
|
||||
Text("No device connected").font(.title3)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString
|
||||
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
if bleManager.peripherals.count > 0 {
|
||||
Section(header: Text("Available Radios").font(.title)) {
|
||||
ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.name < $1.name })) { peripheral in
|
||||
HStack {
|
||||
Image(systemName: "circle.fill")
|
||||
.imageScale(.large).foregroundColor(.gray)
|
||||
.padding(.trailing)
|
||||
Button(action: {
|
||||
self.bleManager.stopScanning()
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
self.bleManager.disconnectPeripheral()
|
||||
}
|
||||
self.bleManager.connectTo(peripheral: peripheral.peripheral)
|
||||
if userSettings.preferredPeripheralId == peripheral.peripheral.identifier.uuidString {
|
||||
|
||||
isPreferredRadio = true
|
||||
} else {
|
||||
|
||||
isPreferredRadio = false
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId {
|
||||
|
||||
userSettings.preferredPeripheralId = ""
|
||||
userSettings.preferredPeripheralName = ""
|
||||
}
|
||||
}) {
|
||||
Text(peripheral.name).font(.title3)
|
||||
}
|
||||
Spacer()
|
||||
Text(String(peripheral.rssi) + " dB").font(.title3)
|
||||
}.padding([.bottom, .top])
|
||||
}
|
||||
}
|
||||
}.textCase(nil)
|
||||
|
||||
}
|
||||
.swipeActions {
|
||||
|
||||
Button(role: .destructive) {
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
||||
bleManager.disconnectPeripheral()
|
||||
isPreferredRadio = false
|
||||
}
|
||||
} label: {
|
||||
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
||||
}
|
||||
}
|
||||
.padding([.top, .bottom])
|
||||
|
||||
|
||||
} else {
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.red)
|
||||
.padding(.trailing)
|
||||
Text("No device connected").font(.title3)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
if bleManager.peripherals.count > 0 {
|
||||
Section(header: Text("Available Radios").font(.title)) {
|
||||
ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.name < $1.name })) { peripheral in
|
||||
HStack {
|
||||
Image(systemName: "circle.fill")
|
||||
.imageScale(.large).foregroundColor(.gray)
|
||||
.padding(.trailing)
|
||||
Button(action: {
|
||||
self.bleManager.stopScanning()
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
self.bleManager.disconnectPeripheral()
|
||||
}
|
||||
self.bleManager.connectTo(peripheral: peripheral.peripheral)
|
||||
if userSettings.preferredPeripheralId == peripheral.peripheral.identifier.uuidString {
|
||||
|
||||
isPreferredRadio = true
|
||||
} else {
|
||||
|
||||
isPreferredRadio = false
|
||||
}
|
||||
}) {
|
||||
Text(peripheral.name).font(.title3)
|
||||
}
|
||||
Spacer()
|
||||
Text(String(peripheral.rssi) + " dB").font(.title3)
|
||||
}.padding([.bottom, .top])
|
||||
}
|
||||
}.textCase(nil)
|
||||
}
|
||||
|
||||
} else {
|
||||
Text("Bluetooth: OFF")
|
||||
.foregroundColor(.red)
|
||||
.font(.title)
|
||||
}
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
|
||||
|
|
@ -250,11 +255,7 @@ struct Connect: View {
|
|||
}
|
||||
.padding(.bottom, 10)
|
||||
|
||||
} else {
|
||||
Text("Bluetooth: OFF")
|
||||
.foregroundColor(.red)
|
||||
.font(.title)
|
||||
}
|
||||
|
||||
}
|
||||
.navigationTitle("Bluetooth Radios")
|
||||
.navigationBarItems(trailing:
|
||||
|
|
@ -272,11 +273,11 @@ struct Connect: View {
|
|||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.onAppear(perform: {
|
||||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
|
||||
if initialLoad {
|
||||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
|
||||
// Ask for notification permission
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
|
||||
if success {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ struct ContentView: View {
|
|||
TabView(selection: $selection) {
|
||||
Contacts()
|
||||
.tabItem {
|
||||
Label("Messages", systemImage: "text.bubble")
|
||||
Label("Messages", systemImage: "message")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.symbolVariant(.none)
|
||||
|
||||
|
|
|
|||
115
Meshtastic/Views/Nodes/LocationHistory.swift
Normal file
115
Meshtastic/Views/Nodes/LocationHistory.swift
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// LocationHistory.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 7/5/22.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct LocationHistory: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
List {
|
||||
|
||||
ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
|
||||
VStack {
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.callout)
|
||||
|
||||
Text("Lat/Long:").font(.callout)
|
||||
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
|
||||
Image(systemName: "arrow.up.arrow.down.circle")
|
||||
.font(.callout)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Alt:")
|
||||
.font(.callout)
|
||||
|
||||
Text("\(String(mappin.altitude))m")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.font(.callout)
|
||||
DateTimeText(dateTime: mappin.time)
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline)
|
||||
Text("Lat/Long:").font(.caption)
|
||||
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
Image(systemName: "arrow.up.arrow.down.circle")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Alt:")
|
||||
.font(.caption)
|
||||
|
||||
Text("\(String(mappin.altitude))m")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.font(.caption)
|
||||
DateTimeText(dateTime: mappin.time)
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Location History \(node.positions?.count ?? 0) Points")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,8 @@ struct NodeDetail: View {
|
|||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
@State var initialLoad: Bool = true
|
||||
|
||||
@State private var isPresentingShutdownConfirm: Bool = false
|
||||
@State private var isPresentingRebootConfirm: Bool = false
|
||||
|
|
@ -28,8 +29,8 @@ struct NodeDetail: View {
|
|||
|
||||
VStack {
|
||||
|
||||
if node.positions?.count ?? 0 >= 1 {
|
||||
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
|
||||
let mostRecent = node.positions?.lastObject as! PositionEntity
|
||||
|
||||
if mostRecent.coordinate != nil {
|
||||
|
|
@ -51,43 +52,50 @@ struct NodeDetail: View {
|
|||
interactionModes: [.all],
|
||||
showsUserLocation: true,
|
||||
userTrackingMode: .constant(.follow),
|
||||
annotationItems: annotations
|
||||
|
||||
)
|
||||
annotationItems: annotations)
|
||||
{ location in
|
||||
|
||||
return MapAnnotation(
|
||||
coordinate: location.coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0),
|
||||
|
||||
content: {
|
||||
|
||||
NodeAnnotation(time: location.time!)
|
||||
}
|
||||
coordinate: location.coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0),
|
||||
content: {
|
||||
|
||||
NodeAnnotation(time: location.time!)
|
||||
}
|
||||
)
|
||||
}
|
||||
.frame(idealWidth: bounds.size.width, maxHeight: bounds.size.height / 2)
|
||||
|
||||
.ignoresSafeArea(.all, edges: [.leading, .trailing])
|
||||
.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
NavigationLink {
|
||||
LocationHistory(node: node)
|
||||
} label: {
|
||||
|
||||
Image(node.user?.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: bounds.size.width, height: bounds.size.height / 2)
|
||||
Image(systemName: "building.columns")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
Text("Position History \(node.positions?.count ?? 0) Points")
|
||||
.font(.title2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
Image(node.user?.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(10)
|
||||
.frame(width: bounds.size.width, height: bounds.size.height / 2)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ScrollView {
|
||||
|
||||
HStack {
|
||||
if self.bleManager.connectedPeripheral != nil && self.bleManager.connectedPeripheral.num == node.num && self.bleManager.connectedPeripheral.num == node.num {
|
||||
|
||||
if self.bleManager.connectedPeripheral != nil && self.bleManager.connectedPeripheral.num == node.num && self.bleManager.connectedPeripheral.num == node.num {
|
||||
HStack {
|
||||
|
||||
if hwModelString == "TBEAM" || hwModelString == "TECHO" || hwModelString.contains("4631") {
|
||||
|
||||
|
|
@ -108,7 +116,7 @@ struct NodeDetail: View {
|
|||
) {
|
||||
Button("Shutdown Node?", role: .destructive) {
|
||||
|
||||
if !bleManager.sendShutdown(destNum: node.num, wantResponse: false) {
|
||||
if !bleManager.sendShutdown(destNum: node.num, wantResponse: true) {
|
||||
|
||||
print("Shutdown Failed")
|
||||
}
|
||||
|
|
@ -129,215 +137,284 @@ struct NodeDetail: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingRebootConfirm
|
||||
) {
|
||||
|
||||
Button("Reboot Node?", role: .destructive) {
|
||||
|
||||
if !bleManager.sendReboot(destNum: node.num, wantResponse: false) {
|
||||
if !bleManager.sendReboot(destNum: node.num, wantResponse: true) {
|
||||
|
||||
print("Reboot Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
Divider()
|
||||
HStack {
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
LastHeardText(lastHeard: node.lastHeard).font(.title3)
|
||||
}
|
||||
.padding()
|
||||
Divider()
|
||||
|
||||
HStack {
|
||||
|
||||
VStack(alignment: .center) {
|
||||
Text("AKA").font(.title2).fixedSize()
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .accentColor)
|
||||
.offset(y: 10)
|
||||
}
|
||||
.padding(5)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack {
|
||||
|
||||
if node.user != nil {
|
||||
|
||||
Image(node.user!.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.frame(width: 50, height: 50)
|
||||
.cornerRadius(5)
|
||||
|
||||
Text(String(node.user!.hwModel ?? "UNSET"))
|
||||
.font(.callout).fixedSize()
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
// Add a divider if there is no map
|
||||
if (node.positions?.count ?? 0) == 0 {
|
||||
|
||||
Divider()
|
||||
}
|
||||
|
||||
if node.snr > 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Image(systemName: "waveform.path")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("SNR").font(.title2).fixedSize()
|
||||
Text(String(node.snr))
|
||||
.font(.title2)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
|
||||
if node.telemetries?.count ?? 0 >= 1 {
|
||||
|
||||
let mostRecent = node.telemetries?.lastObject as! TelemetryEntity
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .title, color: .accentColor)
|
||||
.padding(.bottom)
|
||||
|
||||
if mostRecent.batteryLevel > 0 {
|
||||
Text(String(mostRecent.batteryLevel) + "%")
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
if mostRecent.voltage > 0 {
|
||||
|
||||
Text(String(format: "%.2f", mostRecent.voltage) + " V")
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
}
|
||||
.padding(4)
|
||||
|
||||
Divider()
|
||||
|
||||
HStack(alignment: .center) {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "person")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("User Id:").font(.title2)
|
||||
}
|
||||
Text(node.user?.userId ?? "??????").font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "number")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Node Number:").font(.title2)
|
||||
}
|
||||
Text(String(node.num)).font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
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()
|
||||
|
||||
if node.positions?.count ?? 0 >= 1 {
|
||||
|
||||
Divider()
|
||||
|
||||
HStack {
|
||||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Text("AKA").font(.largeTitle)
|
||||
.foregroundColor(.gray).fixedSize()
|
||||
.offset(y:20)
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .accentColor, circleSize: 75, fontSize: 26)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Image(systemName: "location.circle.fill")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Location History").font(.title2)
|
||||
Divider()
|
||||
|
||||
VStack {
|
||||
|
||||
if node.user != nil {
|
||||
|
||||
Image(hwModelString)
|
||||
.resizable()
|
||||
.frame(width: 90, height: 90)
|
||||
.cornerRadius(5)
|
||||
|
||||
Text(String(hwModelString))
|
||||
.foregroundColor(.gray)
|
||||
.font(.largeTitle).fixedSize()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
||||
if node.snr > 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Image(systemName: "waveform.path")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.padding(.bottom, 10)
|
||||
Text("SNR").font(.largeTitle).fixedSize()
|
||||
Text(String(node.snr))
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
if node.telemetries?.count ?? 0 >= 1 {
|
||||
|
||||
let mostRecent = node.telemetries?.lastObject as! TelemetryEntity
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .largeTitle, color: .accentColor)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
if mostRecent.batteryLevel > 0 {
|
||||
Text(String(mostRecent.batteryLevel) + "%")
|
||||
.font(.largeTitle)
|
||||
.frame(width: 100)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
if mostRecent.voltage > 0 {
|
||||
|
||||
Text(String(format: "%.2f", mostRecent.voltage) + " V")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
Divider()
|
||||
|
||||
ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
|
||||
if mappin.coordinate != nil {
|
||||
|
||||
VStack {
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline)
|
||||
Text("Lat/Long:").font(.caption)
|
||||
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
|
||||
|
||||
|
||||
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
Image(systemName: "arrow.up.arrow.down.circle")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
Text("Alt:")
|
||||
.font(.caption)
|
||||
|
||||
Text("\(String(mappin.altitude))m")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.font(.caption)
|
||||
DateTimeText(dateTime: mappin.time)
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
Divider()
|
||||
}
|
||||
HStack(alignment: .center) {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "person")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("User Id:").font(.title)
|
||||
}
|
||||
Text(node.user?.userId ?? "??????").font(.title).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "number")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Node Number:").font(.title)
|
||||
}
|
||||
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")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Last Heard: ").font(.title)
|
||||
|
||||
}
|
||||
DateTimeText(dateTime: node.lastHeard)
|
||||
.font(.title)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
Divider()
|
||||
|
||||
} else {
|
||||
|
||||
HStack {
|
||||
|
||||
VStack(alignment: .center) {
|
||||
Text("AKA").font(.title2).fixedSize()
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .accentColor)
|
||||
.offset(y: 10)
|
||||
}
|
||||
.padding(5)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack {
|
||||
|
||||
if node.user != nil {
|
||||
|
||||
Image(node.user!.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.frame(width: 50, height: 50)
|
||||
.cornerRadius(5)
|
||||
|
||||
Text(String(node.user!.hwModel ?? "UNSET"))
|
||||
.font(.callout).fixedSize()
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
|
||||
|
||||
if node.snr > 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Image(systemName: "waveform.path")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("SNR").font(.title2).fixedSize()
|
||||
Text(String(node.snr))
|
||||
.font(.title2)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
|
||||
if node.telemetries?.count ?? 0 >= 1 {
|
||||
|
||||
let mostRecent = node.telemetries?.lastObject as! TelemetryEntity
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .title, color: .accentColor)
|
||||
.padding(.bottom)
|
||||
|
||||
if mostRecent.batteryLevel > 0 {
|
||||
Text(String(mostRecent.batteryLevel) + "%")
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
if mostRecent.voltage > 0 {
|
||||
|
||||
Text(String(format: "%.2f", mostRecent.voltage) + " V")
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
}
|
||||
}
|
||||
.padding(4)
|
||||
|
||||
HStack(alignment: .center) {
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "person")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("User Id:").font(.title2)
|
||||
}
|
||||
Text(node.user?.userId ?? "??????").font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "number")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Node Number:").font(.title2)
|
||||
}
|
||||
Text(String(node.num)).font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.padding(5)
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
.edgesIgnoringSafeArea([.leading, .trailing])
|
||||
.padding(1)
|
||||
}
|
||||
}
|
||||
.navigationTitle((node.user != nil) ? String(node.user!.longName ?? "Unknown") : "Unknown")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarTitleDisplayMode(.automatic)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
|
@ -348,12 +425,15 @@ struct NodeDetail: View {
|
|||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
}
|
||||
)
|
||||
.onAppear(perform: {
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,16 @@ struct NodeList: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
@State var initialLoad: Bool = true
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
|
||||
animation: .default)
|
||||
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
||||
@State private var selection: String?
|
||||
@State private var selection: String? = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -102,13 +104,17 @@ struct NodeList: View {
|
|||
.navigationTitle("All Nodes")
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
if initialLoad {
|
||||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
self.initialLoad = false
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
if nodes.count > 0 {
|
||||
selection = "0"
|
||||
if nodes.count > 0 {
|
||||
selection = "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import CoreLocation
|
|||
struct AdminMessageList: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var user: UserEntity?
|
||||
|
|
@ -38,10 +37,7 @@ struct AdminMessageList: View {
|
|||
Image(systemName: "checkmark.square")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
Text("Acknowledged: ")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
DateTimeText(dateTime: Date(timeIntervalSince1970: TimeInterval(am.messageTimestamp)))
|
||||
Text("Acknowledged: \(Date(timeIntervalSince1970: TimeInterval(am.messageTimestamp)), style: .time)")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
|
|
@ -49,7 +45,7 @@ struct AdminMessageList: View {
|
|||
Image(systemName: "square")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
Text("Acknowledged")
|
||||
Text("Not Acknowledged")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
|
|
@ -57,7 +53,7 @@ struct AdminMessageList: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Admin Message History")
|
||||
.navigationTitle("Admin Message Log")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
|
|
|||
|
|
@ -119,28 +119,8 @@ struct AppSettings: View {
|
|||
.foregroundColor(.gray)
|
||||
|
||||
}
|
||||
Section(header: Text("OPTIONS")) {
|
||||
Section(header: Text("Options")) {
|
||||
|
||||
Toggle(isOn: $userSettings.provideLocation) {
|
||||
|
||||
Label("Provide location to mesh", systemImage: "location.circle.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
if userSettings.provideLocation {
|
||||
|
||||
Picker(" Update Interval", selection: $userSettings.provideLocationInterval) {
|
||||
ForEach(LocationUpdateInterval.allCases) { lu in
|
||||
Text(lu.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
Text("How frequently your phone will send your location to the device, location updates to the mesh are managed by the device.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
|
||||
Picker("Keyboard Type", selection: $userSettings.keyboardType) {
|
||||
ForEach(KeyboardType.allCases) { kb in
|
||||
Text(kb.description)
|
||||
|
|
@ -156,6 +136,28 @@ struct AppSettings: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
}
|
||||
|
||||
Section(header: Text("Phone GPS")) {
|
||||
|
||||
Toggle(isOn: $userSettings.provideLocation) {
|
||||
|
||||
Label("Provide location to mesh", systemImage: "location.circle.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
if userSettings.provideLocation {
|
||||
|
||||
Picker(" Update Interval", selection: $userSettings.provideLocationInterval) {
|
||||
ForEach(LocationUpdateInterval.allCases) { lu in
|
||||
Text(lu.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
Text("How frequently your phone will send your location to the device, location updates to the mesh are managed by the device.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("App Settings")
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ struct DeviceConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingFactoryResetConfirm: Bool = false
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
|
|
@ -124,7 +124,7 @@ struct DeviceConfig: View {
|
|||
dc.serialDisabled = !serialEnabled
|
||||
dc.debugLogEnabled = debugLogEnabled
|
||||
|
||||
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ struct DeviceConfig: View {
|
|||
) {
|
||||
Button("Erase all device settings?", role: .destructive) {
|
||||
|
||||
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: true) {
|
||||
|
||||
print("Factory Reset Failed")
|
||||
}
|
||||
|
|
@ -176,33 +176,24 @@ struct DeviceConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.deviceRole = Int(node.deviceConfig?.role ?? 0)
|
||||
self.serialEnabled = (node.deviceConfig?.serialEnabled ?? true)
|
||||
self.debugLogEnabled = node.deviceConfig?.debugLogEnabled ?? false
|
||||
self.deviceRole = Int(node!.deviceConfig?.role ?? 0)
|
||||
self.serialEnabled = (node!.deviceConfig?.serialEnabled ?? true)
|
||||
self.debugLogEnabled = node!.deviceConfig?.debugLogEnabled ?? false
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceRole) { newRole in
|
||||
|
||||
if newRole != node.deviceConfig!.role {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newRole != node!.deviceConfig!.role { hasChanges = true }
|
||||
}
|
||||
.onChange(of: serialEnabled) { newSerial in
|
||||
|
||||
if newSerial != node.deviceConfig!.serialEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newSerial != node!.deviceConfig!.serialEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: debugLogEnabled) { newDebugLog in
|
||||
|
||||
if newDebugLog != node.deviceConfig!.debugLogEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newDebugLog != node!.deviceConfig!.debugLogEnabled { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,22 +58,18 @@ enum GpsFormats: Int, CaseIterable, Identifiable {
|
|||
// Default of 0 is One Minute
|
||||
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case fifteenSeconds = 15
|
||||
case thirtySeconds = 30
|
||||
case oneMinute = 0
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 600
|
||||
case tenMinutes = 0
|
||||
case fifteenMinutes = 900
|
||||
case max = 2147483647
|
||||
case thirtyMinutes = 1800
|
||||
case oneHour = 3600
|
||||
case max = 31536000 // One Year
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .fifteenSeconds:
|
||||
return "Fifteen Seconds"
|
||||
case .thirtySeconds:
|
||||
return "Thirty Seconds"
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .fiveMinutes:
|
||||
|
|
@ -82,6 +78,10 @@ enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
|||
return "Ten Minutes"
|
||||
case .fifteenMinutes:
|
||||
return "Fifteen Minutes"
|
||||
case .thirtyMinutes:
|
||||
return "Thirty Minutes"
|
||||
case .oneHour:
|
||||
return "One Hour"
|
||||
case .max:
|
||||
return "Always On"
|
||||
}
|
||||
|
|
@ -125,7 +125,8 @@ struct DisplayConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
|
@ -203,7 +204,7 @@ struct DisplayConfig: View {
|
|||
dc.screenOnSecs = UInt32(screenOnSeconds)
|
||||
dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval)
|
||||
|
||||
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -230,30 +231,30 @@ struct DisplayConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.gpsFormat = Int(node.displayConfig?.gpsFormat ?? 0)
|
||||
self.screenOnSeconds = Int(node.displayConfig?.screenOnSeconds ?? 0)
|
||||
self.screenCarouselInterval = Int(node.displayConfig?.screenCarouselInterval ?? 0)
|
||||
self.gpsFormat = Int(node!.displayConfig?.gpsFormat ?? 0)
|
||||
self.screenOnSeconds = Int(node!.displayConfig?.screenOnSeconds ?? 0)
|
||||
self.screenCarouselInterval = Int(node!.displayConfig?.screenCarouselInterval ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: screenOnSeconds) { newScreenSecs in
|
||||
|
||||
if newScreenSecs != node.displayConfig!.screenOnSeconds {
|
||||
if newScreenSecs != node!.displayConfig!.screenOnSeconds {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: screenCarouselInterval) { newCarouselSecs in
|
||||
|
||||
if newCarouselSecs != node.displayConfig!.screenCarouselInterval {
|
||||
if newCarouselSecs != node!.displayConfig!.screenCarouselInterval {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: gpsFormat) { newGpsFormat in
|
||||
|
||||
if newGpsFormat != node.displayConfig!.gpsFormat {
|
||||
if newGpsFormat != node!.displayConfig!.gpsFormat {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,8 +96,8 @@ enum ModemPresets : Int, CaseIterable, Identifiable {
|
|||
case LongFast = 0
|
||||
case LongSlow = 1
|
||||
case VLongSlow = 2
|
||||
case MidSlow = 3
|
||||
case MidFast = 4
|
||||
case MedSlow = 3
|
||||
case MedFast = 4
|
||||
case ShortSlow = 5
|
||||
case ShortFast = 6
|
||||
|
||||
|
|
@ -112,9 +112,9 @@ enum ModemPresets : Int, CaseIterable, Identifiable {
|
|||
return "Long Range - Slow"
|
||||
case .VLongSlow:
|
||||
return "Very Long Range - Slow"
|
||||
case .MidSlow:
|
||||
case .MedSlow:
|
||||
return "Medium Range - Slow"
|
||||
case .MidFast:
|
||||
case .MedFast:
|
||||
return "Medium Range - Fast"
|
||||
case .ShortSlow:
|
||||
return "Short Range - Slow"
|
||||
|
|
@ -133,9 +133,9 @@ enum ModemPresets : Int, CaseIterable, Identifiable {
|
|||
return Config.LoRaConfig.ModemPreset.longSlow
|
||||
case .VLongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.vlongSlow
|
||||
case .MidSlow:
|
||||
case .MedSlow:
|
||||
return Config.LoRaConfig.ModemPreset.medSlow
|
||||
case .MidFast:
|
||||
case .MedFast:
|
||||
return Config.LoRaConfig.ModemPreset.medFast
|
||||
case .ShortSlow:
|
||||
return Config.LoRaConfig.ModemPreset.shortSlow
|
||||
|
|
@ -185,7 +185,7 @@ struct LoRaConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -263,7 +263,7 @@ struct LoRaConfig: View {
|
|||
lc.region = RegionCodes(rawValue: region)!.protoEnumValue()
|
||||
lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue()
|
||||
|
||||
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -290,35 +290,29 @@ struct LoRaConfig: View {
|
|||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
self.hopLimit = Int(node.loRaConfig?.hopLimit ?? 0)
|
||||
self.region = Int(node.loRaConfig?.regionCode ?? 0)
|
||||
self.modemPreset = Int(node.loRaConfig?.modemPreset ?? 0)
|
||||
self.hopLimit = Int(node!.loRaConfig?.hopLimit ?? 0)
|
||||
self.region = Int(node!.loRaConfig?.regionCode ?? 0)
|
||||
self.modemPreset = Int(node!.loRaConfig?.modemPreset ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: region) { newRegion in
|
||||
|
||||
if node.loRaConfig != nil {
|
||||
|
||||
if newRegion != node.loRaConfig!.regionCode {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if node!.loRaConfig != nil {
|
||||
if newRegion != node!.loRaConfig!.regionCode { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: modemPreset) { newModemPreset in
|
||||
|
||||
if newModemPreset != node.loRaConfig!.modemPreset {
|
||||
|
||||
hasChanges = true
|
||||
if node!.loRaConfig != nil {
|
||||
if newModemPreset != node!.loRaConfig!.modemPreset { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: hopLimit) { newHopLimit in
|
||||
|
||||
if newHopLimit != node.loRaConfig!.hopLimit {
|
||||
|
||||
hasChanges = true
|
||||
if node!.loRaConfig != nil {
|
||||
if newHopLimit != node!.loRaConfig!.hopLimit { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ enum ConfigPresets : Int, CaseIterable, Identifiable {
|
|||
case rakRotaryEncoder = 1
|
||||
case tbeamThreeButtonScreen = 2
|
||||
case cardKB = 3
|
||||
case facesKB = 4
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
|
|
@ -26,7 +27,9 @@ enum ConfigPresets : Int, CaseIterable, Identifiable {
|
|||
case .tbeamThreeButtonScreen:
|
||||
return "TBEAM 3 Button OLED Screen"
|
||||
case .cardKB:
|
||||
return "Card KB"
|
||||
return "M5 Stack Card KeyBoard"
|
||||
case .facesKB:
|
||||
return "M5 Stack Faces KeyBoard"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,7 +100,7 @@ struct CannedMessagesConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -260,7 +263,6 @@ struct CannedMessagesConfig: View {
|
|||
}
|
||||
.disabled(configPreset > 0)
|
||||
}
|
||||
.disabled(!(node.myInfo?.hasWifi ?? false))
|
||||
|
||||
Button {
|
||||
|
||||
|
|
@ -270,7 +272,7 @@ struct CannedMessagesConfig: 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)
|
||||
|
|
@ -290,7 +292,7 @@ struct CannedMessagesConfig: View {
|
|||
if rotary1Enabled {
|
||||
|
||||
/// Input event origin accepted by the canned messages
|
||||
/// Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any"
|
||||
/// Can be e.g. "rotEnc1", "upDownEnc1", "cardkb", "faceskb" 623or keyword "_any"
|
||||
cmc.allowInputSource = "rotEnc1"
|
||||
|
||||
} else if updown1Enabled {
|
||||
|
|
@ -308,7 +310,7 @@ struct CannedMessagesConfig: View {
|
|||
cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue()
|
||||
cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue()
|
||||
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -331,13 +333,13 @@ struct CannedMessagesConfig: View {
|
|||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
self.enabled = node.cannedMessageConfig?.enabled ?? false
|
||||
self.sendBell = node.cannedMessageConfig?.sendBell ?? false
|
||||
self.rotary1Enabled = node.cannedMessageConfig?.rotary1Enabled ?? false
|
||||
self.updown1Enabled = node.cannedMessageConfig?.updown1Enabled ?? false
|
||||
self.inputbrokerPinA = Int(node.cannedMessageConfig?.inputbrokerPinA ?? 0)
|
||||
self.inputbrokerPinB = Int(node.cannedMessageConfig?.inputbrokerPinB ?? 0)
|
||||
self.inputbrokerPinPress = Int(node.cannedMessageConfig?.inputbrokerPinPress ?? 0)
|
||||
self.enabled = node!.cannedMessageConfig?.enabled ?? false
|
||||
self.sendBell = node!.cannedMessageConfig?.sendBell ?? false
|
||||
self.rotary1Enabled = node!.cannedMessageConfig?.rotary1Enabled ?? false
|
||||
self.updown1Enabled = node!.cannedMessageConfig?.updown1Enabled ?? false
|
||||
self.inputbrokerPinA = Int(node!.cannedMessageConfig?.inputbrokerPinA ?? 0)
|
||||
self.inputbrokerPinB = Int(node!.cannedMessageConfig?.inputbrokerPinB ?? 0)
|
||||
self.inputbrokerPinPress = Int(node!.cannedMessageConfig?.inputbrokerPinPress ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
|
|
@ -361,35 +363,35 @@ struct CannedMessagesConfig: View {
|
|||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if newEnabled != node.cannedMessageConfig!.enabled { hasChanges = true }
|
||||
if newEnabled != node!.cannedMessageConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: sendBell) { newBell in
|
||||
|
||||
if newBell != node.cannedMessageConfig!.sendBell { hasChanges = true }
|
||||
if newBell != node!.cannedMessageConfig!.sendBell { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerPinA) { newPinA in
|
||||
|
||||
if newPinA != node.cannedMessageConfig!.inputbrokerPinA { hasChanges = true }
|
||||
if newPinA != node!.cannedMessageConfig!.inputbrokerPinA { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerPinB) { newPinB in
|
||||
|
||||
if newPinB != node.cannedMessageConfig!.inputbrokerPinB { hasChanges = true }
|
||||
if newPinB != node!.cannedMessageConfig!.inputbrokerPinB { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerPinPress) { newPinPress in
|
||||
|
||||
if newPinPress != node.cannedMessageConfig!.inputbrokerPinPress { hasChanges = true }
|
||||
if newPinPress != node!.cannedMessageConfig!.inputbrokerPinPress { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerEventCw) { newKeyA in
|
||||
|
||||
if newKeyA != node.cannedMessageConfig!.inputbrokerEventCw { hasChanges = true }
|
||||
if newKeyA != node!.cannedMessageConfig!.inputbrokerEventCw { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerEventCcw) { newKeyB in
|
||||
|
||||
if newKeyB != node.cannedMessageConfig!.inputbrokerEventCcw { hasChanges = true }
|
||||
if newKeyB != node!.cannedMessageConfig!.inputbrokerEventCcw { hasChanges = true }
|
||||
}
|
||||
.onChange(of: inputbrokerEventPress) { newKeyPress in
|
||||
|
||||
if newKeyPress != node.cannedMessageConfig!.inputbrokerEventPress { hasChanges = true }
|
||||
if newKeyPress != node!.cannedMessageConfig!.inputbrokerEventPress { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ struct ExternalNotificationConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -138,7 +138,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 || !(node!.myInfo?.hasWifi ?? false))
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
|
|
@ -158,7 +158,7 @@ struct ExternalNotificationConfig: View {
|
|||
enc.output = UInt32(output)
|
||||
enc.outputMs = UInt32(outputMilliseconds)
|
||||
|
||||
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0{
|
||||
|
||||
|
|
@ -185,12 +185,12 @@ struct ExternalNotificationConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.enabled = node.externalNotificationConfig?.enabled ?? false
|
||||
self.alertBell = node.externalNotificationConfig?.alertBell ?? false
|
||||
self.alertMessage = node.externalNotificationConfig?.alertMessage ?? false
|
||||
self.active = node.externalNotificationConfig?.active ?? false
|
||||
self.output = Int(node.externalNotificationConfig?.output ?? 0)
|
||||
self.outputMilliseconds = Int(node.externalNotificationConfig?.outputMilliseconds ?? 0)
|
||||
self.enabled = node!.externalNotificationConfig?.enabled ?? false
|
||||
self.alertBell = node!.externalNotificationConfig?.alertBell ?? false
|
||||
self.alertMessage = node!.externalNotificationConfig?.alertMessage ?? false
|
||||
self.active = node!.externalNotificationConfig?.active ?? false
|
||||
self.output = Int(node!.externalNotificationConfig?.output ?? 0)
|
||||
self.outputMilliseconds = Int(node!.externalNotificationConfig?.outputMilliseconds ?? 0)
|
||||
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
|
|
@ -198,27 +198,27 @@ struct ExternalNotificationConfig: View {
|
|||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if newEnabled != node.externalNotificationConfig!.enabled { hasChanges = true }
|
||||
if newEnabled != node!.externalNotificationConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: alertBell) { newAlertBell in
|
||||
|
||||
if newAlertBell != node.externalNotificationConfig!.alertBell { hasChanges = true }
|
||||
if newAlertBell != node!.externalNotificationConfig!.alertBell { hasChanges = true }
|
||||
}
|
||||
.onChange(of: alertMessage) { newAlertMessage in
|
||||
|
||||
if newAlertMessage != node.externalNotificationConfig!.alertMessage { hasChanges = true }
|
||||
if newAlertMessage != node!.externalNotificationConfig!.alertMessage { hasChanges = true }
|
||||
}
|
||||
.onChange(of: active) { newActuve in
|
||||
|
||||
if newActuve != node.externalNotificationConfig!.active { hasChanges = true }
|
||||
if newActuve != node!.externalNotificationConfig!.active { hasChanges = true }
|
||||
}
|
||||
.onChange(of: output) { newOutput in
|
||||
|
||||
if newOutput != node.externalNotificationConfig!.output { hasChanges = true }
|
||||
if newOutput != node!.externalNotificationConfig!.output { hasChanges = true }
|
||||
}
|
||||
.onChange(of: outputMilliseconds) { newOutputMs in
|
||||
|
||||
if newOutputMs != node.externalNotificationConfig!.outputMilliseconds { hasChanges = true }
|
||||
if newOutputMs != node!.externalNotificationConfig!.outputMilliseconds { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ struct RangeTestConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -80,13 +80,13 @@ struct RangeTestConfig: View {
|
|||
Label("Save", systemImage: "square.and.arrow.down.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.disabled(!(node.myInfo?.hasWifi ?? false))
|
||||
.disabled(!(node!.myInfo?.hasWifi ?? false))
|
||||
|
||||
Text("Saves a CSV with the range test message details, currently only available on ESP32 devices with a web server.")
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.disabled(!(node.myInfo?.hasWifi ?? false))
|
||||
.disabled(!(node!.myInfo?.hasWifi ?? false))
|
||||
|
||||
Button {
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ struct RangeTestConfig: View {
|
|||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges || !(node.myInfo?.hasWifi ?? false))
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges || !(node!.myInfo?.hasWifi ?? false))
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
|
|
@ -113,7 +113,7 @@ struct RangeTestConfig: View {
|
|||
rtc.save = save
|
||||
rtc.sender = UInt32(sender)
|
||||
|
||||
let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -139,33 +139,24 @@ struct RangeTestConfig: View {
|
|||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
self.enabled = node.rangeTestConfig?.enabled ?? false
|
||||
self.save = node.rangeTestConfig?.save ?? false
|
||||
self.sender = Int(node.rangeTestConfig?.sender ?? 0)
|
||||
self.enabled = node!.rangeTestConfig?.enabled ?? false
|
||||
self.save = node!.rangeTestConfig?.save ?? false
|
||||
self.sender = Int(node!.rangeTestConfig?.sender ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if newEnabled != node.rangeTestConfig!.enabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newEnabled != node!.rangeTestConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: save) { newSave in
|
||||
|
||||
if newSave != node.rangeTestConfig!.save {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newSave != node!.rangeTestConfig!.save { hasChanges = true }
|
||||
}
|
||||
.onChange(of: sender) { newSender in
|
||||
|
||||
if newSender != node.rangeTestConfig!.sender {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newSender != node!.rangeTestConfig!.sender { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ struct SerialConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -302,7 +302,7 @@ struct SerialConfig: View {
|
|||
sc.timeout = UInt32(timeout)
|
||||
sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue()
|
||||
|
||||
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -329,13 +329,13 @@ struct SerialConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.enabled = node.serialConfig?.enabled ?? false
|
||||
self.echo = node.serialConfig?.echo ?? false
|
||||
self.rxd = Int(node.serialConfig?.rxd ?? 0)
|
||||
self.txd = Int(node.serialConfig?.txd ?? 0)
|
||||
self.baudRate = Int(node.serialConfig?.baudRate ?? 0)
|
||||
self.timeout = Int(node.serialConfig?.timeout ?? 0)
|
||||
self.mode = Int(node.serialConfig?.mode ?? 0)
|
||||
self.enabled = node!.serialConfig?.enabled ?? false
|
||||
self.echo = node!.serialConfig?.echo ?? false
|
||||
self.rxd = Int(node!.serialConfig?.rxd ?? 0)
|
||||
self.txd = Int(node!.serialConfig?.txd ?? 0)
|
||||
self.baudRate = Int(node!.serialConfig?.baudRate ?? 0)
|
||||
self.timeout = Int(node!.serialConfig?.timeout ?? 0)
|
||||
self.mode = Int(node!.serialConfig?.mode ?? 0)
|
||||
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
|
|
@ -343,31 +343,31 @@ struct SerialConfig: View {
|
|||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if newEnabled != node.serialConfig!.enabled { hasChanges = true }
|
||||
if newEnabled != node!.serialConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: echo) { newEcho in
|
||||
|
||||
if newEcho != node.serialConfig!.echo { hasChanges = true }
|
||||
if newEcho != node!.serialConfig!.echo { hasChanges = true }
|
||||
}
|
||||
.onChange(of: rxd) { newRxd in
|
||||
|
||||
if newRxd != node.serialConfig!.rxd { hasChanges = true }
|
||||
if newRxd != node!.serialConfig!.rxd { hasChanges = true }
|
||||
}
|
||||
.onChange(of: txd) { newTxd in
|
||||
|
||||
if newTxd != node.serialConfig!.txd { hasChanges = true }
|
||||
if newTxd != node!.serialConfig!.txd { hasChanges = true }
|
||||
}
|
||||
.onChange(of: baudRate) { newBaud in
|
||||
|
||||
if newBaud != node.serialConfig!.baudRate { hasChanges = true }
|
||||
if newBaud != node!.serialConfig!.baudRate { hasChanges = true }
|
||||
}
|
||||
.onChange(of: timeout) { newTimeout in
|
||||
|
||||
if newTimeout != node.serialConfig!.timeout { hasChanges = true }
|
||||
if newTimeout != node!.serialConfig!.timeout { hasChanges = true }
|
||||
}
|
||||
.onChange(of: mode) { newMode in
|
||||
|
||||
if newMode != node.serialConfig!.mode { hasChanges = true }
|
||||
if newMode != node!.serialConfig!.mode { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ struct TelemetryConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
|
|
@ -289,7 +289,7 @@ struct TelemetryConfig: 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)
|
||||
|
|
@ -311,7 +311,7 @@ struct TelemetryConfig: View {
|
|||
tc.environmentRecoveryInterval = UInt32(environmentRecoveryInterval)
|
||||
tc.environmentReadErrorCountThreshold = UInt32(environmentReadErrorCountThreshold)
|
||||
|
||||
let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -338,14 +338,14 @@ struct TelemetryConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.deviceUpdateInterval = Int(node.telemetryConfig?.deviceUpdateInterval ?? 0)
|
||||
self.environmentUpdateInterval = Int(node.telemetryConfig?.environmentUpdateInterval ?? 0)
|
||||
self.environmentMeasurementEnabled = node.telemetryConfig?.environmentMeasurementEnabled ?? false
|
||||
self.environmentSensorType = Int(node.telemetryConfig?.environmentSensorType ?? 0)
|
||||
self.environmentScreenEnabled = node.telemetryConfig?.environmentScreenEnabled ?? false
|
||||
self.environmentDisplayFahrenheit = node.telemetryConfig?.environmentDisplayFahrenheit ?? false
|
||||
self.environmentRecoveryInterval = Int(node.telemetryConfig?.environmentRecoveryInterval ?? 0)
|
||||
self.environmentReadErrorCountThreshold = Int(node.telemetryConfig?.environmentReadErrorCountThreshold ?? 0)
|
||||
self.deviceUpdateInterval = Int(node!.telemetryConfig?.deviceUpdateInterval ?? 0)
|
||||
self.environmentUpdateInterval = Int(node!.telemetryConfig?.environmentUpdateInterval ?? 0)
|
||||
self.environmentMeasurementEnabled = node!.telemetryConfig?.environmentMeasurementEnabled ?? false
|
||||
self.environmentSensorType = Int(node!.telemetryConfig?.environmentSensorType ?? 0)
|
||||
self.environmentScreenEnabled = node!.telemetryConfig?.environmentScreenEnabled ?? false
|
||||
self.environmentDisplayFahrenheit = node!.telemetryConfig?.environmentDisplayFahrenheit ?? false
|
||||
self.environmentRecoveryInterval = Int(node!.telemetryConfig?.environmentRecoveryInterval ?? 0)
|
||||
self.environmentReadErrorCountThreshold = Int(node!.telemetryConfig?.environmentReadErrorCountThreshold ?? 0)
|
||||
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
|
|
@ -353,35 +353,35 @@ struct TelemetryConfig: View {
|
|||
}
|
||||
.onChange(of: deviceUpdateInterval) { newDeviceInterval in
|
||||
|
||||
if newDeviceInterval != node.telemetryConfig!.deviceUpdateInterval { hasChanges = true }
|
||||
if newDeviceInterval != node!.telemetryConfig!.deviceUpdateInterval { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentUpdateInterval) { newEnvInterval in
|
||||
|
||||
if newEnvInterval != node.telemetryConfig!.environmentUpdateInterval { hasChanges = true }
|
||||
if newEnvInterval != node!.telemetryConfig!.environmentUpdateInterval { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentMeasurementEnabled) { newEnvEnabled in
|
||||
|
||||
if newEnvEnabled != node.telemetryConfig!.environmentMeasurementEnabled { hasChanges = true }
|
||||
if newEnvEnabled != node!.telemetryConfig!.environmentMeasurementEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentSensorType) { newEnvSensorType in
|
||||
|
||||
if newEnvSensorType != node.telemetryConfig!.environmentSensorType { hasChanges = true }
|
||||
if newEnvSensorType != node!.telemetryConfig!.environmentSensorType { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentScreenEnabled) { newEnvScreenEnabled in
|
||||
|
||||
if newEnvScreenEnabled != node.telemetryConfig!.environmentScreenEnabled { hasChanges = true }
|
||||
if newEnvScreenEnabled != node!.telemetryConfig!.environmentScreenEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentDisplayFahrenheit) { newEnvDisplayF in
|
||||
|
||||
if newEnvDisplayF != node.telemetryConfig!.environmentDisplayFahrenheit { hasChanges = true }
|
||||
if newEnvDisplayF != node!.telemetryConfig!.environmentDisplayFahrenheit { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentRecoveryInterval) { newEnvRecoveryInterval in
|
||||
|
||||
if newEnvRecoveryInterval != node.telemetryConfig!.environmentRecoveryInterval { hasChanges = true }
|
||||
if newEnvRecoveryInterval != node!.telemetryConfig!.environmentRecoveryInterval { hasChanges = true }
|
||||
}
|
||||
.onChange(of: environmentReadErrorCountThreshold) { newEnvReadErrorCountThreshold in
|
||||
|
||||
if newEnvReadErrorCountThreshold != node.telemetryConfig!.environmentReadErrorCountThreshold { hasChanges = true }
|
||||
if newEnvReadErrorCountThreshold != node!.telemetryConfig!.environmentReadErrorCountThreshold { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ struct PositionConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
|
@ -282,7 +283,7 @@ struct PositionConfig: View {
|
|||
pc.gpsAttemptTime = UInt32(gpsAttemptTime)
|
||||
pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds)
|
||||
|
||||
let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0{
|
||||
|
||||
|
|
@ -308,36 +309,27 @@ struct PositionConfig: View {
|
|||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
self.smartPositionEnabled = node.positionConfig?.smartPositionEnabled ?? true
|
||||
self.deviceGpsEnabled = node.positionConfig?.deviceGpsEnabled ?? true
|
||||
self.fixedPosition = node.positionConfig?.fixedPosition ?? false
|
||||
self.gpsUpdateInterval = Int(node.positionConfig?.gpsUpdateInterval ?? 0)
|
||||
self.gpsAttemptTime = Int(node.positionConfig?.gpsAttemptTime ?? 0)
|
||||
self.positionBroadcastSeconds = Int(node.positionConfig?.positionBroadcastSeconds ?? 0)
|
||||
self.smartPositionEnabled = node!.positionConfig?.smartPositionEnabled ?? true
|
||||
self.deviceGpsEnabled = node!.positionConfig?.deviceGpsEnabled ?? true
|
||||
self.fixedPosition = node!.positionConfig?.fixedPosition ?? false
|
||||
self.gpsUpdateInterval = Int(node!.positionConfig?.gpsUpdateInterval ?? 0)
|
||||
self.gpsAttemptTime = Int(node!.positionConfig?.gpsAttemptTime ?? 0)
|
||||
self.positionBroadcastSeconds = Int(node!.positionConfig?.positionBroadcastSeconds ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: smartPositionEnabled) { newSmartPosition in
|
||||
|
||||
if newSmartPosition != node.positionConfig!.smartPositionEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newSmartPosition != node!.positionConfig!.smartPositionEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: deviceGpsEnabled) { newDeviceGps in
|
||||
|
||||
if newDeviceGps != node.positionConfig!.deviceGpsEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newDeviceGps != node!.positionConfig!.deviceGpsEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: fixedPosition) { newFixed in
|
||||
|
||||
if newFixed != node.positionConfig!.fixedPosition {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
if newFixed != node!.positionConfig!.fixedPosition { hasChanges = true }
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ struct MeshLog: View {
|
|||
|
||||
} label: {
|
||||
|
||||
Label("Download Log", systemImage: "arrow.down.circle.fill")
|
||||
Label("Save Log", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
|
|
|
|||
|
|
@ -19,18 +19,17 @@ struct Settings: View {
|
|||
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
||||
@State private var selection: String? = ""
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
List {
|
||||
|
||||
let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0
|
||||
|
||||
NavigationLink(tag: String("0"), selection: $selection) {
|
||||
NavigationLink() {
|
||||
AppSettings()
|
||||
} label: {
|
||||
|
||||
|
||||
Image(systemName: "gearshape")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("App Settings")
|
||||
|
|
@ -39,7 +38,7 @@ struct Settings: View {
|
|||
Section("Radio Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
ShareChannel(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
ShareChannel(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
Image(systemName: "qrcode")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
|
@ -48,7 +47,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
UserConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
UserConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "person.crop.rectangle.fill")
|
||||
|
|
@ -60,7 +59,7 @@ struct Settings: View {
|
|||
|
||||
NavigationLink() {
|
||||
|
||||
LoRaConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
LoRaConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
|
|
@ -71,7 +70,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "flipphone")
|
||||
|
|
@ -81,7 +80,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
DisplayConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
DisplayConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "display")
|
||||
|
|
@ -91,7 +90,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "location")
|
||||
|
|
@ -108,7 +107,7 @@ struct Settings: View {
|
|||
Section("Module Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
CannedMessagesConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
CannedMessagesConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "list.bullet.rectangle.fill")
|
||||
|
|
@ -119,7 +118,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
ExternalNotificationConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
ExternalNotificationConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "megaphone")
|
||||
|
|
@ -130,7 +129,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "point.3.connected.trianglepath.dotted")
|
||||
|
|
@ -139,11 +138,9 @@ struct Settings: View {
|
|||
Text("Range Test")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
//nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true)//||
|
||||
// nodes.first(where: { $0.num == connectedNodeNum })!.rangeTestConfig != nil)
|
||||
|
||||
NavigationLink {
|
||||
SerialConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
SerialConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "terminal")
|
||||
|
|
@ -155,7 +152,7 @@ struct Settings: View {
|
|||
|
||||
|
||||
NavigationLink {
|
||||
TelemetryConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
TelemetryConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "chart.xyaxis.line")
|
||||
|
|
@ -183,7 +180,7 @@ struct Settings: View {
|
|||
|
||||
let connectedNode = nodes.first(where: { $0.num == connectedNodeNum })
|
||||
|
||||
AdminMessageList(user: connectedNode?.user ?? UserEntity())
|
||||
AdminMessageList(user: connectedNode?.user)
|
||||
} label: {
|
||||
|
||||
Image(systemName: "building.columns")
|
||||
|
|
@ -203,13 +200,7 @@ struct Settings: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
if nodes.count > 0 {
|
||||
selection = "0"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.navigationTitle("Settings")
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ struct ShareChannel: View {
|
|||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var text = "https://meshtastic.org/e/#test"
|
||||
var qrCodeImage = QrCodeImage()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ struct UserConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingFactoryResetConfirm: Bool = false
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
|
|
@ -112,7 +112,7 @@ struct UserConfig: View {
|
|||
u.shortName = shortName
|
||||
u.longName = longName
|
||||
|
||||
let adminMessageId = bleManager.saveUser(config: u, fromUser: node.user!, toUser: node.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveUser(config: u, fromUser: node?.user, toUser: node?.user, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
|
@ -137,22 +137,22 @@ struct UserConfig: View {
|
|||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.shortName = node.user!.shortName ?? ""
|
||||
self.longName = node.user!.longName ?? ""
|
||||
self.shortName = node?.user!.shortName ?? ""
|
||||
self.longName = node?.user!.longName ?? ""
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: shortName) { newShort in
|
||||
|
||||
if newShort != node.user!.shortName {
|
||||
if newShort != node?.user!.shortName {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: longName) { newLong in
|
||||
|
||||
if newLong != node.user!.longName {
|
||||
if newLong != node?.user!.longName {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue