Rebuild nodes page with data from device, improve layout consistency

This commit is contained in:
Garth Vander Houwen 2021-09-14 21:38:12 -07:00
parent a27e8b29fa
commit ccf5bf1610
18 changed files with 647 additions and 227 deletions

View file

@ -7,7 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
DD8E565A26EE85E3002CD952 /* MeshtasticClient.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DD8E565826EE85E3002CD952 /* MeshtasticClient.xcdatamodeld */; };
DD47E3CC26F0E51D00029299 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CB26F0E51D00029299 /* NodeDetail.swift */; };
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; };
DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CF26F1073F00029299 /* NodeRow.swift */; };
DD47E3D226F1210600029299 /* HelperFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D126F1210600029299 /* HelperFunctions.swift */; };
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D526F17ED900029299 /* CircleText.swift */; };
DD7AA3F326F05C120077AF76 /* NodeInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7AA3F226F05C120077AF76 /* NodeInfoModel.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 */; };
@ -59,7 +64,13 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
DD8E565926EE85E3002CD952 /* MeshtasticClient.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticClient.xcdatamodel; sourceTree = "<group>"; };
DD47E3CB26F0E51D00029299 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = "<group>"; };
DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = "<group>"; };
DD47E3CF26F1073F00029299 /* NodeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeRow.swift; sourceTree = "<group>"; };
DD47E3D126F1210600029299 /* HelperFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperFunctions.swift; sourceTree = "<group>"; };
DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = "<group>"; };
DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = "<group>"; };
DD7AA3F426F05D660077AF76 /* nodeInfoModel.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = nodeInfoModel.json; sourceTree = "<group>"; };
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = "<group>"; };
DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mesh.pb.swift; sourceTree = "<group>"; };
DDAF8C5C26ED09490058C060 /* portnums.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = portnums.pb.swift; sourceTree = "<group>"; };
@ -125,6 +136,16 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DD47E3CA26F0E50300029299 /* Nodes */ = {
isa = PBXGroup;
children = (
DD47E3CB26F0E51D00029299 /* NodeDetail.swift */,
DD47E3CD26F103C600029299 /* NodeList.swift */,
DD47E3CF26F1073F00029299 /* NodeRow.swift */,
);
path = Nodes;
sourceTree = "<group>";
};
DDAF8C5626ED07740058C060 /* Protobufs */ = {
isa = PBXGroup;
children = (
@ -206,6 +227,7 @@
DDC2E18726CE24E40042C5E4 /* Views */ = {
isa = PBXGroup;
children = (
DD47E3CA26F0E50300029299 /* Nodes */,
DDC2E18C26CE25B00042C5E4 /* Devices */,
DDC2E18B26CE25A70042C5E4 /* Messages */,
DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */,
@ -217,10 +239,10 @@
DDC2E18826CE24EE0042C5E4 /* Model */ = {
isa = PBXGroup;
children = (
DD8E565826EE85E3002CD952 /* MeshtasticClient.xcdatamodeld */,
DDC2E19C26CE27580042C5E4 /* ModelData.swift */,
DDC2E19E26CE27630042C5E4 /* Device.swift */,
DDAF8C6F26ED1DD20058C060 /* Device2.swift */,
DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */,
);
path = Model;
sourceTree = "<group>";
@ -231,6 +253,7 @@
DDC2E18A26CE25690042C5E4 /* deviceData.json */,
DDC2E1AA26DD89EC0042C5E4 /* packets.json */,
DDAF8C7126ED2AD80058C060 /* nodeInfo.json */,
DD7AA3F426F05D660077AF76 /* nodeInfoModel.json */,
);
path = Resources;
sourceTree = "<group>";
@ -259,6 +282,7 @@
isa = PBXGroup;
children = (
DDC2E19A26CE27150042C5E4 /* CircleImage.swift */,
DD47E3D526F17ED900029299 /* CircleText.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -269,6 +293,7 @@
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */,
DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */,
DDAF8C6D26ED19040058C060 /* Extensions.swift */,
DD47E3D126F1210600029299 /* HelperFunctions.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -411,9 +436,12 @@
buildActionMask = 2147483647;
files = (
DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */,
DD47E3CC26F0E51D00029299 /* NodeDetail.swift in Sources */,
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */,
DDAF8C7026ED1DD20058C060 /* Device2.swift in Sources */,
DD7AA3F326F05C120077AF76 /* NodeInfoModel.swift in Sources */,
DDC2E19F26CE27630042C5E4 /* Device.swift in Sources */,
DD47E3D226F1210600029299 /* HelperFunctions.swift in Sources */,
DDC2E19926CE26940042C5E4 /* DeviceMap.swift in Sources */,
DDC2E19526CE26760042C5E4 /* DeviceDetail.swift in Sources */,
DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */,
@ -425,9 +453,11 @@
DDAF8C6B26ED0DD80058C060 /* environmental_measurement.pb.swift in Sources */,
DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */,
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */,
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */,
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */,
DD8E565A26EE85E3002CD952 /* MeshtasticClient.xcdatamodeld in Sources */,
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */,
DDC2E19326CE266B0042C5E4 /* DeviceHome.swift in Sources */,
DDC2E19B26CE27150042C5E4 /* CircleImage.swift in Sources */,
@ -773,19 +803,6 @@
productName = SwiftProtobuf;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */
DD8E565826EE85E3002CD952 /* MeshtasticClient.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
DD8E565926EE85E3002CD952 /* MeshtasticClient.xcdatamodel */,
);
currentVersion = DD8E565926EE85E3002CD952 /* MeshtasticClient.xcdatamodel */;
path = MeshtasticClient.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = DDC2E14C26CE248E0042C5E4 /* Project object */;
}

View file

@ -1,6 +1,7 @@
import Foundation
import CoreData
import CoreBluetooth
import SwiftUI
struct Peripheral: Identifiable {
let id: String
@ -20,7 +21,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
@Published var peripheralArray = [CBPeripheral]()
private var rssiArray = [NSNumber]()
private var timer = Timer()
@Published var isSwitchedOn = false
@Published var peripherals = [Peripheral]()
@ -230,6 +230,17 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
print("Save a myInfo")
do {
print(try decodedInfo.myInfo.jsonString())
// let node = MyInfoEntity(context: persistentContainer.viewContext)
// node.myNodeNum = Int64(decodedInfo.myInfo.myNodeNum)
// node.num = Int(decodedInfo.myInfo.myNodeNum)
// node.user?.hwModel = decodedInfo.myInfo.hwModelDeprecated
//node.lastHeard
// node.snr
// node.user?.shortName decodedInfo.myInfo.
// try persistentContainer.viewContext.save()
} catch {
fatalError("Failed to decode json")
}
@ -239,7 +250,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
{
print("Save a nodeInfo")
do {
let node =
print(try decodedInfo.nodeInfo.jsonString())
} catch {
fatalError("Failed to decode json")

View file

@ -1,4 +1,5 @@
import Foundation
import SwiftUI
extension Data
{
@ -8,6 +9,13 @@ extension Data
}
}
extension Date
{
static var currentTimeStamp: Int64
{
return Int64(Date().timeIntervalSince1970 * 1000)
}
}
extension String
{
@ -36,11 +44,40 @@ extension String
}
typealias NodeColor = Int
extension Date
{
static var currentTimeStamp: Int64
{
return Int64(Date().timeIntervalSince1970 * 1000)
extension NodeColor {
// a color to represent an individual meshtastic device
var color: Color {
switch self {
case 0:
return Color(.red)
case 1:
return Color(.blue)
case 2:
return Color(.yellow)
case 3:
return Color(.green)
case 4:
return Color(.purple)
case 5:
return Color(.systemPink)
case 6:
return Color(.systemTeal)
case 7:
return Color(.black)
case 8:
return Color(.gray)
case 9:
return Color(.brown)
case 10:
return Color(.magenta)
case 11:
return Color(.orange)
case 12:
return Color(.cyan)
default:
return Color(.blue)
}
}
}

View file

@ -0,0 +1,11 @@
//
// HelperFunctions.swift
// MeshtasticClient
//
// Created by Garth Vander Houwen on 9/14/21.
//
import Foundation
import SwiftUI

View file

@ -9,6 +9,8 @@ import Combine
final class ModelData: ObservableObject {
@Published var devices: [Device] = load("deviceData.json")
@Published var nodes: [NodeInfoModel] = load("packets.json")
var nearby: [Device] {
devices
}

View file

@ -0,0 +1,37 @@
//
// NodeInfo.swift
// MeshtasticClient
//
// Created by Garth Vander Houwen on 9/13/21.
//
import Foundation
import SwiftUI
import CoreLocation
struct NodeInfoModel: Hashable, Codable, Identifiable {
let id = UUID()
var num: UInt32
var user: User
struct User: Hashable, Codable, Identifiable {
var id: String
var longName: String
var shortName: String
var macaddr: String
var hwModel: String
}
var position: Position
struct Position: Hashable, Codable {
var latitudeI: Int32?
var longitudeI: Int32?
var altitude: Int32?
var batteryLevel: Int32?
var time: Int32?
}
var lastHeard: Double
var snr: Double?
}

View file

@ -1,38 +1,43 @@
[{
"nodeInfo": {
"num":3186677820,
[
{
"num":2792101487,
"user":{
"id":"!bdf0d83c",
"longName":"Garth Vander Houwen",
"shortName":"GVH",
"macaddr":"fJ698Ng8",
"hwModel":"TBEAM"
"id":"!a66c166f",
"longName":"RAK Solar 2",
"shortName":"RS2",
"macaddr":"8eambBZv",
"hwModel":"RAK4631"
},
"position":{
"latitudeI":476040257,
"longitudeI":-1221545412,
"altitude":11,
"batteryLevel":100,
"time":1631384279
},
"lastHeard":1631384279
"batteryLevel":68},
"lastHeard":1631593661
}
},
{
"nodeInfo":{
"num":2792101487,
"user":{
"id":"!a66c166f",
"longName":"RAK Solar 2",
"shortName":"RS2",
"macaddr":"8eambBZv",
"hwModel":"RAK4631"
},
"position":{
"batteryLevel":88,
"time":1631297585
},
"lastHeard":1631383493,
"snr":12.0
}
}
}]
"num":1000569662,
"user":{
"id":"!3ba37b3e",
"longName":"RAK Solar 1",
"shortName":"RS1",
"macaddr":"1Kc7o3s+",
"hwModel":"RAK4631"
},
"position":{
"latitudeI":476021390,
"longitudeI":-1221532609,
"altitude":71,
"batteryLevel":70,
"time":1629314497
},
"lastHeard":1629392801,
"snr":5.25
},
{"num":1133445808,"user":{"id":"!438f02b0","longName":"RAK DEV NODE","shortName":"RDN","macaddr":"92VDjwKw","hwModel":"RAK4631"},"position":{"latitudeI":476022071,"longitudeI":-1221533607,"altitude":91,"batteryLevel":88,"time":1629391435},"lastHeard":1629393577,"snr":4.5},
{"num":84682040,"user":{"id":"!050c2538","longName":"Yellow Beam","shortName":"YB","macaddr":"PGEFDCU4","hwModel":"TBEAM"},"position":{"latitudeI":476020703,"longitudeI":-1221531973,"altitude":79,"batteryLevel":1,"time":1631593233},"lastHeard":1631593034,"snr":5.0},
{"num":2354994191,"user":{"id":"!8c5e5c0f","longName":"RAK Solar 3","shortName":"RS3","macaddr":"+GKMXlwP","hwModel":"RAK4631"},"position":{"batteryLevel":91,"time":1629263310},"lastHeard":1629393330,"snr":4.5},
{"num":2718727166,"user":{"id":"!a20c7bfe","longName":"RAK Large Node","shortName":"RLN","macaddr":"zXSiDHv+","hwModel":"RAK4631"},"position":{"batteryLevel":32,"time":1629061939},"lastHeard":1629102457,"snr":-17.5},
{"num":84681200,"user":{"id":"!050c21f0","longName":"Unknown 21f0","shortName":"?F0","macaddr":"PGEFDCHw","hwModel":"TBEAM"},"position":{"latitudeI":476022115,"longitudeI":-1221531952,"altitude":85,"batteryLevel":100,"time":1631593830},"lastHeard":1631593584,"snr":6.75},
{"num":2930161432,"user":{"id":"!aea6b718","longName":"Unknown b718","shortName":"?18","macaddr":"TBGuprcY","hwModel":"TBEAM"},"position":{"latitudeI":476020757,"longitudeI":-1221533124,"altitude":56,"batteryLevel":100,"time":1631593558},"lastHeard":1631593554,"snr":6.75},
{"num":4064637200,"user":{"id":"!f2457110","longName":"Unknown 7110","shortName":"?10","macaddr":"CDryRXEQ","hwModel":"TBEAM"},"position":{"latitudeI":476021534,"longitudeI":-1221533621,"altitude":49,"batteryLevel":100,"time":1631593778},"lastHeard":1631593533,"snr":7.0}
]

View file

@ -0,0 +1,43 @@
[
{
"num":2792101487,
"user":{
"id":"!a66c166f",
"longName":"RAK Solar 2",
"shortName":"RS2",
"macaddr":"8eambBZv",
"hwModel":"RAK4631"
},
"position":{
"batteryLevel":68},
"lastHeard":1631593661
}
},
{
"num":1000569662,
"user":{
"id":"!3ba37b3e",
"longName":"RAK Solar 1",
"shortName":"RS1",
"macaddr":"1Kc7o3s+",
"hwModel":"RAK4631"
},
"position":{
"latitudeI":476021390,
"longitudeI":-1221532609,
"altitude":71,
"batteryLevel":70,
"time":1629314497
},
"lastHeard":1629392801,
"snr":5.25
},
{"num":1133445808,"user":{"id":"!438f02b0","longName":"RAK DEV NODE","shortName":"RDN","macaddr":"92VDjwKw","hwModel":"RAK4631"},"position":{"latitudeI":476022071,"longitudeI":-1221533607,"altitude":91,"batteryLevel":88,"time":1629391435},"lastHeard":1629393577,"snr":4.5},
{"num":84682040,"user":{"id":"!050c2538","longName":"Yellow Beam","shortName":"YB","macaddr":"PGEFDCU4","hwModel":"TBEAM"},"position":{"latitudeI":476020703,"longitudeI":-1221531973,"altitude":79,"batteryLevel":1,"time":1631593233},"lastHeard":1631593034,"snr":5.0},
{"num":2354994191,"user":{"id":"!8c5e5c0f","longName":"RAK Solar 3","shortName":"RS3","macaddr":"+GKMXlwP","hwModel":"RAK4631"},"position":{"batteryLevel":91,"time":1629263310},"lastHeard":1629393330,"snr":4.5},
{"num":2718727166,"user":{"id":"!a20c7bfe","longName":"RAK Large Node","shortName":"RLN","macaddr":"zXSiDHv+","hwModel":"RAK4631"},"position":{"batteryLevel":32,"time":1629061939},"lastHeard":1629102457,"snr":-17.5},
{"num":84681200,"user":{"id":"!050c21f0","longName":"Unknown 21f0","shortName":"?F0","macaddr":"PGEFDCHw","hwModel":"TBEAM"},"position":{"latitudeI":476022115,"longitudeI":-1221531952,"altitude":85,"batteryLevel":100,"time":1631593830},"lastHeard":1631593584,"snr":6.75},
{"num":2930161432,"user":{"id":"!aea6b718","longName":"Unknown b718","shortName":"?18","macaddr":"TBGuprcY","hwModel":"TBEAM"},"position":{"latitudeI":476020757,"longitudeI":-1221533124,"altitude":56,"batteryLevel":100,"time":1631593558},"lastHeard":1631593554,"snr":6.75},
{"num":4064637200,"user":{"id":"!f2457110","longName":"Unknown 7110","shortName":"?10","macaddr":"CDryRXEQ","hwModel":"TBEAM"},"position":{"latitudeI":476021534,"longitudeI":-1221533621,"altitude":49,"batteryLevel":100,"time":1631593778},"lastHeard":1631593533,"snr":7.0}
]

View file

@ -1,149 +1,152 @@
[
{
"from":1133445808,
"to":1133445808,
"decoded":{
"portnum":"ADMIN_APP",
"payload":"b"":\\x04\\x08\\x01\\x12\\x00",
"requestId":1506683854,
"admin":{
"getChannelResponse":{
"index":1,
"settings":{
}
},
"raw":"get_channel_response"{
"index":1 settings{
}
}
}
},
"id":1481765942,
"rxTime":1630360112,
"hopLimit":3,
"priority":"RELIABLE",
"raw":"from":"1133445808 to":1133445808 decoded{
"portnum":"ADMIN_APP payload":":\\004\\010\\001\\022\\000""request_id":1506683854
}"id":"1481765942 rx_time":"1630360112 hop_limit":"3 priority":"RELIABLE",
"fromId":"!438f02b0",
"toId":"!438f02b0"
}{
"num":1133445808,
"user":{
"id":"!438f02b0",
"longName":"RAK DEV NODE",
"shortName":"RDN",
"macaddr":"92VDjwKw",
"hwModel":"RAK4631",
"raw":"id":"!438f02b0""long_name":"RAK DEV NODE""short_name":"RDN""macaddr":"\\367eC\\217\\002\\260""hw_model":RAK4631
},
"position":{
"latitudeI":476021540,
"longitudeI":-1221532059,
"altitude":111,
"time":1630360110,
"latitude":47.602154,
"longitude":-122.15320589999999
},
"lastHeard":1630360100,
"lastReceived":{
"from":1133445808,
"to":4294967295,
"decoded":{
"portnum":"NODEINFO_APP",
"payload":"b""\n\t!438f02b0\\x12\\x0cRAK DEV NODE\\x1a\\x03RDN\"\\x06\\xf7eC\\x8f\\x02\\xb00\t",
"wantResponse":true,
"user":{
"id":"!438f02b0",
"longName":"RAK DEV NODE",
"shortName":"RDN",
"macaddr":"92VDjwKw",
"hwModel":"RAK4631",
"raw":"id":"!438f02b0""long_name":"RAK DEV NODE""short_name":"RDN""macaddr":"\\367eC\\217\\002\\260""hw_model":RAK4631
}
},
"id":1481765939,
"rxTime":1630360100,
"hopLimit":3,
"priority":"BACKGROUND",
"raw":"from":"1133445808 to":4294967295 decoded{
"portnum":"NODEINFO_APP payload":"\n\t!438f02b0\\022\\014RAK DEV NODE\\032\\003RDN\"\\006\\367eC\\217\\002\\2600\t""want_response":true
}"id":"1481765939 rx_time":"1630360100 hop_limit":"3 priority":"BACKGROUND",
"fromId":"!438f02b0",
"toId":"^all"
},
"snr":"None",
"hopLimit":3
}{
"num":1000569662,
"user":{
"id":"!3ba37b3e",
"longName":"RAK Solar 1",
"shortName":"RS1",
"macaddr":"1Kc7o3s+",
"hwModel":"RAK4631"
},
"position":{
"latitudeI":476022865,
"longitudeI":-1221531705,
"altitude":81,
"batteryLevel":4,
"time":1628091161,
"latitude":47.6022865,
"longitude":-122.15317049999999
},
"lastHeard":1630234276,
"snr":5.25
}{
"num":2792101487,
"user":{
"id":"!a66c166f",
"longName":"RAK Solar 2",
"shortName":"RS2",
"macaddr":"8eambBZv",
"hwModel":"RAK4631"
},
"position":{
"batteryLevel":56,
"time":1628096176
},
"lastHeard":1628231149,
"snr":4.75
}{
"num":84682040,
"user":{
"id":"!050c2538",
"longName":"Yellow Beam",
"shortName":"YB",
"macaddr":"PGEFDCU4",
"hwModel":"TBEAM"
},
"position":{
"latitudeI":476020441,
"longitudeI":-1221533598,
"altitude":65,
"batteryLevel":100,
"time":1628405738,
"latitude":47.6020441,
"longitude":-122.15335979999999
},
"lastHeard":1628405744,
"snr":4.5
}{
"num":2354994191,
"user":{
"id":"!8c5e5c0f",
"longName":"RAK Solar 3",
"shortName":"RS3",
"macaddr":"+GKMXlwP",
"hwModel":"RAK4631"
},
"position":{
"batteryLevel":62
},
"lastHeard":1630252441,
"snr":4.75
}
]
[{
"num": 2792101487,
"user": {
"id": "!a66c166f",
"longName": "RAK Solar 2",
"shortName": "RS2",
"macaddr": "8eambBZv",
"hwModel": "RAK4631"
},
"position": {
"batteryLevel": 68
},
"lastHeard": 1631593661
}, {
"num": 1000569662,
"user": {
"id": "!3ba37b3e",
"longName": "RAK Solar 1",
"shortName": "RS1",
"macaddr": "1Kc7o3s+",
"hwModel": "RAK4631"
},
"position": {
"latitudeI": 476021390,
"longitudeI": -1221532609,
"altitude": 71,
"batteryLevel": 70,
"time": 1629314497
},
"lastHeard": 1629392801,
"snr": 5.25
}, {
"num": 1133445808,
"user": {
"id": "!438f02b0",
"longName": "RAK DEV NODE",
"shortName": "RDN",
"macaddr": "92VDjwKw",
"hwModel": "RAK4631"
},
"position": {
"latitudeI": 476022071,
"longitudeI": -1221533607,
"altitude": 91,
"batteryLevel": 88,
"time": 1629391435
},
"lastHeard": 1629393577,
"snr": 4.5
}, {
"num": 84682040,
"user": {
"id": "!050c2538",
"longName": "Yellow Beam",
"shortName": "YB",
"macaddr": "PGEFDCU4",
"hwModel": "TBEAM"
},
"position": {
"latitudeI": 476020703,
"longitudeI": -1221531973,
"altitude": 79,
"batteryLevel": 1,
"time": 1631593233
},
"lastHeard": 1631593034,
"snr": 5.0
}, {
"num": 2354994191,
"user": {
"id": "!8c5e5c0f",
"longName": "RAK Solar 3",
"shortName": "RS3",
"macaddr": "+GKMXlwP",
"hwModel": "RAK4631"
},
"position": {
"batteryLevel": 91,
"time": 1629263310
},
"lastHeard": 1629393330,
"snr": 4.5
}, {
"num": 2718727166,
"user": {
"id": "!a20c7bfe",
"longName": "RAK Large Node",
"shortName": "RLN",
"macaddr": "zXSiDHv+",
"hwModel": "RAK4631"
},
"position": {
"batteryLevel": 32,
"time": 1629061939
},
"lastHeard": 1629102457,
"snr": -17.5
}, {
"num": 84681200,
"user": {
"id": "!050c21f0",
"longName": "Unknown 21f0",
"shortName": "?F0",
"macaddr": "PGEFDCHw",
"hwModel": "TBEAM"
},
"position": {
"latitudeI": 476022115,
"longitudeI": -1221531952,
"altitude": 85,
"batteryLevel": 100,
"time": 1631593830
},
"lastHeard": 1631593584,
"snr": 6.75
}, {
"num": 2930161432,
"user": {
"id": "!aea6b718",
"longName": "Unknown b718",
"shortName": "?18",
"macaddr": "TBGuprcY",
"hwModel": "TBEAM"
},
"position": {
"latitudeI": 476020757,
"longitudeI": -1221533124,
"altitude": 56,
"batteryLevel": 100,
"time": 1631593558
},
"lastHeard": 1631593554,
"snr": 6.75
}, {
"num": 4064637200,
"user": {
"id": "!f2457110",
"longName": "Unknown 7110",
"shortName": "?10",
"macaddr": "CDryRXEQ",
"hwModel": "TBEAM"
},
"position": {
"latitudeI": 476021534,
"longitudeI": -1221533621,
"altitude": 49,
"batteryLevel": 100,
"time": 1631593778
},
"lastHeard": 1631593533,
"snr": 7.0
}]

View file

@ -14,6 +14,7 @@ struct ContentView: View {
case featured
case list
case ble
case nodes
}
var body: some View {
@ -33,6 +34,11 @@ struct ContentView: View {
Label("Devices", systemImage: "flipphone")
}
.tag(Tab.devices)
NodeList()
.tabItem {
Label("Nodes", systemImage: "flipphone")
}
.tag(Tab.nodes)
DeviceBLE()
.tabItem {
Label("Bluetooth", systemImage: "dot.radiowaves.left.and.right")

View file

@ -17,7 +17,6 @@ struct DeviceDetail: View {
var deviceIndex: Int {
modelData.devices.firstIndex(where: { $0.id == device.id })!
}
struct MapLocation: Identifiable {
let id = UUID()
let name: String

View file

@ -40,21 +40,25 @@ struct DeviceMap: View {
MapLocation(name: devices[3].shortName, coordinate: CLLocationCoordinate2D(latitude: devices[3].position.latitude, longitude: devices[3].position.longitude))
]
ZStack {
Map(coordinateRegion: regionBinding,
interactionModes: [.all],
showsUserLocation: true,
userTrackingMode: .constant(.follow), annotationItems: annotations) { location in
MapAnnotation(
coordinate: location.coordinate,
content: {
Text(location.name).font(.caption2).foregroundColor(.white)
.background(Circle()
.fill(Color.blue)
.frame(width: 40, height: 40)) }
)
}.frame(maxHeight:.infinity)
}
NavigationView {
ZStack {
Map(coordinateRegion: regionBinding,
interactionModes: [.all],
showsUserLocation: true,
userTrackingMode: .constant(.follow), annotationItems: annotations) { location in
MapAnnotation(
coordinate: location.coordinate,
content: {
Text(location.name).font(.caption2).foregroundColor(.white)
.background(Circle()
.fill(Color.blue)
.frame(width: 40, height: 40)) }
)
}.frame(maxHeight:.infinity)
}
.navigationTitle("Mesh Map")
.navigationBarTitleDisplayMode(.inline)
}.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -12,6 +12,7 @@ struct CircleImage: View {
var body: some View {
image
.resizable()
.clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/)
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 7)

View file

@ -0,0 +1,26 @@
/*
See LICENSE folder for this samples licensing information.
Abstract:
A view that clips an image to a circle and adds a stroke and shadow.
*/
import SwiftUI
struct CircleText: View {
var text: String
var body: some View {
Text(text).font(.subheadline).foregroundColor(.white)
.background(Circle()
.fill(Color.blue)
.frame(width: 40, height: 40, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/))
}
}
struct CircleText_Previews: PreviewProvider {
static var previews: some View {
CircleText(text: "RDN")
}
}

View file

@ -4,7 +4,7 @@ import CoreLocation
struct Messages: View {
var body: some View {
ZStack {
NavigationView {
List {
HStack {
@ -50,8 +50,8 @@ struct Messages: View {
Spacer()
}
}
}
.navigationTitle("Broadcast Channel")
}
}.navigationTitle("Broadcast Channel")
.navigationBarTitleDisplayMode(.inline)
}.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -0,0 +1,129 @@
/*
See LICENSE folder for this samples licensing information.
Abstract:
A view showing the details for a device.
*/
import SwiftUI
import MapKit
import CoreLocation
import CoreBluetooth
struct NodeDetail: View {
@EnvironmentObject var modelData: ModelData
var node: NodeInfoModel
var nodeIndex: Int {
modelData.nodes.firstIndex(where: { $0.id == node.id })!
}
struct MapLocation: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}
var body: some View {
let location = LocationHelper.currentLocation
let currentCoordinatePosition = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
let regionBinding = Binding<MKCoordinateRegion>(
get: {
MKCoordinateRegion(center: currentCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
},
set: { _ in }
)
GeometryReader { bounds in
VStack {
// Map or Device Image
if(node.position.latitudeI != nil && node.position.latitudeI! > 0) {
Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .constant(.follow))
.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2)
}
else
{
Image(node.user.hwModel.lowercased())
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: bounds.size.width, height: bounds.size.height / 2)
}
ScrollView {
HStack {
Spacer()
Image(systemName: "flipphone").font(.largeTitle).foregroundColor(.blue)
Text("Model: " + String(node.user.hwModel)).font(.title)
Spacer()
}.padding()
Divider()
HStack {
Image(systemName: "antenna.radiowaves.left.and.right").font(.title2).foregroundColor(.blue)
VStack(alignment: .center) {
Text("SNR").font(.title3)
Text(String(node.snr!)).font(.title3).foregroundColor(.gray)
}
Divider()
VStack(alignment: .center) {
Text("AKA").font(.title3)
Text(node.user.shortName).font(.title3).foregroundColor(.gray)
}
Divider()
VStack(alignment: .leading) {
Text("Battery").font(.title3)
Text(String(node.position.batteryLevel!) + "%").font(.title3).foregroundColor(.gray)
}
}.padding(4)
Divider()
HStack{
Image(systemName: "clock").font(.title2).foregroundColor(.blue)
let lastHeard = Date(timeIntervalSince1970: node.lastHeard)
Text("Last Heard:").font(.title3)
Text(lastHeard, style: .relative).font(.title3)
Text("ago").font(.title3)
}.padding()
Divider()
HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) {
Image(systemName: "mappin").font(.title).foregroundColor(.blue)
VStack(alignment: .leading) {
Text("Latitude").font(.headline)
Text(String(node.position.latitudeI!)).font(.caption).foregroundColor(.gray)
}
VStack(alignment: .leading) {
Text("Longitude").font(.headline)
Text(String(node.position.longitudeI!)).font(.caption).foregroundColor(.gray)
}
VStack(alignment: .leading) {
Text("Altitude").font(.headline)
Text(String(node.position.altitude!) + " m").font(.caption).foregroundColor(.gray)
}
}.padding()
Divider()
HStack {
Spacer()
Image(systemName: "person").font(.title3).foregroundColor(.blue)
Text("Unique Id: " + String(node.user.id)).font(.headline)
Divider()
Image(systemName: "number").font(.title3).foregroundColor(.blue)
Text("Node Num: " + String(node.num)).font(.headline)
Spacer()
}.padding()
}
}.navigationTitle(node.user.longName)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
HStack {
CircleText(text: node.user.shortName).offset(y: -2)
}
)
}.ignoresSafeArea(.all, edges: [.leading, .trailing])
}
}

View file

@ -0,0 +1,41 @@
//
// DeviceHome.swift
// MeshtasticClient
//
// Created by Garth Vander Houwen on 8/7/21.
//
// Abstract:
// A view showing a list of devices that have been seen on the mesh network
import SwiftUI
struct NodeList: View {
@EnvironmentObject var modelData: ModelData
var body: some View {
NavigationView {
List {
ForEach(modelData.nodes) { node in
NavigationLink(destination: NodeDetail(node: node)) {
NodeRow(node: node)
}
}
}
.navigationTitle("All Nodes")
}.navigationViewStyle(StackNavigationViewStyle()) // Force Full screen master details
}
}
struct NodeList_Previews: PreviewProvider {
static var previews: some View {
NodeList()
.environmentObject(ModelData())
}
}

View file

@ -0,0 +1,49 @@
//
// DeviceMap.swift
// MeshtasticClient
//
// Created by Garth Vander Houwen on 8/7/21.
//
// Abstract:
// A single row to be displayed in a list of landmarks.
import SwiftUI
struct NodeRow: View {
var node: NodeInfoModel
var body: some View {
HStack {
Image(node.user.hwModel.lowercased()).resizable().frame(width: 150, height: 150)
VStack(alignment: .leading) {
Text(node.user.longName).font(.title2)
HStack {
if node.user.hwModel == "TBEAM" || node.user.hwModel == "TLORA" {
Image(systemName: "wifi")
.foregroundColor(.blue).font(.title3)
}
if false {
Image(systemName: "rectangle.connected.to.line.below")
.foregroundColor(.green).font(.title2)
}
}
}
Spacer()
}
}
}
struct NodeRow_Previews: PreviewProvider {
static var nodes = ModelData().nodes
static var previews: some View {
Group {
NodeRow(node: nodes[0])
NodeRow(node: nodes[1])
NodeRow(node: nodes[2])
}
.previewLayout(.fixed(width: 300, height: 70))
}
}