From 79851345fa94e1f3a88885bae366df8155e741f6 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 20 Sep 2021 22:29:10 -0700 Subject: [PATCH] iOS 15 updates, icons fixes, new data models --- Meshtastic Client.xcodeproj/project.pbxproj | 46 ++++++++--- MeshtasticClient/Helpers/BLEManager.swift | 24 +++++- MeshtasticClient/Helpers/LocationHelper.swift | 11 ++- MeshtasticClient/MeshtasticClientApp.swift | 9 +- MeshtasticClient/Model/Color.swift | 82 +++++++++++++++++++ MeshtasticClient/Model/MeshData.swift | 49 +++++++++++ MeshtasticClient/Model/ModelData.swift | 8 +- MeshtasticClient/Model/NodeInfoModel.swift | 78 +++++++++++++++--- MeshtasticClient/Model/NodeInfoModel3.swift | 77 +++++++++++++++++ .../Views/Bluetooth/Connect.swift | 13 ++- MeshtasticClient/Views/ContentView.swift | 6 +- .../Views/Helpers/BatteryIcon.swift | 5 ++ .../Views/Messages/MessageList.swift | 3 +- MeshtasticClient/Views/Nodes/NodeDetail.swift | 2 + MeshtasticClient/Views/Nodes/NodeList.swift | 4 +- MeshtasticClient/Views/Nodes/NodeMap.swift | 4 +- .../MeshtasticClientTests.swift | 2 +- 17 files changed, 373 insertions(+), 50 deletions(-) create mode 100644 MeshtasticClient/Model/Color.swift create mode 100644 MeshtasticClient/Model/MeshData.swift create mode 100644 MeshtasticClient/Model/NodeInfoModel3.swift diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 9ec062ba..a3e9e4bd 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -16,14 +16,17 @@ DD47E3DB26F3901B00029299 /* MessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DA26F3901A00029299 /* MessageList.swift */; }; DD47E3DD26F390A000029299 /* MessageDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DC26F390A000029299 /* MessageDetail.swift */; }; DD47E3DF26F39D9F00029299 /* MyInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */; }; - DD7AA3F326F05C120077AF76 /* NodeInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */; }; + DD7AA3F326F05C120077AF76 /* NodeInfoModel3.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7AA3F226F05C120077AF76 /* NodeInfoModel3.swift */; }; DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AE626F6B38600ABCC23 /* Connect.swift */; }; DD836AEB26F7AF9000ABCC23 /* nodeinfomodel2.json in Resources */ = {isa = PBXBuildFile; fileRef = DD836AEA26F7AF9000ABCC23 /* nodeinfomodel2.json */; }; + DD836AED26F858F900ABCC23 /* MeshData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AEC26F858F900ABCC23 /* MeshData.swift */; }; + DD836AEF26F85D8D00ABCC23 /* NodeInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */; }; + DD836AF126F8613500ABCC23 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AF026F8613500ABCC23 /* Color.swift */; }; + DD8EDE9426F97A2B00A5A10B /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD8EDE9326F97A2B00A5A10B /* SwiftProtobuf */; }; DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; }; DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; }; DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5226EB1DF10058C060 /* BLEManager.swift */; }; DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */; }; - DDAF8C5B26ED08D30058C060 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DDAF8C5A26ED08D30058C060 /* SwiftProtobuf */; }; DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5C26ED09490058C060 /* portnums.pb.swift */; }; DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5E26ED09B50058C060 /* radioconfig.pb.swift */; }; DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6026ED0A230058C060 /* mqtt.pb.swift */; }; @@ -72,10 +75,13 @@ DD47E3DA26F3901A00029299 /* MessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageList.swift; sourceTree = ""; }; DD47E3DC26F390A000029299 /* MessageDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDetail.swift; sourceTree = ""; }; DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoModel.swift; sourceTree = ""; }; - DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = ""; }; + DD7AA3F226F05C120077AF76 /* NodeInfoModel3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel3.swift; sourceTree = ""; }; DD7AA3F426F05D660077AF76 /* nodeinfomodel.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = nodeinfomodel.json; sourceTree = ""; }; DD836AE626F6B38600ABCC23 /* Connect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connect.swift; sourceTree = ""; }; DD836AEA26F7AF9000ABCC23 /* nodeinfomodel2.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = nodeinfomodel2.json; sourceTree = ""; }; + DD836AEC26F858F900ABCC23 /* MeshData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshData.swift; sourceTree = ""; }; + DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = ""; }; + DD836AF026F8613500ABCC23 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; DD90860A26F645B700DC5189 /* MeshtasticClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticClient.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -114,7 +120,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DDAF8C5B26ED08D30058C060 /* SwiftProtobuf in Frameworks */, + DD8EDE9426F97A2B00A5A10B /* SwiftProtobuf in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -154,6 +160,13 @@ path = Bluetooth; sourceTree = ""; }; + DD8EDE9226F97A2B00A5A10B /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; DDAF8C5626ED07740058C060 /* Protobufs */ = { isa = PBXGroup; children = ( @@ -177,6 +190,7 @@ DDC2E16D26CE248F0042C5E4 /* MeshtasticClientTests */, DDC2E17826CE248F0042C5E4 /* MeshtasticClientUITests */, DDC2E15526CE248E0042C5E4 /* Products */, + DD8EDE9226F97A2B00A5A10B /* Frameworks */, ); sourceTree = ""; }; @@ -249,8 +263,11 @@ isa = PBXGroup; children = ( DDC2E19C26CE27580042C5E4 /* ModelData.swift */, - DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */, + DD7AA3F226F05C120077AF76 /* NodeInfoModel3.swift */, DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */, + DD836AEC26F858F900ABCC23 /* MeshData.swift */, + DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */, + DD836AF026F8613500ABCC23 /* Color.swift */, ); path = Model; sourceTree = ""; @@ -314,7 +331,7 @@ ); name = MeshtasticClient; packageProductDependencies = ( - DDAF8C5A26ED08D30058C060 /* SwiftProtobuf */, + DD8EDE9326F97A2B00A5A10B /* SwiftProtobuf */, ); productName = MeshtasticClient; productReference = DDC2E15426CE248E0042C5E4 /* MeshtasticClient.app */; @@ -437,8 +454,10 @@ DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */, DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */, DD47E3CC26F0E51D00029299 /* NodeDetail.swift in Sources */, + DD836AEF26F85D8D00ABCC23 /* NodeInfoModel.swift in Sources */, DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */, - DD7AA3F326F05C120077AF76 /* NodeInfoModel.swift in Sources */, + DD836AF126F8613500ABCC23 /* Color.swift in Sources */, + DD7AA3F326F05C120077AF76 /* NodeInfoModel3.swift in Sources */, DD47E3D226F1210600029299 /* HelperFunctions.swift in Sources */, DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, @@ -446,6 +465,7 @@ DDC2E19126CE26290042C5E4 /* Messages.swift in Sources */, DD47E3DB26F3901B00029299 /* MessageList.swift in Sources */, DDAF8C6926ED0D070058C060 /* deviceonly.pb.swift in Sources */, + DD836AED26F858F900ABCC23 /* MeshData.swift in Sources */, DDC2E19D26CE27580042C5E4 /* ModelData.swift in Sources */, DDAF8C6B26ED0DD80058C060 /* environmental_measurement.pb.swift in Sources */, DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */, @@ -619,18 +639,19 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\""; DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = MeshtasticClient/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.06; + MARKETING_VERSION = 1.091; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = NO; @@ -644,18 +665,19 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\""; DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = MeshtasticClient/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.06; + MARKETING_VERSION = 1.091; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = NO; @@ -801,7 +823,7 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - DDAF8C5A26ED08D30058C060 /* SwiftProtobuf */ = { + DD8EDE9326F97A2B00A5A10B /* SwiftProtobuf */ = { isa = XCSwiftPackageProductDependency; package = DDAF8C5926ED08D30058C060 /* XCRemoteSwiftPackageReference "swift-protobuf" */; productName = SwiftProtobuf; diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index 8118bc1b..dbc109cf 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -23,6 +23,8 @@ final class Peripheral: Identifiable, ObservableObject { class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate { // Data + // @EnvironmentObject var meshData: MeshData + @EnvironmentObject private var meshData : MeshData private var centralManager: CBCentralManager! @Published var connectedPeripheral: CBPeripheral! @Published var peripheralArray = [CBPeripheral]() @@ -38,11 +40,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph 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") //Notify + let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453") override init() { + super.init() - centralManager = CBCentralManager(delegate: self, queue: nil) centralManager.delegate = self @@ -257,6 +259,24 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph { print("Save a nodeInfo") do { + // meshData.nodes.append( + // NodeInfoModel(num: decodedInfo.nodeInfo.num, + // user: NodeInfoModel.User(id: decodedInfo.nodeInfo.user.id, + // longName: decodedInfo.nodeInfo.user.longName, + // shortName: decodedInfo.nodeInfo.user.shortName + //macaddr: "", + //hwModel: String((HardwareModel)decodedInfo.nodeInfo.user.hwModel) + // ), + // position: NodeInfoModel.Position(latitudeI:nil, + // longitudeI: nil, + // altitude: nil, + // batteryLevel: 68, + // time: nil), + // lastHeard: 1631593661, + // snr: nil) + // ) + // meshData.save() + print(try decodedInfo.nodeInfo.jsonString()) } catch { fatalError("Failed to decode json") diff --git a/MeshtasticClient/Helpers/LocationHelper.swift b/MeshtasticClient/Helpers/LocationHelper.swift index 1726011e..902cb137 100644 --- a/MeshtasticClient/Helpers/LocationHelper.swift +++ b/MeshtasticClient/Helpers/LocationHelper.swift @@ -1,16 +1,14 @@ import CoreLocation -struct MyAnnotationItem: Identifiable { - var coordinate: CLLocationCoordinate2D - let id = UUID() -} - class LocationHelper: NSObject, ObservableObject { static let shared = LocationHelper() - static let DefaultLocation = CLLocationCoordinate2D(latitude: 51.506520923981554, longitude: -0.10689139236939127) + + // 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 } @@ -20,6 +18,7 @@ class LocationHelper: NSObject, ObservableObject { private let locationManager = CLLocationManager() private override init() { + super.init() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest diff --git a/MeshtasticClient/MeshtasticClientApp.swift b/MeshtasticClient/MeshtasticClientApp.swift index d98de677..d1df6fbf 100644 --- a/MeshtasticClient/MeshtasticClientApp.swift +++ b/MeshtasticClient/MeshtasticClientApp.swift @@ -9,12 +9,17 @@ import SwiftUI @main struct MeshtasticClientApp: App { - @StateObject private var modelData = ModelData() + + @ObservedObject private var meshData = MeshData() var body: some Scene { WindowGroup { ContentView() - .environmentObject(modelData) + .environmentObject(meshData) + .onAppear{ + meshData.load() + } + } } } diff --git a/MeshtasticClient/Model/Color.swift b/MeshtasticClient/Model/Color.swift new file mode 100644 index 00000000..ef8f131a --- /dev/null +++ b/MeshtasticClient/Model/Color.swift @@ -0,0 +1,82 @@ +/* +See LICENSE folder for this sample’s licensing information. +*/ + +import SwiftUI + +extension Color: Codable { + private struct Components { + let red: Double + let green: Double + let blue: Double + let alpha: Double + } + + private enum CodingKeys: String, CodingKey { + case red + case green + case blue + case alpha + } + + /// A new random color. + static var random: Color { + let red = Double.random(in: 0...1) + let green = Double.random(in: 0...1) + let blue = Double.random(in: 0...1) + return Color(.sRGB, red: red, green: green, blue: blue, opacity: 1) + } + + private var components: Components { + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: &alpha) + return Components(red: Double(red), + green: Double(green), + blue: Double(blue), + alpha: Double(alpha)) + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let red = try container.decode(Double.self, forKey: .red) + let green = try container.decode(Double.self, forKey: .green) + let blue = try container.decode(Double.self, forKey: .blue) + let alpha = try container.decode(Double.self, forKey: .alpha) + self.init(Components(red: red, green: green, blue: blue, alpha: alpha)) + } + + private init(_ components: Components) { + self.init(.sRGB, red: components.red, green: components.green, blue: components.blue, opacity: components.alpha) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + let components = self.components + try container.encode(components.red, forKey: .red) + try container.encode(components.green, forKey: .green) + try container.encode(components.blue, forKey: .blue) + try container.encode(components.alpha, forKey: .alpha) + } + + // MARK: - font colors + /// This color is either black or white, whichever is more accessible when viewed against the scrum color. + var accessibleFontColor: Color { + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: nil) + return isLightColor(red: red, green: green, blue: blue) ? .black : .white + } + + private func isLightColor(red: CGFloat, green: CGFloat, blue: CGFloat) -> Bool { + let lightRed = red > 0.65 + let lightGreen = green > 0.65 + let lightBlue = blue > 0.65 + + let lightness = [lightRed, lightGreen, lightBlue].reduce(0) { $1 ? $0 + 1 : $0 } + return lightness >= 2 + } +} diff --git a/MeshtasticClient/Model/MeshData.swift b/MeshtasticClient/Model/MeshData.swift new file mode 100644 index 00000000..f5f6975f --- /dev/null +++ b/MeshtasticClient/Model/MeshData.swift @@ -0,0 +1,49 @@ +import Foundation + +class MeshData: ObservableObject { + private static var documentsFolder: URL { + do { + return try FileManager.default.url(for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: false) + } catch { + fatalError("Can't find documents directory.") + } + } + private static var fileURL: URL { + return documentsFolder.appendingPathComponent("scrums.data") + } + @Published var nodes: [NodeInfoModel] = [] + + func load() { + DispatchQueue.global(qos: .background).async { [weak self] in + guard let data = try? Data(contentsOf: Self.fileURL) else { + #if DEBUG + DispatchQueue.main.async { + self?.nodes = NodeInfoModel.data + } + #endif + return + } + guard let nodeList = try? JSONDecoder().decode([NodeInfoModel].self, from: data) else { + fatalError("Can't decode saved node data.") + } + DispatchQueue.main.async { + self?.nodes = nodeList + } + } + } + func save() { + DispatchQueue.global(qos: .background).async { [weak self] in + guard let scrums = self?.nodes else { fatalError("Self out of scope") } + guard let data = try? JSONEncoder().encode(scrums) else { fatalError("Error encoding data") } + do { + let outfile = Self.fileURL + try data.write(to: outfile) + } catch { + fatalError("Can't write to file") + } + } + } +} diff --git a/MeshtasticClient/Model/ModelData.swift b/MeshtasticClient/Model/ModelData.swift index 7ad2a671..91e3ca5e 100644 --- a/MeshtasticClient/Model/ModelData.swift +++ b/MeshtasticClient/Model/ModelData.swift @@ -5,15 +5,13 @@ Storage for model data. import Foundation import Combine -import CoreBluetooth -import CoreData final class ModelData: ObservableObject { - @Published var nodes: [NodeInfoModel] = load("packets.json") + + @Published var nodes: [NodeInfoModel] = load("nodeinfomodel.json") - // @ObservedObject var connectedPeripheral: CBPeripheral! - //@Published var connectedPeripheral: CBPeripheral! + } func load(_ filename: String) -> T { diff --git a/MeshtasticClient/Model/NodeInfoModel.swift b/MeshtasticClient/Model/NodeInfoModel.swift index 0afcc0c7..ab0e7d22 100644 --- a/MeshtasticClient/Model/NodeInfoModel.swift +++ b/MeshtasticClient/Model/NodeInfoModel.swift @@ -1,18 +1,12 @@ -// -// NodeInfo.swift -// MeshtasticClient -// -// Created by Garth Vander Houwen on 9/13/21. -// - -import Foundation +/* +See LICENSE folder for this sample’s licensing information. +*/ import SwiftUI import CoreLocation -import CoreData struct NodeInfoModel: Identifiable, Codable { - let id = UUID() + let id: UUID var num: UInt32 var user: User @@ -20,10 +14,15 @@ struct NodeInfoModel: Identifiable, Codable { var id: String var longName: String var shortName: String - var macaddr: String var hwModel: String + + init(id: String, longName: String, shortName: String, hwModel: String) { + self.id = id + self.longName = longName + self.shortName = shortName + self.hwModel = hwModel + } } - var position: Position struct Position: Codable { var latitudeI: Int32? @@ -61,8 +60,63 @@ struct NodeInfoModel: Identifiable, Codable { var altitude: Int32? var batteryLevel: Int32? var time: Int32? + + init(latitudeI: Int32?, longitudeI: Int32?, altitude: Int32?, batteryLevel: Int32?, time: Int32? ) { + self.latitudeI = latitudeI + self.longitudeI = longitudeI + self.altitude = altitude + self.batteryLevel = batteryLevel + self.time = time + } } var lastHeard: Double var snr: Double? + + + init(id: UUID = UUID(), num: UInt32, user: User, position: Position, lastHeard: Double, snr: Double?) { + self.id = id + self.num = num + self.user = user + self.position = position + self.lastHeard = lastHeard + self.snr = snr + } +} + +extension NodeInfoModel { + //var user = User(id: "!a66c166f", longName: "RAK Solar 2", shortName: "RS2", macaddr:"8eambBZv", hwModel:"RAK4631") + //let position = Position(batteryLevel: 68) + static var data: [NodeInfoModel] { + [ + + NodeInfoModel(num: 2792101487, user: User(id: "!a66c166f", longName: "RAK Solar 2", shortName: "RS2", hwModel: "RAK4631"), position: Position(latitudeI:nil, longitudeI: nil, altitude: nil, batteryLevel: 68, time: nil), lastHeard: 1631593661, snr: nil), + NodeInfoModel(num: 1000569662, user: User(id: "!3ba37b3e", longName: "RAK Solar 1", shortName: "RS1", hwModel: "RAK4631"), position: Position(latitudeI:476021390, longitudeI: -1221532609, altitude: 71, batteryLevel: 70, time: 1629314497), lastHeard: 1629392801, snr: 5.25) + + + ] + } +} + +extension NodeInfoModel { + struct Data { + var num: UInt32 = 0 + var user: User + var postion: Position + var lastHeard: Double + var snr: Double? + + } + + var data: Data { + return Data(num: num, user: user, postion: position, lastHeard: lastHeard, snr: snr) + } + + mutating func update(from data: Data) { + num = data.num + user = data.user + position = data.postion + lastHeard = data.lastHeard + snr = data.snr + } } diff --git a/MeshtasticClient/Model/NodeInfoModel3.swift b/MeshtasticClient/Model/NodeInfoModel3.swift new file mode 100644 index 00000000..6c7a7afc --- /dev/null +++ b/MeshtasticClient/Model/NodeInfoModel3.swift @@ -0,0 +1,77 @@ +// +// NodeInfo.swift +// MeshtasticClient +// +// Created by Garth Vander Houwen on 9/13/21. +// + +import Foundation +import SwiftUI +import CoreLocation +import CoreData + +struct NodeInfoModel3: Identifiable, Codable { + + let id: UUID + var num: UInt32 + + var user: User + struct User: Identifiable, Codable { + var id: String + var longName: String + var shortName: String + var macaddr: String + var hwModel: String + } + + var position: Position + struct Position: Codable { + var latitudeI: Int32? + var latitude: Double? { + if let unwrappedLat = latitudeI { + let d = Double(unwrappedLat) + + return d / 1e7 + } + else { + return nil + } + } + var longitudeI: Int32? + var longitude: Double? { + if let unwrappedLong = longitudeI { + let d = Double(unwrappedLong) + + return d / 1e7 + } + else { + return nil + } + } + var coordinate: CLLocationCoordinate2D? { + if longitude != nil { + let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!) + + return coord + } + else { + return nil + } + } + var altitude: Int32? + var batteryLevel: Int32? + var time: Int32? + } + + var lastHeard: Double + var snr: Double? + + init(id: UUID = UUID(), num: UInt32, user: User, position: Position, lastHeard: Double, snr: Double?) { + self.id = id + self.num = num + self.user = user + self.position = position + self.lastHeard = lastHeard + self.snr = snr + } +} diff --git a/MeshtasticClient/Views/Bluetooth/Connect.swift b/MeshtasticClient/Views/Bluetooth/Connect.swift index 63f0d9cd..32d3a856 100644 --- a/MeshtasticClient/Views/Bluetooth/Connect.swift +++ b/MeshtasticClient/Views/Bluetooth/Connect.swift @@ -25,20 +25,20 @@ struct Connect: View { if bleManager.isSwitchedOn { List { - Section(header: Text("Connected Device").font(.largeTitle)) { + Section(header: Text("Connected Device").font(.title)) { if(bleManager.connectedPeripheral != nil){ HStack{ Image(systemName: "antenna.radiowaves.left.and.right").imageScale(.large).foregroundColor(.green) - Text((bleManager.connectedPeripheral.name != nil) ? bleManager.connectedPeripheral.name! : "Unknown").font(.title) + Text((bleManager.connectedPeripheral.name != nil) ? bleManager.connectedPeripheral.name! : "Unknown").font(.title2) } } else { - Text("No device connected").font(.title) + Text("No device connected").font(.title2) } }.textCase(nil) - Section(header: Text("Other Meshtastic Devices").font(.title)) { + Section(header: Text("New Devices").font(.title)) { ForEach(bleManager.peripherals.sorted(by: { $0.rssi > $1.rssi })) { peripheral in HStack { Image(systemName: "circle.fill").imageScale(.large).foregroundColor(.gray) @@ -54,6 +54,11 @@ struct Connect: View { } } }.textCase(nil) + + Section(header: Text("Known Devices").font(.title)) { + + }.textCase(nil) + } HStack (alignment: .center) { diff --git a/MeshtasticClient/Views/ContentView.swift b/MeshtasticClient/Views/ContentView.swift index e6506302..a9b66273 100644 --- a/MeshtasticClient/Views/ContentView.swift +++ b/MeshtasticClient/Views/ContentView.swift @@ -19,22 +19,26 @@ struct ContentView: View { TabView(selection: $selection) { MessageList() .tabItem { - Label("Messages", systemImage: "message") + Label("Messages", systemImage: "text.bubble") + .symbolRenderingMode(.hierarchical) } .tag(Tab.messages) NodeMap() .tabItem { Label("Mesh Map", systemImage: "map") + .symbolRenderingMode(.hierarchical) } .tag(Tab.map) NodeList() .tabItem { Label("Nodes", systemImage: "flipphone") + .symbolRenderingMode(.hierarchical) } .tag(Tab.nodes) Connect() .tabItem { Label("Bluetooth", systemImage: "dot.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) } .tag(Tab.ble) } diff --git a/MeshtasticClient/Views/Helpers/BatteryIcon.swift b/MeshtasticClient/Views/Helpers/BatteryIcon.swift index a5ddc85b..8697276a 100644 --- a/MeshtasticClient/Views/Helpers/BatteryIcon.swift +++ b/MeshtasticClient/Views/Helpers/BatteryIcon.swift @@ -12,30 +12,35 @@ struct BatteryIcon: View { Image(systemName: "battery.100") .font(font) .foregroundColor(color) + .symbolRenderingMode(.hierarchical) } else if batteryLevel! < 100 && batteryLevel! > 74 { Image(systemName: "battery.75") .font(font) .foregroundColor(color) + .symbolRenderingMode(.hierarchical) } else if batteryLevel! < 75 && batteryLevel! > 49 { Image(systemName: "battery.50") .font(font) .foregroundColor(color) + .symbolRenderingMode(.hierarchical) } else if batteryLevel! < 50 && batteryLevel! > 14 { Image(systemName: "battery.25") .font(font) .foregroundColor(color) + .symbolRenderingMode(.hierarchical) } else { Image(systemName: "battery.0") .font(font) .foregroundColor(color) + .symbolRenderingMode(.hierarchical) } } } diff --git a/MeshtasticClient/Views/Messages/MessageList.swift b/MeshtasticClient/Views/Messages/MessageList.swift index 9a891741..72744ffc 100644 --- a/MeshtasticClient/Views/Messages/MessageList.swift +++ b/MeshtasticClient/Views/Messages/MessageList.swift @@ -16,7 +16,8 @@ struct MessageList: View { GeometryReader { bounds in ScrollView { - + Text(String(bleManager.isSwitchedOn)) + Text(String(bleManager.connectedPeripheral != nil)) }.padding(.all) } diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index 5e9047ac..fb10e8ec 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -81,6 +81,7 @@ struct NodeDetail: View { Image(systemName: "waveform.path") .font(.title2) .foregroundColor(.blue) + .symbolRenderingMode(.hierarchical) Text("SNR").font(.title3) Text(String(node.snr ?? 0)) .font(.title3) @@ -93,6 +94,7 @@ struct NodeDetail: View { Text(String(node.position.batteryLevel!) + "%") .font(.title3) .foregroundColor(.gray) + .symbolRenderingMode(.hierarchical) } }.padding(4) Divider() diff --git a/MeshtasticClient/Views/Nodes/NodeList.swift b/MeshtasticClient/Views/Nodes/NodeList.swift index 16b036e0..3ab0e932 100644 --- a/MeshtasticClient/Views/Nodes/NodeList.swift +++ b/MeshtasticClient/Views/Nodes/NodeList.swift @@ -11,12 +11,12 @@ import SwiftUI struct NodeList: View { - @EnvironmentObject var modelData: ModelData + @EnvironmentObject var meshData: MeshData @State private var showLocationOnly = false var filteredDevices: [NodeInfoModel] { - modelData.nodes.filter { node in + meshData.nodes.filter { node in (!showLocationOnly || node.position.coordinate != nil) } } diff --git a/MeshtasticClient/Views/Nodes/NodeMap.swift b/MeshtasticClient/Views/Nodes/NodeMap.swift index e91b1dd8..d6c78ca9 100644 --- a/MeshtasticClient/Views/Nodes/NodeMap.swift +++ b/MeshtasticClient/Views/Nodes/NodeMap.swift @@ -12,10 +12,10 @@ import CoreLocation struct NodeMap: View { - @EnvironmentObject var modelData: ModelData + @EnvironmentObject var meshData: MeshData var locationNodes: [NodeInfoModel] { - modelData.nodes.filter { node in + meshData.nodes.filter { node in (node.position.coordinate != nil) } } diff --git a/MeshtasticClientTests/MeshtasticClientTests.swift b/MeshtasticClientTests/MeshtasticClientTests.swift index 3042dba2..e08fe973 100644 --- a/MeshtasticClientTests/MeshtasticClientTests.swift +++ b/MeshtasticClientTests/MeshtasticClientTests.swift @@ -6,7 +6,7 @@ // import XCTest -@testable import MeshtasticClient +//@testable import MeshtasticClient class MeshtasticClientTests: XCTestCase {