iOS 15 updates, icons fixes, new data models

This commit is contained in:
Garth Vander Houwen 2021-09-20 22:29:10 -07:00
parent 71b9aa5f2e
commit 79851345fa
17 changed files with 373 additions and 50 deletions

View file

@ -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 = "<group>"; };
DD47E3DC26F390A000029299 /* MessageDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDetail.swift; sourceTree = "<group>"; };
DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoModel.swift; sourceTree = "<group>"; };
DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = "<group>"; };
DD7AA3F226F05C120077AF76 /* NodeInfoModel3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel3.swift; sourceTree = "<group>"; };
DD7AA3F426F05D660077AF76 /* nodeinfomodel.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = nodeinfomodel.json; sourceTree = "<group>"; };
DD836AE626F6B38600ABCC23 /* Connect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connect.swift; sourceTree = "<group>"; };
DD836AEA26F7AF9000ABCC23 /* nodeinfomodel2.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = nodeinfomodel2.json; sourceTree = "<group>"; };
DD836AEC26F858F900ABCC23 /* MeshData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshData.swift; sourceTree = "<group>"; };
DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = "<group>"; };
DD836AF026F8613500ABCC23 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
DD90860A26F645B700DC5189 /* MeshtasticClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticClient.entitlements; sourceTree = "<group>"; };
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = "<group>"; };
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
};
DD8EDE9226F97A2B00A5A10B /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
DDAF8C5626ED07740058C060 /* Protobufs */ = {
isa = PBXGroup;
children = (
@ -177,6 +190,7 @@
DDC2E16D26CE248F0042C5E4 /* MeshtasticClientTests */,
DDC2E17826CE248F0042C5E4 /* MeshtasticClientUITests */,
DDC2E15526CE248E0042C5E4 /* Products */,
DD8EDE9226F97A2B00A5A10B /* Frameworks */,
);
sourceTree = "<group>";
};
@ -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 = "<group>";
@ -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;

View file

@ -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")

View file

@ -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

View file

@ -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()
}
}
}
}

View file

@ -0,0 +1,82 @@
/*
See LICENSE folder for this samples 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
}
}

View file

@ -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")
}
}
}
}

View file

@ -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<T: Decodable>(_ filename: String) -> T {

View file

@ -1,18 +1,12 @@
//
// NodeInfo.swift
// MeshtasticClient
//
// Created by Garth Vander Houwen on 9/13/21.
//
import Foundation
/*
See LICENSE folder for this samples 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
}
}

View file

@ -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
}
}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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)
}
}
}

View file

@ -16,7 +16,8 @@ struct MessageList: View {
GeometryReader { bounds in
ScrollView {
Text(String(bleManager.isSwitchedOn))
Text(String(bleManager.connectedPeripheral != nil))
}.padding(.all)
}

View file

@ -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()

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

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