mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
iOS 15 updates, icons fixes, new data models
This commit is contained in:
parent
71b9aa5f2e
commit
79851345fa
17 changed files with 373 additions and 50 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
82
MeshtasticClient/Model/Color.swift
Normal file
82
MeshtasticClient/Model/Color.swift
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
49
MeshtasticClient/Model/MeshData.swift
Normal file
49
MeshtasticClient/Model/MeshData.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
77
MeshtasticClient/Model/NodeInfoModel3.swift
Normal file
77
MeshtasticClient/Model/NodeInfoModel3.swift
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ struct MessageList: View {
|
|||
GeometryReader { bounds in
|
||||
|
||||
ScrollView {
|
||||
|
||||
Text(String(bleManager.isSwitchedOn))
|
||||
Text(String(bleManager.connectedPeripheral != nil))
|
||||
}.padding(.all)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
|
||||
import XCTest
|
||||
@testable import MeshtasticClient
|
||||
//@testable import MeshtasticClient
|
||||
|
||||
class MeshtasticClientTests: XCTestCase {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue