after running swiftlint --fix

This commit is contained in:
Mike Kinney 2021-11-29 15:59:06 -08:00
parent c831c49832
commit f079ffe119
36 changed files with 577 additions and 673 deletions

View file

@ -3,11 +3,11 @@ import CoreData
import CoreBluetooth
import SwiftUI
//---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// Meshtastic BLE Device Manager
//---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
private static var documentsFolder: URL {
do {
return try FileManager.default.url(
@ -19,48 +19,48 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
fatalError("Can't find documents directory.")
}
}
// Core Data
@Environment(\.managedObjectContext) private var viewContext
@Published var meshData : MeshData
@Published var messageData : MessageData
@Published var meshData: MeshData
@Published var messageData: MessageData
private var centralManager: CBCentralManager!
@Published var connectedPeripheral: Peripheral!
@Published var connectedNode: NodeInfoModel!
@Published var lastConnectedPeripheral: String
@Published var lastConnectionError: String
@Published var isSwitchedOn: Bool = false
@Published var isScanning: Bool = false
@Published var isConnected: Bool = false
@Published var peripherals = [Peripheral]()
var timeoutTimer: Timer?
var timeoutTimerCount = 0
private var meshLoggingEnabled: Bool = false
private var broadcastNodeId: UInt32 = 4294967295
/* Meshtastic Service Details */
var TORADIO_characteristic: CBCharacteristic!
var FROMRADIO_characteristic: CBCharacteristic!
var FROMNUM_characteristic: CBCharacteristic!
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
let FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
let meshLog = documentsFolder.appendingPathComponent("meshlog.txt")
/* init BLEManager */
override init() {
self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
self.meshData = MeshData()
self.messageData = MessageData()
@ -75,111 +75,108 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// called when bluetooth is enabled/disabled for the app
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
startScanning()
}
else {
} else {
isSwitchedOn = false
}
}
// Scan for nearby BLE devices using the Meshtastic BLE service ID
func startScanning() {
if isSwitchedOn {
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: nil)
self.isScanning = self.centralManager.isScanning
print("Scanning Started")
}
}
// Stop Scanning For BLE Devices
func stopScanning() {
if centralManager.isScanning {
self.centralManager.stopScan()
self.isScanning = self.centralManager.isScanning
print("Stopped Scanning")
}
}
/// The action after the timeout-timer has fired
///
/// - Parameters:
/// - timer: The time that fired the event
///
@objc func timeoutTimerFired(timer: Timer)
{
@objc func timeoutTimerFired(timer: Timer) {
guard let context = timer.userInfo as? [String: String] else { return }
let name : String = context["name", default: "Unknown"]
let name: String = context["name", default: "Unknown"]
self.timeoutTimerCount += 1
if timeoutTimerCount == 6 {
if connectedPeripheral != nil {
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
}
connectedNode = nil
connectedPeripheral = nil
self.lastConnectionError = "BLE Connecting Timeout after making \(timeoutTimerCount) attempts to connect to \(name)."
print("BLE Connecting Timeout after making \(timeoutTimerCount) attempts to connect to \(name).")
if meshLoggingEnabled { MeshLogger.log("BLE Connecting Timeout after making \(timeoutTimerCount) attempts to connect to \(String(name)).") }
self.timeoutTimerCount = 0
self.timeoutTimer?.invalidate()
}
else {
} else {
print("BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)")
if meshLoggingEnabled { MeshLogger.log("BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)") }
}
}
// Connect to a specific peripheral
func connectTo(peripheral: CBPeripheral) {
if meshLoggingEnabled { MeshLogger.log("BLE Connecting: \(peripheral.name ?? "Unknown")") }
print("BLE Connecting: \(peripheral.name ?? "Unknown")")
stopScanning()
if self.connectedPeripheral != nil {
self.disconnectPeripheral()
}
self.centralManager?.connect(peripheral)
// Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent
// the timer from running on the main UI thread
let context = ["name": "@\(peripheral.name ?? "Unknown")"]
self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true)
RunLoop.current.add(self.timeoutTimer!, forMode: .common)
}
// Disconnect Peripheral function
func disconnectPeripheral(){
func disconnectPeripheral() {
guard let connectedPeripheral = connectedPeripheral else { return }
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
}
// Called each time a peripheral is discovered
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
var peripheralName: String = peripheral.name ?? "Unknown"
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
}
var newPeripheral = Peripheral(id: peripheral.identifier.uuidString, name: peripheralName, rssi: RSSI.intValue, subscribed: false, peripheral: peripheral, myInfo: nil)
let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id })
@ -189,29 +186,28 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
peripherals[peripheralIndex!] = newPeripheral
peripherals.remove(at: peripheralIndex!)
peripherals.append(newPeripheral)
print("Updating peripheral: \(peripheralName)");
}
else {
print("Updating peripheral: \(peripheralName)")
} else {
if newPeripheral.peripheral.state != CBPeripheralState.connected {
peripherals.append(newPeripheral)
print("Adding peripheral: \(peripheralName)");
print("Adding peripheral: \(peripheralName)")
}
}
}
// called when a peripheral is connected
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//guard let connectedPeripheral = connectedPeripheral else { return }
// guard let connectedPeripheral = connectedPeripheral else { return }
self.isConnected = true
// Invalidate and reset connection timer count, remove any connection errors
self.lastConnectionError = ""
self.timeoutTimer!.invalidate()
self.timeoutTimerCount = 0
// Map the peripheral to the connectedNode and connectedPeripheral ObservedObjects
connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first
connectedPeripheral.peripheral.delegate = self
@ -219,36 +215,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
let peripheralLast4: String = String(deviceName.suffix(4))
connectedNode = self.meshData.nodes.first(where: { $0.user.id.contains(peripheralLast4) })
lastConnectedPeripheral = peripheral.identifier.uuidString
// Discover Services
peripheral.discoverServices([meshtasticServiceCBUUID])
if meshLoggingEnabled { MeshLogger.log("BLE Connected: \(peripheral.name ?? "Unknown")") }
print("BLE Connected: \(peripheral.name ?? "Unknown")")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if meshLoggingEnabled { MeshLogger.log("BLE Failed to Connect: \(peripheral.name ?? "Unknown")") }
print("BLE Failed to Connect: \(peripheral.name ?? "Unknown")")
disconnectPeripheral()
}
// Disconnect Peripheral Event
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
{
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
// Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake
self.startScanning()
self.connectedPeripheral = nil
if let e = error {
// https://developer.apple.com/documentation/corebluetooth/cberror/code
let errorCode = (e as NSError).code
// unknown = 0,
if errorCode == 6 { // CBError.Code.connectionTimeout The connection has timed out unexpectedly.
// Happens when device is manually reset / powered off
// We will try and re-connect to this device
lastConnectionError = "\(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds."
@ -257,31 +252,28 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
print("BLE Reconnecting: \(peripheral.name ?? "Unknown")")
self.connectTo(peripheral: peripheral)
}
}
else if errorCode == 7 { //CBError.Code.peripheralDisconnected The specified device has disconnected from us.
} else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us.
// Seems to be what is received when a tbeam sleeps, immediately recconnecting does not work.
lastConnectionError = e.localizedDescription
self.connectedNode = nil
print("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
if meshLoggingEnabled { MeshLogger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") }
}
else if errorCode == 14 { // Peer removed pairing information
} else if errorCode == 14 { // Peer removed pairing information
// Forgetting and reconnecting seems to be necessary so we need to show the user an error telling them to do that
lastConnectionError = "\(e.localizedDescription) This error usually cannot be fixed without forgetting the device unders Settings > Bluetooth and re-connecting to the radio."
self.connectedNode = nil
if meshLoggingEnabled { MeshLogger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(lastConnectionError)") }
}
else {
} else {
lastConnectionError = e.localizedDescription
self.connectedNode = nil
print("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
if meshLoggingEnabled { MeshLogger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") }
}
} else {
// Disconnected without error which indicates user intent to disconnect
// Happens when swiping to disconnect
self.connectedNode = nil
@ -289,22 +281,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
print("BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect")
}
}
// Discover Services Event
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let e = error {
print("Discover Services error \(e)")
}
guard let services = peripheral.services else { return }
for service in services
{
if (service.uuid == meshtasticServiceCBUUID)
{
guard let services = peripheral.services else { return }
for service in services {
if service.uuid == meshtasticServiceCBUUID {
print("Meshtastic service discovered OK")
if meshLoggingEnabled { MeshLogger.log("BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")") }
peripheral.discoverCharacteristics(nil, for: service)
@ -312,22 +302,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
}
}
// Discover Characteristics Event
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let e = error {
print("Discover Characteristics error \(e)")
if meshLoggingEnabled { MeshLogger.log("BLE didDiscoverCharacteristicsFor error by \(peripheral.name ?? "Unknown") \(e)") }
}
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
switch characteristic.uuid
{
switch characteristic.uuid {
case TORADIO_UUID:
print("TORADIO characteristic OK")
if meshLoggingEnabled { MeshLogger.log("BLE did discover TORADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
@ -337,92 +325,86 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
let binaryData: Data = try! toRadio.serializedData()
peripheral.writeValue(binaryData, for: characteristic, type: .withResponse)
break
case FROMRADIO_UUID:
print("FROMRADIO characteristic OK")
if meshLoggingEnabled { MeshLogger.log("BLE did discover FROMRADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
FROMRADIO_characteristic = characteristic
peripheral.readValue(for: FROMRADIO_characteristic)
break
case FROMNUM_UUID:
print("FROMNUM (Notify) characteristic OK")
if meshLoggingEnabled { MeshLogger.log("BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
FROMNUM_characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
break
default:
break
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)")
if meshLoggingEnabled { MeshLogger.log("didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)") }
if let errorText = error?.localizedDescription
{
if let errorText = error?.localizedDescription {
print("didUpdateNotificationStateFor error: \(errorText)")
}
//commandLock.lock()
// commandLock.lock()
//if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
// if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
// if case .notificationStateUpdate(characteristic: characteristic, enabled: characteristic.isNotifying) = condition {
// return true
// } else {
// return false
// }
//}) {
// }) {
// commandConditions.remove(at: index)
// commandError = error
// if commandConditions.isEmpty {
// commandLock.broadcast()
// }
//}
// }
//commandLock.unlock()
//?.peripheralManager(self, didUpdateNotificationStateFor: characteristic)
// commandLock.unlock()
// ?.peripheralManager(self, didUpdateNotificationStateFor: characteristic)
}
// Data Read / Update Characteristic Event
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
{
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let e = error {
print("didUpdateValueFor Characteristic error \(e)")
if meshLoggingEnabled { MeshLogger.log("BLE didUpdateValueFor characteristic error by \(peripheral.name ?? "Unknown") \(e)") }
}
switch characteristic.uuid
{
switch characteristic.uuid {
case FROMNUM_UUID:
peripheral.readValue(for: FROMNUM_characteristic)
//let byteArrayFromData: [UInt8] = [UInt8](characteristic.value!)
//let stringFromByteArray = String(data: Data(_: byteArrayFromData), encoding: .utf8)
//print("string array data \(stringFromByteArray!)")
//print(characteristic.value?. ?? "no value")
// let byteArrayFromData: [UInt8] = [UInt8](characteristic.value!)
// let stringFromByteArray = String(data: Data(_: byteArrayFromData), encoding: .utf8)
// print("string array data \(stringFromByteArray!)")
// print(characteristic.value?. ?? "no value")
case FROMRADIO_UUID:
if (characteristic.value == nil || characteristic.value!.isEmpty)
{
if characteristic.value == nil || characteristic.value!.isEmpty {
return
}
//print(characteristic.value ?? "no value")
//print(characteristic.value?.hexDescription ?? "no value")
// print(characteristic.value ?? "no value")
// print(characteristic.value?.hexDescription ?? "no value")
var decodedInfo = FromRadio()
decodedInfo = try! FromRadio(serializedData: characteristic.value!)
print("Print DecodedInfo")
print(decodedInfo)
if decodedInfo.myInfo.myNodeNum != 0
{
if decodedInfo.myInfo.myNodeNum != 0 {
// Create a MyInfoModel
let myInfoModel = MyInfoModel(
myNodeNum: decodedInfo.myInfo.myNodeNum,
@ -432,21 +414,21 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
firmwareVersion: decodedInfo.myInfo.firmwareVersion,
messageTimeoutMsec: decodedInfo.myInfo.messageTimeoutMsec,
minAppVersion: decodedInfo.myInfo.minAppVersion)
// Save it to the connected nodeInfo
if connectedPeripheral != nil {
connectedPeripheral.myInfo = myInfoModel
// Save it to the connected node
connectedNode = meshData.nodes.first(where: {$0.num == myInfoModel.myNodeNum})
}
// Since the data is from the device itself we save all myInfo objects since they are always the most up to date
if connectedNode != nil {
connectedNode.myInfo = myInfoModel
let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.myInfo.myNodeNum })
//meshData.nodes.remove(at: nodeIndex!)
//meshData.nodes.append(connectedNode)
// meshData.nodes.remove(at: nodeIndex!)
// meshData.nodes.append(connectedNode)
if nodeIndex != nil {
meshData.nodes[nodeIndex!] = connectedNode
meshData.save()
@ -455,43 +437,42 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and myInfo saved for \(peripheral.name ?? "Unknown")") }
}
}
if decodedInfo.nodeInfo.num != 0 {
print("Save a nodeInfo")
print(decodedInfo.nodeInfo)
if meshData.nodes.contains(where: {$0.id == decodedInfo.nodeInfo.num}) {
// Found a matching node lets update it
let nodeMatch = meshData.nodes.first(where: { $0.id == decodedInfo.nodeInfo.num })
if connectedPeripheral != nil && connectedPeripheral.myInfo?.myNodeNum == nodeMatch?.num {
connectedNode = nodeMatch
}
if nodeMatch?.lastHeard ?? 0 < decodedInfo.nodeInfo.lastHeard && nodeMatch?.user != nil && nodeMatch?.user.longName.count ?? 0 > 0 {
// The data coming from the device is newer
let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.nodeInfo.num })
meshData.nodes.remove(at: nodeIndex!)
meshData.save()
}
else {
} else {
// Data is older than what the app already has
return
}
}
// Set the connected node if the nodeInfo is for the connected node.
if connectedPeripheral != nil && connectedPeripheral.myInfo?.myNodeNum == decodedInfo.nodeInfo.num {
let nodeMatch = meshData.nodes.first(where: { $0.id == decodedInfo.nodeInfo.num })
if nodeMatch != nil {
connectedNode = nodeMatch
}
}
if decodedInfo.nodeInfo.hasUser {
meshData.nodes.append(
NodeInfoModel(
num: decodedInfo.nodeInfo.num,
@ -499,41 +480,39 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
id: decodedInfo.nodeInfo.user.id,
longName: decodedInfo.nodeInfo.user.longName,
shortName: decodedInfo.nodeInfo.user.shortName,
//macaddr: decodedInfo.nodeInfo.user.macaddr,
// macaddr: decodedInfo.nodeInfo.user.macaddr,
hwModel: String(describing: decodedInfo.nodeInfo.user.hwModel)
.uppercased()),
position: NodeInfoModel.Position(
latitudeI: decodedInfo.nodeInfo.position.latitudeI,
longitudeI: decodedInfo.nodeInfo.position.longitudeI,
altitude: decodedInfo.nodeInfo.position.altitude,
batteryLevel: decodedInfo.nodeInfo.position.batteryLevel,
time: decodedInfo.nodeInfo.position.time),
lastHeard: decodedInfo.nodeInfo.lastHeard,
snr: decodedInfo.nodeInfo.snr)
)
meshData.save()
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") }
if connectedNode == nil {
//connectedNode = meshData.nodes.first(where: {$0.num == connectedPeripheral.myInfo!.myNodeNum})
// connectedNode = meshData.nodes.first(where: {$0.num == connectedPeripheral.myInfo!.myNodeNum})
}
}
}
// Handle assorted app packets
if decodedInfo.packet.id != 0 {
print("Handle a Packet")
do {
// Text Message App - Primary Broadcast Channel
if decodedInfo.packet.decoded.portnum == PortNum.textMessageApp {
if let messageText = String(bytes: decodedInfo.packet.decoded.payload, encoding: .utf8) {
print("Message Text: \(messageText)")
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received for text message app \(messageText)") }
@ -548,7 +527,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
toUserLongName = toUser?.user.longName ?? "Unknown"
toUserShortName = toUser?.user.shortName ?? "???"
}
// Add the received message to the local messages list / file and save
messageData.messages.append(
MessageModel(
@ -565,10 +544,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
direction: "IN")
)
messageData.save()
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(decodedInfo.packet.id)"),
@ -579,8 +558,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
manager.schedule()
if meshLoggingEnabled { MeshLogger.log("iOS Notification Scheduled for text message from \(fromUser?.user.longName ?? "Unknown") \(messageText)") }
}
}
else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
} else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
var updatedNode = meshData.nodes.first(where: {$0.id == decodedInfo.packet.from })
if updatedNode != nil {
@ -590,7 +568,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if updatedNode != nil {
updatedNode!.snr = decodedInfo.packet.rxSnr
updatedNode!.lastHeard = decodedInfo.packet.rxTime
//updatedNode!.update(from: updatedNode!.data)
// updatedNode!.update(from: updatedNode!.data)
let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.packet.from })
meshData.nodes.remove(at: nodeIndex!)
meshData.nodes.append(updatedNode!)
@ -598,15 +576,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if meshLoggingEnabled { MeshLogger.log("MESH PACKET Updated NodeInfo SNR and Time from Node Info App Packet For: \(updatedNode!.user.longName)") }
print("Updated NodeInfo SNR and Time from Packet For: \(updatedNode!.user.longName)")
}
}
else if decodedInfo.packet.decoded.portnum == PortNum.positionApp {
} else if decodedInfo.packet.decoded.portnum == PortNum.positionApp {
var updatedNode = meshData.nodes.first(where: {$0.id == decodedInfo.packet.from })
if updatedNode != nil {
updatedNode!.snr = decodedInfo.packet.rxSnr
updatedNode!.lastHeard = decodedInfo.packet.rxTime
//updatedNode!.update(from: updatedNode!.data)
// updatedNode!.update(from: updatedNode!.data)
let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.packet.from })
meshData.nodes.remove(at: nodeIndex!)
meshData.nodes.append(updatedNode!)
@ -617,63 +594,58 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
print("Postion Payload")
print(try decodedInfo.packet.jsonString())
}
else if decodedInfo.packet.decoded.portnum == PortNum.adminApp {
} else if decodedInfo.packet.decoded.portnum == PortNum.adminApp {
if meshLoggingEnabled { MeshLogger.log("MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print("Admin App Packet")
print(try decodedInfo.packet.jsonString())
}
else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
} else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
if meshLoggingEnabled { MeshLogger.log("MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print("Routing App Packet")
print(try decodedInfo.packet.jsonString())
}
else
{
} else {
if meshLoggingEnabled { MeshLogger.log("MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") }
print("Other App Packet")
print(try decodedInfo.packet.jsonString())
}
} catch {
if meshLoggingEnabled { MeshLogger.log("Fatal Error: Failed to decode json") }
fatalError("Failed to decode json")
}
}
if decodedInfo.configCompleteID != 0 {
if meshLoggingEnabled { MeshLogger.log("BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)") }
print("BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)")
self.connectedPeripheral.subscribed = true
}
default:
if meshLoggingEnabled { MeshLogger.log("Unhandled Characteristic UUID: \(characteristic.uuid)") }
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
}
peripheral.readValue(for: FROMRADIO_characteristic)
}
// Send Broadcast Message
public func sendMessage(message: String) -> Bool
{
public func sendMessage(message: String) -> Bool {
var success = false
// Return false if we are not properly connected to a device, handle retry logic in the view for now
if connectedPeripheral == nil || connectedPeripheral!.peripheral.state != CBPeripheralState.connected {
self.disconnectPeripheral()
self.startScanning()
// Try and connect to the preferredPeripherial first
let preferredPeripheral = peripherals.filter({ $0.peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" }).first
if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil {
connectTo(peripheral: preferredPeripheral!.peripheral)
} else {
// Try and connect to the last connected device
let lastConnectedPeripheral = peripherals.filter({ $0.peripheral.identifier.uuidString == self.lastConnectedPeripheral }).first
if lastConnectedPeripheral != nil && lastConnectedPeripheral?.peripheral != nil {
@ -681,43 +653,40 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
}
success = false
}
else if message.count < 1 {
} else if message.count < 1 {
// Don's send an empty message
success = false
}
else {
} else {
var longName: String = self.connectedPeripheral.name
var shortName: String = "???"
var nodeNum: UInt32 = self.connectedPeripheral.myInfo?.myNodeNum ?? 0
if connectedNode != nil {
longName = connectedNode.user.longName
shortName = connectedNode.user.shortName
nodeNum = connectedNode.num
}
let messageModel = MessageModel(messageId: 0, messageTimeStamp: UInt32(Date().timeIntervalSince1970), fromUserId: nodeNum, toUserId: broadcastNodeId, fromUserLongName: longName, toUserLongName: "Broadcast", fromUserShortName: shortName, toUserShortName: "BC", receivedACK: false, messagePayload: message, direction: "OUT")
let dataType = PortNum.textMessageApp
let payloadData: Data = message.data(using: String.Encoding.utf8)!
var dataMessage = DataMessage()
dataMessage.payload = payloadData
dataMessage.portnum = dataType
var meshPacket = MeshPacket()
meshPacket.to = broadcastNodeId
meshPacket.decoded = dataMessage
meshPacket.wantAck = true
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if (connectedPeripheral!.peripheral.state == CBPeripheralState.connected)
{
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
messageData.messages.append(messageModel)
messageData.save()
@ -726,7 +695,5 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
return success
}
}

View file

@ -1,24 +1,19 @@
import Foundation
import SwiftUI
extension Data
{
var hexDescription: String
{
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
extension Date
{
static var currentTimeStamp: Int64
{
extension Date {
static var currentTimeStamp: Int64 {
return Int64(Date().timeIntervalSince1970 * 1000)
}
}
extension String
{
extension String {
/// Create `Data` from hexadecimal string representation
///
@ -26,8 +21,7 @@ extension String
///
/// - returns: Data represented by this hexadecimal string.
var hexadecimal: Data?
{
var hexadecimal: Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)

View file

@ -7,5 +7,3 @@
import Foundation
import SwiftUI

View file

@ -1,14 +1,12 @@
import Foundation
import SwiftUI
class LocalNotificationManager {
var notifications = [Notification]()
// Step 1 Request Permissions for notifications
private func requestAuthorization()
{
private func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted == true && error == nil {
@ -16,9 +14,8 @@ class LocalNotificationManager {
}
}
}
func schedule()
{
func schedule() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
switch settings.authorizationStatus {
case .notDetermined:
@ -30,12 +27,10 @@ class LocalNotificationManager {
}
}
}
// This function iterates over the Notification objects in the notifications array and schedules them for delivery in the future
private func scheduleNotifications()
{
for notification in notifications
{
private func scheduleNotifications() {
for notification in notifications {
let content = UNMutableNotificationContent()
content.subtitle = notification.subtitle
content.title = notification.title
@ -43,7 +38,6 @@ class LocalNotificationManager {
content.sound = .default
content.interruptionLevel = .timeSensitive
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: trigger)
@ -55,10 +49,9 @@ class LocalNotificationManager {
}
}
}
// Check and debug what local notifications have been scheduled
func listScheduledNotifications()
{
func listScheduledNotifications() {
UNUserNotificationCenter.current().getPendingNotificationRequests { notifications in
for notification in notifications {
@ -66,8 +59,7 @@ class LocalNotificationManager {
}
}
}
}
struct Notification {

View file

@ -1,24 +1,24 @@
import CoreLocation
class LocationHelper: NSObject, ObservableObject {
static let shared = LocationHelper()
// Mount Rainier
static let DefaultLocation = CLLocationCoordinate2D(latitude: 46.879967, longitude: -121.726906)
static var currentLocation: CLLocationCoordinate2D {
guard let location = shared.locationManager.location else {
return DefaultLocation
}
return location.coordinate
}
private let locationManager = CLLocationManager()
private override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
@ -29,11 +29,11 @@ class LocationHelper: NSObject, ObservableObject {
extension LocationHelper: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { }
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location manager failed with error: \(error.localizedDescription)")
}
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("Location manager changed the status: \(status)")
}

View file

@ -6,9 +6,9 @@ struct MeshtasticClientApp: App {
@ObservedObject private var bleManager: BLEManager = BLEManager()
@ObservedObject private var userSettings: UserSettings = UserSettings()
@Environment(\.scenePhase) var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
@ -28,17 +28,17 @@ struct MeshtasticClientApp: App {
}
}
}
var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Mesh")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores(completionHandler: { (_, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func saveContext() {
let context = persistentContainer.viewContext
if context.hasChanges {

View file

@ -12,11 +12,11 @@ class MeshData: ObservableObject {
fatalError("Can't find documents directory.")
}
}
private static var fileURL: URL {
return documentsFolder.appendingPathComponent("nodeinfo.data")
}
@Published var nodes: [NodeInfoModel] = []
func load() {
@ -33,12 +33,11 @@ class MeshData: ObservableObject {
do {
// If the file is borked delete it so we stop crashing
try FileManager.default.removeItem(at: Self.fileURL)
}
catch {
} catch {
fatalError("Can't delete saved node data.")
}
fatalError("Can't decode saved node data.")
}
DispatchQueue.main.async {

View file

@ -1,7 +1,7 @@
import Foundation
class MessageData: ObservableObject {
private static var documentsFolder: URL {
do {
return try FileManager.default.url(for: .documentDirectory,
@ -12,13 +12,13 @@ class MessageData: ObservableObject {
fatalError("Can't find documents directory.")
}
}
private static var fileURL: URL {
return documentsFolder.appendingPathComponent("messages.data")
}
@Published var messages: [MessageModel] = []
func load() {
DispatchQueue.global(qos: .background).async { [weak self] in
guard let data = try? Data(contentsOf: Self.fileURL) else {

View file

@ -6,8 +6,7 @@
//
import Foundation
struct MessageModel : Identifiable, Codable
{
struct MessageModel: Identifiable, Codable {
let id: UUID
var messageId: UInt32
var messageTimestamp: UInt32
@ -20,9 +19,8 @@ struct MessageModel : Identifiable, Codable
var receivedACK: Bool
var messagePayload: String
var direction: String
init(id: UUID = UUID(), messageId: UInt32, messageTimeStamp: UInt32, fromUserId: UInt32, toUserId: UInt32, fromUserLongName: String, toUserLongName: String, fromUserShortName: String, toUserShortName: String, receivedACK: Bool, messagePayload: String, direction: String)
{
init(id: UUID = UUID(), messageId: UInt32, messageTimeStamp: UInt32, fromUserId: UInt32, toUserId: UInt32, fromUserLongName: String, toUserLongName: String, fromUserShortName: String, toUserShortName: String, receivedACK: Bool, messagePayload: String, direction: String) {
self.id = id
self.messageId = messageId
self.messageTimestamp = messageTimeStamp
@ -43,7 +41,7 @@ extension MessageModel {
static var data: [MessageModel] {
[
//MessageModel(messageId: 3773493338, messageTimeStamp: 1632407404, fromUserId: 2930161432, toUserId: 4294967295, fromUserLongName: "TBEAM ARMY GREEN", toUserLongName: "Unknown 1", fromUserShortName: "GVH", toUserShortName: "U1", receivedACK: false, messagePayload: "yo", direction: "received")
// MessageModel(messageId: 3773493338, messageTimeStamp: 1632407404, fromUserId: 2930161432, toUserId: 4294967295, fromUserLongName: "TBEAM ARMY GREEN", toUserLongName: "Unknown 1", fromUserShortName: "GVH", toUserShortName: "U1", receivedACK: false, messagePayload: "yo", direction: "received")
]
}
}
@ -62,7 +60,7 @@ extension MessageModel {
var receivedACK: Bool
var messagePayload: String
var direction: String
}
var data: Data {

View file

@ -1,7 +1,7 @@
import Foundation
struct MyInfoModel: Identifiable, Codable {
// Uses the BLE Peripheral identifier as the ID
// So myInfo can map between Peripherals and Nodes
var id: UInt32
@ -12,9 +12,9 @@ struct MyInfoModel: Identifiable, Codable {
var firmwareVersion: String
var messageTimeoutMsec: UInt32
var minAppVersion: UInt32
init(myNodeNum: UInt32, hasGps: Bool, numBands: UInt32, maxChannels: UInt32, firmwareVersion: String, messageTimeoutMsec: UInt32, minAppVersion: UInt32) {
self.id = myNodeNum
self.myNodeNum = myNodeNum
self.hasGps = hasGps

View file

@ -5,7 +5,7 @@ import SwiftUI
import CoreLocation
struct NodeInfoModel: Identifiable, Codable {
var id: UInt32
var num: UInt32
var myInfo: MyInfoModel?
@ -15,7 +15,7 @@ struct NodeInfoModel: Identifiable, Codable {
var longName: String
var shortName: String
var hwModel: String
init(id: String, longName: String, shortName: String, hwModel: String) {
self.id = id
self.longName = longName
@ -33,8 +33,7 @@ struct NodeInfoModel: Identifiable, Codable {
return nil
}
return d / 1e7
}
else {
} else {
return nil
}
}
@ -46,25 +45,23 @@ struct NodeInfoModel: Identifiable, Codable {
return nil
}
return d / 1e7
}
else {
} else {
return nil
}
}
var coordinate: CLLocationCoordinate2D? {
if latitude != nil && longitude != nil {
let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
return coord
}
else {
} else {
return nil
}
}
var altitude: Int32?
var batteryLevel: Int32?
var time: UInt32?
init(latitudeI: Int32?, longitudeI: Int32?, altitude: Int32?, batteryLevel: Int32?, time: UInt32? ) {
self.latitudeI = latitudeI
self.longitudeI = longitudeI
@ -73,11 +70,10 @@ struct NodeInfoModel: Identifiable, Codable {
self.time = time
}
}
var lastHeard: UInt32
var snr: Float?
init(num: UInt32, user: User, position: Position, lastHeard: UInt32, snr: Float?) {
self.id = num
self.num = num
@ -106,7 +102,7 @@ extension NodeInfoModel {
var postion: Position
var lastHeard: UInt32
var snr: Float?
}
var data: Data {

View file

@ -7,9 +7,9 @@ struct Peripheral: Identifiable {
var rssi: Int
var subscribed: Bool
var peripheral: CBPeripheral
var myInfo: MyInfoModel?
init(id: String, name: String, rssi: Int, subscribed: Bool, peripheral: CBPeripheral, myInfo: MyInfoModel?) {
self.id = id
self.name = name

View file

@ -15,7 +15,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -29,7 +29,7 @@ struct AdminMessage {
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var variant: AdminMessage.OneOf_Variant? = nil
var variant: AdminMessage.OneOf_Variant?
///
/// set the radio provisioning for this node
@ -258,7 +258,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
32: .standard(proto: "confirm_set_channel"),
33: .standard(proto: "confirm_set_radio"),
34: .standard(proto: "exit_simulator"),
35: .standard(proto: "reboot_seconds"),
35: .standard(proto: "reboot_seconds")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {

View file

@ -34,7 +34,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -230,7 +230,7 @@ extension ChannelSettings.ModemConfig: CaseIterable {
.bw125Cr45Sf128,
.bw500Cr45Sf128,
.bw3125Cr48Sf512,
.bw125Cr48Sf4096,
.bw125Cr48Sf4096
]
}
@ -319,7 +319,7 @@ struct Channel {
init() {}
fileprivate var _settings: ChannelSettings? = nil
fileprivate var _settings: ChannelSettings?
}
#if swift(>=4.2)
@ -329,7 +329,7 @@ extension Channel.Role: CaseIterable {
static var allCases: [Channel.Role] = [
.disabled,
.primary,
.secondary,
.secondary
]
}
@ -350,7 +350,7 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen
5: .same(proto: "name"),
10: .same(proto: "id"),
16: .standard(proto: "uplink_enabled"),
17: .standard(proto: "downlink_enabled"),
17: .standard(proto: "downlink_enabled")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -434,7 +434,7 @@ extension ChannelSettings.ModemConfig: SwiftProtobuf._ProtoNameProviding {
0: .same(proto: "Bw125Cr45Sf128"),
1: .same(proto: "Bw500Cr45Sf128"),
2: .same(proto: "Bw31_25Cr48Sf512"),
3: .same(proto: "Bw125Cr48Sf4096"),
3: .same(proto: "Bw125Cr48Sf4096")
]
}
@ -443,7 +443,7 @@ extension Channel: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "index"),
2: .same(proto: "settings"),
3: .same(proto: "role"),
3: .same(proto: "role")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -486,6 +486,6 @@ extension Channel.Role: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "DISABLED"),
1: .same(proto: "PRIMARY"),
2: .same(proto: "SECONDARY"),
2: .same(proto: "SECONDARY")
]
}

View file

@ -15,7 +15,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -56,7 +56,7 @@ struct LegacyRadioConfig {
init() {}
fileprivate var _preferences: LegacyRadioConfig.LegacyPreferences? = nil
fileprivate var _preferences: LegacyRadioConfig.LegacyPreferences?
}
///
@ -180,7 +180,7 @@ struct ChannelFile {
extension LegacyRadioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "LegacyRadioConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "preferences"),
1: .same(proto: "preferences")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -212,7 +212,7 @@ extension LegacyRadioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
extension LegacyRadioConfig.LegacyPreferences: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = LegacyRadioConfig.protoMessageName + ".LegacyPreferences"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
15: .same(proto: "region"),
15: .same(proto: "region")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -252,17 +252,17 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
8: .same(proto: "version"),
7: .standard(proto: "rx_text_message"),
9: .standard(proto: "no_save"),
11: .standard(proto: "did_gps_reset"),
11: .standard(proto: "did_gps_reset")
]
fileprivate class _StorageClass {
var _legacyRadio: LegacyRadioConfig? = nil
var _myNode: MyNodeInfo? = nil
var _owner: User? = nil
var _legacyRadio: LegacyRadioConfig?
var _myNode: MyNodeInfo?
var _owner: User?
var _nodeDb: [NodeInfo] = []
var _receiveQueue: [MeshPacket] = []
var _version: UInt32 = 0
var _rxTextMessage: MeshPacket? = nil
var _rxTextMessage: MeshPacket?
var _noSave: Bool = false
var _didGpsReset: Bool = false
@ -372,7 +372,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "ChannelFile"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "channels"),
1: .same(proto: "channels")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {

View file

@ -15,7 +15,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -43,7 +43,7 @@ extension EnvironmentalMeasurement: SwiftProtobuf.Message, SwiftProtobuf._Messag
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "temperature"),
2: .standard(proto: "relative_humidity"),
3: .standard(proto: "barometric_pressure"),
3: .standard(proto: "barometric_pressure")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {

View file

@ -34,7 +34,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -152,7 +152,7 @@ extension HardwareModel: CaseIterable {
.genieblocks,
.nrf52Unknown,
.portduino,
.androidSim,
.androidSim
]
}
@ -203,7 +203,7 @@ extension Constants: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [Constants] = [
.unused,
.dataPayloadLen,
.dataPayloadLen
]
}
@ -261,7 +261,7 @@ enum CriticalErrorCode: SwiftProtobuf.Enum {
case sx1262Failure // = 10
/// A (likely software but possibly hardware) failure was detected while trying to send packets. If this occurs on your board, please
///post in the forum so that we can ask you to collect some information to allow fixing this bug
/// post in the forum so that we can ask you to collect some information to allow fixing this bug
case radioSpiBug // = 11
case UNRECOGNIZED(Int)
@ -323,7 +323,7 @@ extension CriticalErrorCode: CaseIterable {
.transmitFailed,
.brownout,
.sx1262Failure,
.radioSpiBug,
.radioSpiBug
]
}
@ -450,13 +450,13 @@ struct RouteDiscovery {
}
///
///A Routing control Data packet handled by the routing plugin
/// A Routing control Data packet handled by the routing plugin
struct Routing {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var variant: Routing.OneOf_Variant? = nil
var variant: Routing.OneOf_Variant?
///
/// A route request going from the requester
@ -635,7 +635,7 @@ extension Routing.Error: CaseIterable {
.tooLarge,
.noResponse,
.badRequest,
.notAuthorized,
.notAuthorized
]
}
@ -719,7 +719,7 @@ struct MeshPacket {
/// This 'trick' is only used while the payloadVariant is an 'encrypted'.
var channel: UInt32 = 0
var payloadVariant: MeshPacket.OneOf_PayloadVariant? = nil
var payloadVariant: MeshPacket.OneOf_PayloadVariant?
var decoded: DataMessage {
get {
@ -915,7 +915,7 @@ extension MeshPacket.Priority: CaseIterable {
.default,
.reliable,
.ack,
.max,
.max
]
}
@ -988,8 +988,8 @@ struct NodeInfo {
init() {}
fileprivate var _user: User? = nil
fileprivate var _position: Position? = nil
fileprivate var _user: User?
fileprivate var _position: Position?
}
///
@ -1169,7 +1169,7 @@ extension LogRecord.Level: CaseIterable {
.warning,
.info,
.debug,
.trace,
.trace
]
}
@ -1341,7 +1341,7 @@ struct ToRadio {
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var payloadVariant: ToRadio.OneOf_PayloadVariant? = nil
var payloadVariant: ToRadio.OneOf_PayloadVariant?
///
/// send this packet on the mesh
@ -1495,14 +1495,14 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
35: .same(proto: "GENIEBLOCKS"),
36: .same(proto: "NRF52_UNKNOWN"),
37: .same(proto: "PORTDUINO"),
38: .same(proto: "ANDROID_SIM"),
38: .same(proto: "ANDROID_SIM")
]
}
extension Constants: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "Unused"),
237: .same(proto: "DATA_PAYLOAD_LEN"),
237: .same(proto: "DATA_PAYLOAD_LEN")
]
}
@ -1519,7 +1519,7 @@ extension CriticalErrorCode: SwiftProtobuf._ProtoNameProviding {
8: .same(proto: "TransmitFailed"),
9: .same(proto: "Brownout"),
10: .same(proto: "SX1262Failure"),
11: .same(proto: "RadioSpiBug"),
11: .same(proto: "RadioSpiBug")
]
}
@ -1530,7 +1530,7 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
2: .standard(proto: "longitude_i"),
3: .same(proto: "altitude"),
4: .standard(proto: "battery_level"),
9: .same(proto: "time"),
9: .same(proto: "time")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1587,7 +1587,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
3: .standard(proto: "short_name"),
4: .same(proto: "macaddr"),
6: .standard(proto: "hw_model"),
7: .standard(proto: "is_licensed"),
7: .standard(proto: "is_licensed")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1644,7 +1644,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
extension RouteDiscovery: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "RouteDiscovery"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
2: .same(proto: "route"),
2: .same(proto: "route")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1678,7 +1678,7 @@ extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "route_request"),
2: .standard(proto: "route_reply"),
3: .standard(proto: "error_reason"),
3: .standard(proto: "error_reason")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1767,7 +1767,7 @@ extension Routing.Error: SwiftProtobuf._ProtoNameProviding {
7: .same(proto: "TOO_LARGE"),
8: .same(proto: "NO_RESPONSE"),
32: .same(proto: "BAD_REQUEST"),
33: .same(proto: "NOT_AUTHORIZED"),
33: .same(proto: "NOT_AUTHORIZED")
]
}
@ -1779,7 +1779,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
3: .standard(proto: "want_response"),
4: .same(proto: "dest"),
5: .same(proto: "source"),
6: .standard(proto: "request_id"),
6: .standard(proto: "request_id")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1847,7 +1847,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
10: .standard(proto: "hop_limit"),
11: .standard(proto: "want_ack"),
12: .same(proto: "priority"),
13: .standard(proto: "rx_rssi"),
13: .standard(proto: "rx_rssi")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1965,7 +1965,7 @@ extension MeshPacket.Priority: SwiftProtobuf._ProtoNameProviding {
64: .same(proto: "DEFAULT"),
70: .same(proto: "RELIABLE"),
120: .same(proto: "ACK"),
127: .same(proto: "MAX"),
127: .same(proto: "MAX")
]
}
@ -1976,7 +1976,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
2: .same(proto: "user"),
3: .same(proto: "position"),
7: .same(proto: "snr"),
4: .standard(proto: "last_heard"),
4: .standard(proto: "last_heard")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2040,7 +2040,7 @@ extension MyNodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
9: .standard(proto: "error_count"),
10: .standard(proto: "reboot_count"),
13: .standard(proto: "message_timeout_msec"),
14: .standard(proto: "min_app_version"),
14: .standard(proto: "min_app_version")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2135,7 +2135,7 @@ extension LogRecord: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
1: .same(proto: "message"),
2: .same(proto: "time"),
3: .same(proto: "source"),
4: .same(proto: "level"),
4: .same(proto: "level")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2187,7 +2187,7 @@ extension LogRecord.Level: SwiftProtobuf._ProtoNameProviding {
20: .same(proto: "INFO"),
30: .same(proto: "WARNING"),
40: .same(proto: "ERROR"),
50: .same(proto: "CRITICAL"),
50: .same(proto: "CRITICAL")
]
}
@ -2200,7 +2200,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
4: .standard(proto: "node_info"),
7: .standard(proto: "log_record"),
8: .standard(proto: "config_complete_id"),
9: .same(proto: "rebooted"),
9: .same(proto: "rebooted")
]
fileprivate class _StorageClass {
@ -2368,7 +2368,7 @@ extension ToRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
2: .same(proto: "packet"),
3: .standard(proto: "peer_info"),
100: .standard(proto: "want_config_id"),
104: .same(proto: "disconnect"),
104: .same(proto: "disconnect")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2461,7 +2461,7 @@ extension ToRadio.PeerInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
static let protoMessageName: String = ToRadio.protoMessageName + ".PeerInfo"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "app_version"),
2: .standard(proto: "mqtt_gateway"),
2: .standard(proto: "mqtt_gateway")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {

View file

@ -15,7 +15,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -68,11 +68,11 @@ extension ServiceEnvelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "packet"),
2: .standard(proto: "channel_id"),
3: .standard(proto: "gateway_id"),
3: .standard(proto: "gateway_id")
]
fileprivate class _StorageClass {
var _packet: MeshPacket? = nil
var _packet: MeshPacket?
var _channelID: String = String()
var _gatewayID: String = String()

View file

@ -15,7 +15,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -197,7 +197,7 @@ extension PortNum: CaseIterable {
.environmentalMeasurementApp,
.privateApp,
.atakForwarder,
.max,
.max
]
}
@ -222,6 +222,6 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
67: .same(proto: "ENVIRONMENTAL_MEASUREMENT_APP"),
256: .same(proto: "PRIVATE_APP"),
257: .same(proto: "ATAK_FORWARDER"),
511: .same(proto: "MAX"),
511: .same(proto: "MAX")
]
}

View file

@ -34,7 +34,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -114,7 +114,7 @@ extension RegionCode: CaseIterable {
.anz,
.kr,
.tw,
.ru,
.ru
]
}
@ -217,7 +217,7 @@ extension ChargeCurrent: CaseIterable {
.ma1080,
.ma1160,
.ma1240,
.ma1320,
.ma1320
]
}
@ -292,7 +292,7 @@ extension GpsOperation: CaseIterable {
.gpsOpStationary,
.gpsOpMobile,
.gpsOpTimeOnly,
.gpsOpDisabled,
.gpsOpDisabled
]
}
@ -375,7 +375,7 @@ extension GpsCoordinateFormat: CaseIterable {
.gpsFormatUtm,
.gpsFormatMgrs,
.gpsFormatOlc,
.gpsFormatOsgr,
.gpsFormatOsgr
]
}
@ -430,7 +430,7 @@ extension LocationSharing: CaseIterable {
static var allCases: [LocationSharing] = [
.locUnset,
.locEnabled,
.locDisabled,
.locDisabled
]
}
@ -789,7 +789,7 @@ struct RadioConfig {
}
///
///Preferences for the RangeTestPlugin
/// Preferences for the RangeTestPlugin
/// FIXME - Move this out of UserPreferences and into a section for plugin configuration.
var rangeTestPluginEnabled: Bool {
get {return _storage._rangeTestPluginEnabled}
@ -808,7 +808,7 @@ struct RadioConfig {
///
/// Preferences for the StoreForwardPlugin
///FIXME - Move this out of UserPreferences and into a section for plugin configuration. (was 136)
/// FIXME - Move this out of UserPreferences and into a section for plugin configuration. (was 136)
var storeForwardPluginEnabled: Bool {
get {return _storage._storeForwardPluginEnabled}
set {_uniqueStorage()._storeForwardPluginEnabled = newValue}
@ -917,7 +917,7 @@ struct RadioConfig {
init() {}
fileprivate var _preferences: RadioConfig.UserPreferences? = nil
fileprivate var _preferences: RadioConfig.UserPreferences?
}
#if swift(>=4.2)
@ -925,7 +925,7 @@ struct RadioConfig {
extension RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType] = [
.dht11,
.dht11
]
}
@ -944,7 +944,7 @@ extension RegionCode: SwiftProtobuf._ProtoNameProviding {
6: .same(proto: "ANZ"),
7: .same(proto: "KR"),
8: .same(proto: "TW"),
9: .same(proto: "RU"),
9: .same(proto: "RU")
]
}
@ -966,7 +966,7 @@ extension ChargeCurrent: SwiftProtobuf._ProtoNameProviding {
13: .same(proto: "MA1080"),
14: .same(proto: "MA1160"),
15: .same(proto: "MA1240"),
16: .same(proto: "MA1320"),
16: .same(proto: "MA1320")
]
}
@ -976,7 +976,7 @@ extension GpsOperation: SwiftProtobuf._ProtoNameProviding {
1: .same(proto: "GpsOpStationary"),
2: .same(proto: "GpsOpMobile"),
3: .same(proto: "GpsOpTimeOnly"),
4: .same(proto: "GpsOpDisabled"),
4: .same(proto: "GpsOpDisabled")
]
}
@ -987,7 +987,7 @@ extension GpsCoordinateFormat: SwiftProtobuf._ProtoNameProviding {
2: .same(proto: "GpsFormatUTM"),
3: .same(proto: "GpsFormatMGRS"),
4: .same(proto: "GpsFormatOLC"),
5: .same(proto: "GpsFormatOSGR"),
5: .same(proto: "GpsFormatOSGR")
]
}
@ -995,14 +995,14 @@ extension LocationSharing: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "LocUnset"),
1: .same(proto: "LocEnabled"),
2: .same(proto: "LocDisabled"),
2: .same(proto: "LocDisabled")
]
}
extension RadioConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "RadioConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "preferences"),
1: .same(proto: "preferences")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1088,7 +1088,7 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes
144: .standard(proto: "environmental_measurement_plugin_recovery_interval"),
145: .standard(proto: "environmental_measurement_plugin_display_farenheit"),
146: .standard(proto: "environmental_measurement_plugin_sensor_type"),
147: .standard(proto: "environmental_measurement_plugin_sensor_pin"),
147: .standard(proto: "environmental_measurement_plugin_sensor_pin")
]
fileprivate class _StorageClass {
@ -1529,6 +1529,6 @@ extension RadioConfig.UserPreferences: SwiftProtobuf.Message, SwiftProtobuf._Mes
extension RadioConfig.UserPreferences.EnvironmentalMeasurementSensorType: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "DHT11"),
0: .same(proto: "DHT11")
]
}

View file

@ -15,7 +15,7 @@ import SwiftProtobuf
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
@ -126,7 +126,7 @@ extension HardwareMessage.TypeEnum: CaseIterable {
.watchGpios,
.gpiosChanged,
.readGpios,
.readGpiosReply,
.readGpiosReply
]
}
@ -139,7 +139,7 @@ extension HardwareMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "typ"),
2: .standard(proto: "gpio_mask"),
3: .standard(proto: "gpio_value"),
3: .standard(proto: "gpio_value")
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -185,6 +185,6 @@ extension HardwareMessage.TypeEnum: SwiftProtobuf._ProtoNameProviding {
2: .same(proto: "WATCH_GPIOS"),
3: .same(proto: "GPIOS_CHANGED"),
4: .same(proto: "READ_GPIOS"),
5: .same(proto: "READ_GPIOS_REPLY"),
5: .same(proto: "READ_GPIOS_REPLY")
]
}

View file

@ -14,47 +14,46 @@ import CoreLocation
import CoreBluetooth
struct Connect: View {
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
@State var isPreferredRadio: Bool = false
var body: some View {
NavigationView {
VStack {
if bleManager.isSwitchedOn {
List {
if bleManager.lastConnectionError.count > 0 {
Section(header: Text("Connection Error").font(.title)) {
Text(bleManager.lastConnectionError).font(.title3).foregroundColor(.red)
}
.textCase(nil)
}
Section(header: Text("Connected Radio").font(.title)) {
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)
VStack (alignment: .leading) {
VStack(alignment: .leading) {
if bleManager.connectedNode != nil {
Text(bleManager.connectedNode.user.longName).font(.title2)
}
else {
} else {
Text(String(bleManager.connectedPeripheral.peripheral.name ?? "Unknown")).font(.title2)
}
if bleManager.connectedNode != nil {
@ -69,9 +68,9 @@ struct Connect: View {
}
}
Spacer()
VStack (alignment: .center) {
VStack(alignment: .center) {
Text("Preferred").font(.caption2)
Text("Radio").font(.caption2)
Toggle("Preferred Radio", isOn: $isPreferredRadio)
@ -79,7 +78,7 @@ struct Connect: View {
.labelsHidden()
.onChange(of: isPreferredRadio) { value in
if value {
if bleManager.connectedNode != nil {
let deviceName = "\(bleManager.connectedNode.user.longName) / \(bleManager.connectedPeripheral.peripheral.name ?? "")"
@ -87,13 +86,13 @@ struct Connect: View {
} else {
userSettings.preferredPeripheralName = bleManager.connectedPeripheral.peripheral.name ?? "Unknown Device"
}
userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString
} else {
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId {
userSettings.preferredPeripheralId = ""
userSettings.preferredPeripheralName = ""
}
@ -102,10 +101,9 @@ struct Connect: View {
}
}
.swipeActions {
Button(role: .destructive) {
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected
{
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
bleManager.disconnectPeripheral()
isPreferredRadio = false
}
@ -114,9 +112,8 @@ struct Connect: View {
}
}
.padding([.top, .bottom])
}
else {
HStack{
} else {
HStack {
Image(systemName: "antenna.radiowaves.left.and.right.slash")
.symbolRenderingMode(.hierarchical)
.imageScale(.large).foregroundColor(.red)
@ -125,10 +122,10 @@ struct Connect: View {
}
.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
@ -138,18 +135,16 @@ struct Connect: View {
.padding(.trailing)
Button(action: {
self.bleManager.stopScanning()
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected
{
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 {
} else {
isPreferredRadio = false
}
}) {
@ -157,13 +152,13 @@ struct Connect: View {
}
Spacer()
Text(String(peripheral.rssi) + " dB").font(.title3)
}.padding([.bottom,.top])
}.padding([.bottom, .top])
}
}.textCase(nil)
}
}
HStack (alignment: .center) {
HStack(alignment: .center) {
Spacer()
Button(action: {
self.bleManager.startScanning()
@ -193,9 +188,8 @@ struct Connect: View {
Spacer()
}
.padding(.bottom, 10)
}
else {
} else {
Text("Bluetooth: OFF")
.foregroundColor(.red)
.font(.title)
@ -203,7 +197,7 @@ struct Connect: View {
}
.navigationTitle("Bluetooth Radios")
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") )
@ -215,8 +209,7 @@ struct Connect: View {
if bleManager.connectedPeripheral != nil && userSettings.preferredPeripheralId == bleManager.connectedPeripheral.peripheral.identifier.uuidString {
isPreferredRadio = true
}
else {
} else {
isPreferredRadio = false
}
})

View file

@ -16,7 +16,7 @@ struct ContentView: View {
}
var body: some View {
TabView(selection: $selection) {
Channels()
.tabItem {

View file

@ -4,46 +4,41 @@ struct BatteryIcon: View {
var batteryLevel: Int32?
var font: Font
var color: Color
var body: some View {
if batteryLevel == 100 {
Image(systemName: "battery.100.bolt")
.font(font)
.foregroundColor(color)
.symbolRenderingMode(.hierarchical)
}
else if batteryLevel! < 100 && batteryLevel! > 74 {
} else if batteryLevel! < 100 && batteryLevel! > 74 {
Image(systemName: "battery.75")
.font(font)
.foregroundColor(color)
.symbolRenderingMode(.hierarchical)
}
else if batteryLevel! < 75 && batteryLevel! > 49 {
} else if batteryLevel! < 75 && batteryLevel! > 49 {
Image(systemName: "battery.50")
.font(font)
.foregroundColor(color)
.symbolRenderingMode(.hierarchical)
}
else if batteryLevel! < 50 && batteryLevel! > 14 {
} else if batteryLevel! < 50 && batteryLevel! > 14 {
Image(systemName: "battery.25")
.font(font)
.foregroundColor(color)
.symbolRenderingMode(.hierarchical)
}
else if batteryLevel! == 0 {
} else if batteryLevel! == 0 {
Image(systemName: "powerplug")
.font(font)
.foregroundColor(color)
.symbolRenderingMode(.hierarchical)
}
else {
} else {
Image(systemName: "battery.0")
.font(font)
.foregroundColor(color)

View file

@ -11,7 +11,7 @@ struct ConnectedDevice: View {
var name: String?
var body: some View {
HStack {
if bluetoothOn {
@ -21,18 +21,16 @@ struct ConnectedDevice: View {
.foregroundColor(.green)
.symbolRenderingMode(.hierarchical)
Text(name!).font(.subheadline).foregroundColor(.gray)
}
else {
} else {
Image(systemName: "antenna.radiowaves.left.and.right.slash")
.imageScale(.medium)
.foregroundColor(.red)
.symbolRenderingMode(.hierarchical)
Text("Disconnected").font(.subheadline).foregroundColor(.gray)
}
}
else {
} else {
Text("Bluetooth Off").font(.subheadline).foregroundColor(.red)
}
}
@ -43,9 +41,9 @@ struct ConnectedDevice_Previews: PreviewProvider {
static var previews: some View {
ConnectedDevice(bluetoothOn: true, deviceConnected: false, name: "Yellow Beam")
.previewLayout(.fixed(width: 80, height: 70))
ConnectedDevice(bluetoothOn: true, deviceConnected: false, name: "Yellow Beam")
ConnectedDevice(bluetoothOn: true, deviceConnected: false, name: "Yellow Beam")
.previewLayout(.fixed(width: 80, height: 70))
}
}

View file

@ -1,42 +1,40 @@
import SwiftUI
struct MessageBubble: View {
@State var showAlert = false
var contentMessage: String
var isCurrentUser: Bool
var time: Int32
var shortName: String
var id: UInt32
var body: some View {
HStack (alignment: .top) {
HStack(alignment: .top) {
CircleText(text: shortName, color: isCurrentUser ? Color.blue : Color(.darkGray)).padding(.all, 5)
.gesture(LongPressGesture(minimumDuration: 2)
.onEnded {_ in
print("I want to delete message: \(id)")
self.showAlert = true
})
VStack (alignment: .leading) {
VStack(alignment: .leading) {
Text(contentMessage)
.textSelection(.enabled)
.padding(10)
.foregroundColor(.white)
.background(isCurrentUser ? Color.blue : Color(.darkGray))
.cornerRadius(10)
HStack (spacing: 4) {
HStack(spacing: 4) {
let messageDate = Date(timeIntervalSince1970: TimeInterval(time))
if time != 0 {
Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray)
Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray)
}
else {
} else {
Text("Unknown").font(.caption2).foregroundColor(.gray)
}
}
@ -46,11 +44,11 @@ struct MessageBubble: View {
}
.alert(isPresented: $showAlert) {
Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."),
primaryButton: .destructive (Text("OK")) {
primaryButton: .destructive(Text("OK")) {
print("OK button tapped")
//let messageIndex = meshData.nodes.firstIndex(where: { $0.id == node.id })
//meshData.nodes.remove(at: nodeIndex!)
//meshData.save()
// let messageIndex = meshData.nodes.firstIndex(where: { $0.id == node.id })
// meshData.nodes.remove(at: nodeIndex!)
// meshData.save()
},
secondaryButton: .cancel()
)
@ -66,5 +64,3 @@ struct MessageBubble_Previews: PreviewProvider {
.previewLayout(.fixed(width: 300, height: 100))
}
}

View file

@ -3,31 +3,30 @@ import SwiftUI
import CoreBluetooth
struct Channels: View {
@State private var isShowingDetailView = true
var body: some View {
NavigationView {
GeometryReader { bounds in
GeometryReader { _ in
NavigationLink(destination: Messages(), isActive: $isShowingDetailView) {
List{
List {
HStack {
Image(systemName: "dial.max.fill")
.font(.system(size: 62))
.symbolRenderingMode(.hierarchical)
.padding(.trailing)
.foregroundColor(.accentColor)
Text("Primary")
.font(.largeTitle)
}.padding()
}
}
@ -39,7 +38,7 @@ struct Channels: View {
}
struct MessageList_Previews: PreviewProvider {
static let meshData = MeshData()
static var previews: some View {

View file

@ -4,78 +4,76 @@ import Foundation
import CoreLocation
struct Messages: View {
enum Field: Hashable {
case messageText
}
// Keyboard State
@State var typingMessage: String = ""
@State private var totalBytes = 0
private var maxbytes = 228
@State private var lastTypingMessage = ""
@FocusState private var focusedField: Field?
@Namespace var topId
@Namespace var bottomId
@State var showDeleteMessageAlert = false
@State private var deleteMessageId : UInt32 = 0
@State private var deleteMessageId: UInt32 = 0
// Message Data and Bluetooth
@EnvironmentObject var bleManager: BLEManager
public var broadcastNodeId: UInt32 = 4294967295
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State var messageCount: Int = 0;
@State var messageCount: Int = 0
var body: some View {
Text("\(messageCount) Messages").font(.caption)
GeometryReader { bounds in
VStack {
ScrollViewReader { scrollView in
if self.bleManager.messageData.messages.count > 0 {
ScrollView {
Text("Hidden Top Anchor").hidden().frame(height: 0).id(topId)
ForEach(bleManager.messageData.messages.sorted(by: { $0.messageTimestamp < $1.messageTimestamp })) { message in
HStack (alignment: .top) {
HStack(alignment: .top) {
let currentUser: Bool = (bleManager.connectedNode != nil) && ((bleManager.connectedNode.id) == message.fromUserId)
CircleText(text: message.fromUserShortName, color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5)
.gesture(LongPressGesture(minimumDuration: 2)
.onEnded {_ in
print("I want to delete message: \(message.messageId)")
self.showDeleteMessageAlert = true
self.deleteMessageId = message.messageId
})
VStack (alignment: .leading) {
VStack(alignment: .leading) {
Text(message.messagePayload)
.textSelection(.enabled)
.padding(10)
.foregroundColor(.white)
.background(currentUser ? Color.blue : Color(.darkGray))
.cornerRadius(10)
HStack (spacing: 4) {
HStack(spacing: 4) {
let time = Int32(message.messageTimestamp)
let messageDate = Date(timeIntervalSince1970: TimeInterval(time))
if time != 0 {
Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray)
Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray)
}
else {
} else {
Text("Unknown").font(.caption2).foregroundColor(.gray)
}
}
@ -85,10 +83,10 @@ struct Messages: View {
}
.alert(isPresented: $showDeleteMessageAlert) {
Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."),
primaryButton: .destructive (Text("Delete")) {
primaryButton: .destructive(Text("Delete")) {
print("OK button tapped")
if deleteMessageId > 0 {
let messageIndex = bleManager.messageData.messages.firstIndex(where: { $0.messageId == deleteMessageId })
bleManager.messageData.messages.remove(at: messageIndex!)
bleManager.messageData.save()
@ -101,13 +99,13 @@ struct Messages: View {
)
}
}
.onAppear(perform: { scrollView.scrollTo(bottomId) } )
.onAppear(perform: { scrollView.scrollTo(bottomId) })
Text("Hidden Bottom Anchor").hidden().frame(height: 0).id(bottomId)
}
.onReceive(timer) { input in
.onReceive(timer) { _ in
if messageCount < bleManager.messageData.messages.count {
bleManager.messageData.load()
scrollView.scrollTo(bottomId)
messageCount = bleManager.messageData.messages.count
@ -116,9 +114,9 @@ struct Messages: View {
.padding(.horizontal)
}
}
HStack (alignment: .top) {
HStack(alignment: .top) {
ZStack {
let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0)
@ -130,24 +128,22 @@ struct Messages: View {
if totalBytes <= maxbytes {
// Allow the user to type
lastTypingMessage = typingMessage
}
else {
} else {
// Set the message back and remove the bytes over the count
self.typingMessage = lastTypingMessage
}
})
.keyboardType(kbType!)
.toolbar
{
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Dismiss Keyboard") {
focusedField = nil
}
.font(.subheadline)
Spacer()
ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
.frame(width: 130)
.padding(5)
@ -159,32 +155,30 @@ struct Messages: View {
.focused($focusedField, equals: .messageText)
.multilineTextAlignment(.leading)
.frame(minHeight: bounds.size.height / 4, maxHeight: bounds.size.height / 4)
Text(typingMessage).opacity(0).padding(.all, 0)
}
.overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1))
.padding(.bottom, 15)
Button(action: {
if bleManager.sendMessage(message: typingMessage) {
typingMessage = ""
}
else {
let _ = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { (timer) in
} else {
_ = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { (_) in
if bleManager.sendMessage(message: typingMessage) {
typingMessage = ""
}
}
}
} ) {
}) {
Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue)
}
}
.padding(.all, 15)
}
@ -192,13 +186,13 @@ struct Messages: View {
.navigationTitle("Channel - Primary")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") )
})
.onAppear {
messageCount = bleManager.messageData.messages.count
}
}

View file

@ -8,27 +8,27 @@ import MapKit
import CoreLocation
struct NodeDetail: View {
@EnvironmentObject var bleManager :BLEManager
@EnvironmentObject var bleManager: BLEManager
var node: NodeInfoModel
struct MapLocation: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}
var body: some View {
GeometryReader { bounds in
VStack {
if(node.position.coordinate != nil) {
if node.position.coordinate != nil {
let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: node.position.latitude!, longitude: node.position.longitude!)
let regionBinding = Binding<MKCoordinateRegion>(
get: {
MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
@ -36,7 +36,7 @@ struct NodeDetail: View {
set: { _ in }
)
let annotations = [MapLocation(name: node.user.shortName, coordinate: node.position.coordinate!)]
Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in
MapAnnotation(
coordinate: location.coordinate,
@ -44,29 +44,27 @@ struct NodeDetail: View {
CircleText(text: node.user.shortName, color: .accentColor)
}
)
}.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2)
}
else
{
}.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2)
} else {
Image(node.user.hwModel)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: bounds.size.width, height: bounds.size.height / 2)
}
ScrollView {
HStack {
VStack(alignment: .center) {
Text("AKA").font(.title2).fixedSize()
CircleText(text: node.user.shortName, color: .accentColor)
.offset(y:10)
.offset(y: 10)
}
.padding([.leading, .trailing, .bottom])
Divider()
if node.snr != nil && node.snr! > 0 {
VStack(alignment: .center) {
Image(systemName: "waveform.path")
.font(.title)
.foregroundColor(.accentColor)
@ -86,38 +84,37 @@ struct NodeDetail: View {
.font(.title2)
.foregroundColor(.gray)
.symbolRenderingMode(.hierarchical)
}
else {
} else {
Text("Powered").font(.title2)
}
}
}.padding(4)
Divider()
HStack {
Image(node.user.hwModel)
.resizable()
.frame(width:60, height: 60)
.frame(width: 60, height: 60)
.cornerRadius(5)
Text("Model: " + String(node.user.hwModel))
.font(.title3)
}
.padding()
Divider()
if node.lastHeard > 0 {
HStack{
HStack {
Image(systemName: "clock").font(.title2).foregroundColor(.accentColor)
let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard))
Text("Last Heard: \(lastHeard, style: .relative) ago").font(.title3)
}.padding()
Divider()
}
if node.position.coordinate != nil {
HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) {
Image(systemName: "mappin").font(.title).foregroundColor(.accentColor)
@ -138,9 +135,9 @@ struct NodeDetail: View {
}.padding()
Divider()
}
HStack (alignment: .center) {
HStack(alignment: .center) {
VStack {
HStack{
HStack {
Image(systemName: "person").font(.title3).foregroundColor(.accentColor)
Text("Unique Id:").font(.title3)
}
@ -159,15 +156,14 @@ struct NodeDetail: View {
}.navigationTitle(node.user.longName)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
ZStack {
//ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") )
// ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") )
})
}.ignoresSafeArea(.all, edges: [.leading, .trailing])
}
}
struct NodeDetail_Previews: PreviewProvider {
static let bleManager = BLEManager()

View file

@ -11,13 +11,13 @@
import SwiftUI
struct NodeList: View {
@EnvironmentObject var bleManager: BLEManager
@State private var selection: String? = nil
@State private var selection: String?
@State private var showLocationOnly = false
var filteredDevices: [NodeInfoModel] {
bleManager.meshData.nodes.filter { node in
(!showLocationOnly || node.position.coordinate != nil)
@ -26,52 +26,49 @@ struct NodeList: View {
var body: some View {
NavigationView {
List {
if bleManager.meshData.nodes.count == 0 {
Text("Scan for Radios").font(.largeTitle)
//.listRowSeparator(.hidden)
// .listRowSeparator(.hidden)
Text("No LoRa Mesh Nodes Found").font(.title2)
//.listRowSeparator(.hidden)
// .listRowSeparator(.hidden)
Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.")
.font(.body)
//.listRowSeparator(.hidden)
// .listRowSeparator(.hidden)
Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.")
//.listRowSeparator(.hidden)
// .listRowSeparator(.hidden)
Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.")
//.listRowSeparator(.hidden)
// .listRowSeparator(.hidden)
Spacer()
//.listRowSeparator(.hidden)
}
else {
// .listRowSeparator(.hidden)
} else {
Toggle(isOn: $showLocationOnly) {
Text("Nodes with location only")
}
ForEach(filteredDevices.sorted(by: { $0.lastHeard > $1.lastHeard })) { node in
let index = filteredDevices.sorted(by: { $0.lastHeard > $1.lastHeard }).firstIndex(where: { $0.id == node.id })
NavigationLink(destination: NodeDetail(node: node), tag: String(index!), selection: $selection) {
if(bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.myInfo != nil) {
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.myInfo != nil {
let connected: Bool = (bleManager.connectedPeripheral.myInfo!.myNodeNum == node.id)
NodeRow(node: node, connected: connected)
}
else {
} else {
NodeRow(node: node, connected: false)
}
}
.swipeActions (edge: .trailing) {
Button (role: .destructive) {
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
let nodeIndex = bleManager.meshData.nodes.firstIndex(where: { $0.num == node.num })
bleManager.meshData.nodes.remove(at: nodeIndex!)
bleManager.meshData.save()
} label: {
Label("Delete from app", systemImage: "trash")
}
}

View file

@ -11,9 +11,9 @@ import MapKit
import CoreLocation
struct NodeMap: View {
@EnvironmentObject var bleManager :BLEManager
@EnvironmentObject var bleManager: BLEManager
var locationNodes: [NodeInfoModel] {
bleManager.meshData.nodes.filter { node in
(node.position.coordinate != nil)
@ -24,7 +24,7 @@ struct NodeMap: View {
let name: String
let coordinate: CLLocationCoordinate2D
}
var body: some View {
let location = LocationHelper.currentLocation
@ -35,15 +35,15 @@ struct NodeMap: View {
},
set: { _ in }
)
NavigationView {
ZStack {
Map(coordinateRegion: regionBinding,
interactionModes: [.all],
showsUserLocation: true,
userTrackingMode: .constant(.follow), annotationItems: locationNodes) { location in
MapAnnotation(
coordinate: location.position.coordinate!,
content: {
@ -51,17 +51,17 @@ struct NodeMap: View {
}
)
}
.frame(maxHeight:.infinity)
.frame(maxHeight: .infinity)
.ignoresSafeArea(.all, edges: [.leading, .trailing])
}
.navigationTitle("Mesh Map")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
ZStack {
// ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") )
})
}
.navigationViewStyle(StackNavigationViewStyle())
}

View file

@ -5,51 +5,45 @@ struct NodeRow: View {
var connected: Bool
var body: some View {
VStack (alignment: .leading) {
HStack() {
VStack(alignment: .leading) {
HStack {
CircleText(text: node.user.shortName, color: Color.accentColor).offset(y: 1).padding(.trailing, 5)
.offset(x: -15)
if UIDevice.current.userInterfaceIdiom == .pad {
Text(node.user.longName).font(.headline)
.offset(x: -15)
}
else {
} else {
Text(node.user.longName).font(.title)
.offset(x: -15)
}
}
.padding(.bottom, 10)
HStack (alignment: .bottom){
HStack(alignment: .bottom) {
Image(systemName: "clock.badge.checkmark.fill").font(.headline).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
if UIDevice.current.userInterfaceIdiom == .pad {
if connected {
Text("Currently Connected").font(.caption).foregroundColor(Color.accentColor)
}
else if node.lastHeard > 0 {
} else if node.lastHeard > 0 {
let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard))
Text("Last Heard: \(lastHeard, style: .relative) ago").font(.caption).foregroundColor(.gray)
}
else {
} else {
Text("Last Heard: Unknown").font(.caption).foregroundColor(.gray)
}
} else {
if connected {
Text("Currently Connected").font(.subheadline).foregroundColor(Color.accentColor)
}
else if node.lastHeard > 0 {
} else if node.lastHeard > 0 {
let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard))
Text("Last Heard: \(lastHeard, style: .relative) ago").font(.subheadline).foregroundColor(.gray)
}
else {
} else {
Text("Last Heard: Unknown").font(.subheadline).foregroundColor(.gray)
}
}

View file

@ -4,13 +4,13 @@ import SwiftUI
import SwiftProtobuf
enum KeyboardType: Int, CaseIterable, Identifiable {
case defaultKeyboard = 0
case asciiCapable = 1
case twitter = 9
case emailAddress = 7
case numbersAndPunctuation = 2
var id: Int { self.rawValue }
var description: String {
get {
@ -31,11 +31,11 @@ enum KeyboardType: Int, CaseIterable, Identifiable {
}
class UserSettings: ObservableObject {
//@Published var meshtasticUsername: String {
// @Published var meshtasticUsername: String {
// didSet {
// UserDefaults.standard.set(meshtasticUsername, forKey: "meshtasticusername")
// }
//}
// }
@Published var preferredPeripheralName: String {
didSet {
UserDefaults.standard.set(preferredPeripheralName, forKey: "preferredPeripheralName")
@ -61,9 +61,9 @@ class UserSettings: ObservableObject {
UserDefaults.standard.set(meshActivityLog, forKey: "meshActivityLog")
}
}
init() {
//self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
// self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
self.preferredPeripheralName = UserDefaults.standard.object(forKey: "preferredPeripheralName") as? String ?? ""
self.preferredPeripheralId = UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? ""
self.provideLocation = UserDefaults.standard.object(forKey: "provideLocation") as? Bool ?? false
@ -73,33 +73,33 @@ class UserSettings: ObservableObject {
}
struct AppSettings: View {
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
@State private var preferredDeviceConnected = false
var perferredPeripheral: String {
UserDefaults.standard.object(forKey: "preferredPeripheralName") as? String ?? ""
}
var body: some View {
NavigationView {
GeometryReader { bounds in
GeometryReader { _ in
List {
Section(header: Text("USER DETAILS")) {
//HStack {
// HStack {
// Label("Name", systemImage: "person.crop.rectangle.fill")
// TextField("Username", text: $userSettings.meshtasticUsername)
// .foregroundColor(.gray)
//}
//.listRowSeparator(.visible)
// }
// .listRowSeparator(.visible)
Toggle(isOn: $userSettings.provideLocation) {
Label("Provide location to mesh", systemImage: "location.circle.fill")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -114,7 +114,7 @@ struct AppSettings: View {
Text("The preferred radio will automatically reconnect if it becomes disconnected and is still within range. This device is assumed to be the primary radio used for messaging.")
.font(.caption2)
.foregroundColor(.gray)
}
Section(header: Text("MESSAGING OPTIONS")) {
@ -127,7 +127,7 @@ struct AppSettings: View {
}
Section(header: Text("MESH NETWORK OPTIONS")) {
Toggle(isOn: $userSettings.meshActivityLog) {
Label("Log all Mesh activity", systemImage: "network")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -142,10 +142,9 @@ struct AppSettings: View {
}
.navigationTitle("App Settings")
.navigationBarItems(trailing:
ZStack {
//ConnectedDevice(bluetoothOn: self.bleManager.isSwitchedOn, deviceConnected: self.bleManager.connectedPeripheral != nil, name: (self.bleManager.connectedNode != nil) ? self.bleManager.connectedNode.user.shortName : ((self.bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.name : "Unknown") )
// ConnectedDevice(bluetoothOn: self.bleManager.isSwitchedOn, deviceConnected: self.bleManager.connectedPeripheral != nil, name: (self.bleManager.connectedNode != nil) ? self.bleManager.connectedNode.user.shortName : ((self.bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.name : "Unknown") )
})
}
.navigationViewStyle(StackNavigationViewStyle())

View file

@ -1,24 +1,24 @@
import SwiftUI
import UniformTypeIdentifiers
struct LogDocument: FileDocument{
static var readableContentTypes:[UTType] {[.plainText]}
struct LogDocument: FileDocument {
static var readableContentTypes: [UTType] {[.plainText]}
var logFile: String
init(logFile: String){
init(logFile: String) {
self.logFile = logFile
}
init(configuration: ReadConfiguration) throws{
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let string = String(data: data, encoding: .utf8)
else{
else {
throw CocoaError(.fileReadCorruptFile)
}
logFile = string
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
return FileWrapper(regularFileWithContents: logFile.data(using: .utf8)!)
}

View file

@ -8,13 +8,13 @@ struct MeshLog: View {
@State private var logs = [String]()
@State private var isExporting: Bool = false
@State private var document: LogDocument = LogDocument(logFile: "MESHTASTIC MESH ACTIVITY LOG\n")
var body: some View {
List(logs, id: \.self, rowContent: Text.init)
.task {
do {
let url = logFile!
logs.removeAll()
for try await log in url.lines {
@ -34,7 +34,7 @@ struct MeshLog: View {
onCompletion: {
result in
if case .success = result {
print("Mesh activity log download: success.")
} else {
@ -42,12 +42,11 @@ struct MeshLog: View {
}
}
)
.textSelection(.enabled)
.font(.caption2)
HStack (alignment: .center) {
HStack(alignment: .center) {
Spacer()
Button(action: {
let text = ""
@ -57,7 +56,7 @@ struct MeshLog: View {
} catch {
print(error)
}
}) {
Image(systemName: "trash").imageScale(.large).foregroundColor(.gray)
Text("Clear Log").font(.caption)
@ -67,9 +66,9 @@ struct MeshLog: View {
.padding()
.background(Color(.systemGray6))
.clipShape(Capsule())
Spacer()
Button(action: {
isExporting = true
}) {
@ -81,9 +80,9 @@ struct MeshLog: View {
.padding()
.background(Color(.systemGray6))
.clipShape(Capsule())
Spacer()
}
.padding(.bottom, 10)
.navigationTitle("Mesh Activity Log")

View file

@ -6,7 +6,7 @@
//
import XCTest
//@testable import MeshtasticClient
// @testable import MeshtasticClient
class MeshtasticClientTests: XCTestCase {