mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #843 from meshtastic/true-bearing
Node List and Details Cleanup
This commit is contained in:
commit
6f2e162e51
29 changed files with 780 additions and 170 deletions
|
|
@ -723,9 +723,6 @@
|
|||
},
|
||||
"Altitude is Mean Sea Level" : {
|
||||
|
||||
},
|
||||
"Altitude: %@" : {
|
||||
|
||||
},
|
||||
"Always point north" : {
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@
|
|||
DD1933762B0835D500771CD5 /* PositionAltitudeChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1933752B0835D500771CD5 /* PositionAltitudeChart.swift */; };
|
||||
DD1933782B084F4200771CD5 /* Measurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1933772B084F4200771CD5 /* Measurement.swift */; };
|
||||
DD1B8F402B35E2F10022AABC /* GPSStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */; };
|
||||
DD1BD0EB2C601795008C0C70 /* CLLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BD0EA2C601795008C0C70 /* CLLocation.swift */; };
|
||||
DD1BD0EE2C603C91008C0C70 /* CustomFormatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BD0ED2C603C91008C0C70 /* CustomFormatters.swift */; };
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; };
|
||||
DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2160AE28C5552500C17253 /* MQTTConfig.swift */; };
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; };
|
||||
|
|
@ -287,6 +289,9 @@
|
|||
DD1933752B0835D500771CD5 /* PositionAltitudeChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAltitudeChart.swift; sourceTree = "<group>"; };
|
||||
DD1933772B084F4200771CD5 /* Measurement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Measurement.swift; sourceTree = "<group>"; };
|
||||
DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPSStatus.swift; sourceTree = "<group>"; };
|
||||
DD1BD0EA2C601795008C0C70 /* CLLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLLocation.swift; sourceTree = "<group>"; };
|
||||
DD1BD0ED2C603C91008C0C70 /* CustomFormatters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFormatters.swift; sourceTree = "<group>"; };
|
||||
DD1BD0F12C61D3AD008C0C70 /* MeshtasticDataModelV 42.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 42.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
|
||||
DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = "<group>"; };
|
||||
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -591,6 +596,14 @@
|
|||
path = CoreData;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD1BD0EC2C603C5B008C0C70 /* Measurement */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD1BD0ED2C603C91008C0C70 /* CustomFormatters.swift */,
|
||||
);
|
||||
path = Measurement;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD47E3CA26F0E50300029299 /* Nodes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -801,6 +814,7 @@
|
|||
DDC2E15626CE248E0042C5E4 /* Meshtastic */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD1BD0EC2C603C5B008C0C70 /* Measurement */,
|
||||
25F5D5BC2C3F6D7B008036E3 /* Router */,
|
||||
DD7709392AA1ABA1007A8BF0 /* Tips */,
|
||||
DD90860A26F645B700DC5189 /* Meshtastic.entitlements */,
|
||||
|
|
@ -962,6 +976,7 @@
|
|||
DD007BB12AA59B9A00F5FA12 /* CoreData */,
|
||||
DDFFA7462B3A7F3C004730DB /* Bundle.swift */,
|
||||
DDDB444529F8A96500EE2349 /* Character.swift */,
|
||||
DD1BD0EA2C601795008C0C70 /* CLLocation.swift */,
|
||||
DDDB444929F8AA3A00EE2349 /* CLLocationCoordinate2D.swift */,
|
||||
DDDB444B29F8AAA600EE2349 /* Color.swift */,
|
||||
25C49D8F2C471AEA0024FBD1 /* Constants.swift */,
|
||||
|
|
@ -1269,6 +1284,7 @@
|
|||
DDA9515A2BC6624100CEA535 /* TelemetryWeather.swift in Sources */,
|
||||
DDB75A232A13CDA9006ED576 /* BatteryLevelCompact.swift in Sources */,
|
||||
DDB75A162A0594AD006ED576 /* TileOverlay.swift in Sources */,
|
||||
DD1BD0EB2C601795008C0C70 /* CLLocation.swift in Sources */,
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
|
||||
DD3CC6BE28E4CD9800FA9159 /* BatteryGauge.swift in Sources */,
|
||||
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */,
|
||||
|
|
@ -1304,6 +1320,7 @@
|
|||
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
|
||||
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */,
|
||||
DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */,
|
||||
DD1BD0EE2C603C91008C0C70 /* CustomFormatters.swift in Sources */,
|
||||
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */,
|
||||
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */,
|
||||
DD97E96828EFE9A00056DDA4 /* About.swift in Sources */,
|
||||
|
|
@ -1812,6 +1829,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD1BD0F12C61D3AD008C0C70 /* MeshtasticDataModelV 42.xcdatamodel */,
|
||||
DD2984A82C5AEF7500B1268D /* MeshtasticDataModelV 41.xcdatamodel */,
|
||||
DD68BAE72C417A74004C01A0 /* MeshtasticDataModelV 40.xcdatamodel */,
|
||||
DD3D17DC2C3D7B1400561584 /* MeshtasticDataModelV 39.xcdatamodel */,
|
||||
|
|
@ -1854,7 +1872,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD2984A82C5AEF7500B1268D /* MeshtasticDataModelV 41.xcdatamodel */;
|
||||
currentVersion = DD1BD0F12C61D3AD008C0C70 /* MeshtasticDataModelV 42.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
28
Meshtastic/Extensions/CLLocation.swift
Normal file
28
Meshtastic/Extensions/CLLocation.swift
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// CLLocation.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/4/24.
|
||||
//
|
||||
import Foundation
|
||||
import MapKit
|
||||
|
||||
func degreesToRadians(degrees: Double) -> Double { return degrees * .pi / 180.0 }
|
||||
func radiansToDegrees(radians: Double) -> Double { return radians * 180.0 / .pi }
|
||||
|
||||
func getBearingBetweenTwoPoints(point1: CLLocation, point2: CLLocation) -> Double {
|
||||
|
||||
let lat1 = degreesToRadians(degrees: point1.coordinate.latitude)
|
||||
let lon1 = degreesToRadians(degrees: point1.coordinate.longitude)
|
||||
|
||||
let lat2 = degreesToRadians(degrees: point2.coordinate.latitude)
|
||||
let lon2 = degreesToRadians(degrees: point2.coordinate.longitude)
|
||||
|
||||
let dLon = lon2 - lon1
|
||||
|
||||
let y = sin(dLon) * cos(lat2)
|
||||
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
|
||||
let radiansBearing = atan2(y, x)
|
||||
|
||||
return radiansToDegrees(radians: radiansBearing)
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ public func createNodeInfo(num: Int64, context: NSManagedObjectContext) -> NodeI
|
|||
newNode.num = Int64(num)
|
||||
let newUser = UserEntity(context: context)
|
||||
newUser.num = Int64(num)
|
||||
let userId = String(format: "%2X", num)
|
||||
let userId = num.toHex()
|
||||
newUser.userId = "!\(userId)"
|
||||
let last4 = String(userId.suffix(4))
|
||||
newUser.longName = "Meshtastic \(last4)"
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ extension Int {
|
|||
|
||||
extension UInt32 {
|
||||
func toHex() -> String {
|
||||
return String(format: "!%2X", self)
|
||||
return String(format: "!%2X", self).lowercased()
|
||||
}
|
||||
}
|
||||
|
||||
extension Int64 {
|
||||
func toHex() -> String {
|
||||
return String(format: "!%2X", self)
|
||||
return String(format: "!%2X", self).lowercased()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ extension UserDefaults {
|
|||
|
||||
@UserDefault(.firmwareVersion, defaultValue: "0.0.0")
|
||||
static var firmwareVersion: String
|
||||
|
||||
|
||||
@UserDefault(.environmentEnableWeatherKit, defaultValue: true)
|
||||
static var environmentEnableWeatherKit: Bool
|
||||
|
||||
|
|
|
|||
|
|
@ -902,7 +902,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(connectedPeripheral.num))
|
||||
do {
|
||||
let fetchedNodeInfo = try context.fetch(fetchNodeInfoRequest) ?? []
|
||||
let fetchedNodeInfo = try context.fetch(fetchNodeInfoRequest)
|
||||
if fetchedNodeInfo.count == 1 {
|
||||
// Subscribe to Mqtt Client Proxy if enabled
|
||||
if fetchedNodeInfo[0].mqttConfig != nil && fetchedNodeInfo[0].mqttConfig?.enabled ?? false && fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false {
|
||||
|
|
@ -1132,7 +1132,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return success
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@MainActor
|
||||
public func getPositionFromPhoneGPS(destNum: Int64) -> Position? {
|
||||
var positionPacket = Position()
|
||||
if #available(iOS 17.0, macOS 14.0, *) {
|
||||
|
|
@ -1265,7 +1265,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
let logString = String.localizedStringWithFormat("mesh.log.sharelocation %@".localized, String(fromNodeNum))
|
||||
Logger.services.debug("📍 \(logString)")
|
||||
return true
|
||||
|
|
@ -1523,7 +1522,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedPeripheral.num))
|
||||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) ?? []
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest)
|
||||
if fetchedMyInfo.count == 1 {
|
||||
i = Int32(fetchedMyInfo[0].channels?.count ?? -1)
|
||||
myInfo = fetchedMyInfo[0]
|
||||
|
|
|
|||
|
|
@ -287,6 +287,13 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newUser.longName = nodeInfo.user.longName
|
||||
newUser.shortName = nodeInfo.user.shortName
|
||||
newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
newUser.hwModelId = Int32(nodeInfo.user.hwModel.rawValue)
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == newUser.hwModelId })
|
||||
newUser.hwDisplayName = dh?.displayName
|
||||
}
|
||||
}
|
||||
newUser.isLicensed = nodeInfo.user.isLicensed
|
||||
newUser.role = Int32(nodeInfo.user.role.rawValue)
|
||||
newNode.user = newUser
|
||||
|
|
@ -354,6 +361,13 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
fetchedNode[0].user!.isLicensed = nodeInfo.user.isLicensed
|
||||
fetchedNode[0].user!.role = Int32(nodeInfo.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfo.user.hwModel.rawValue)
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == fetchedNode[0].user!.hwModelId })
|
||||
fetchedNode[0].user!.hwDisplayName = dh?.displayName
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if fetchedNode[0].user == nil && nodeInfo.num > Constants.minimumNodeNum {
|
||||
|
||||
|
|
|
|||
18
Meshtastic/Measurement/CustomFormatters.swift
Normal file
18
Meshtastic/Measurement/CustomFormatters.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// CustomFormatters.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 8/4/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Custom altitude formatter that always returns the provided unit
|
||||
/// Needs to be used in conjunction with logic that checks for metric and displays the right value.
|
||||
public var altitudeFormatter: MeasurementFormatter {
|
||||
let formatter = MeasurementFormatter()
|
||||
formatter.unitOptions = .providedUnit
|
||||
formatter.unitStyle = .long
|
||||
formatter.numberFormatter.maximumFractionDigits = 1
|
||||
return formatter
|
||||
}
|
||||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV 41.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV 42.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,450 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22758" systemVersion="23G5075b" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="green" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ledState" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="red" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="ambientLightingConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceLoggingEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
|
||||
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="downlinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="detectionTriggeredHigh" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="disableTripleClick" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="isManaged" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="ledHeartbeatEnabled" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="tzdef" optional="YES" attributeType="String"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
|
||||
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hwModel" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="units" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wakeOnTapOrMotion" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="useI2SAsBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="routeLocation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RouteEntity" inverseName="locations" inverseEntity="RouteEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ignoreMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPositionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="13" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPublishIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mapReportingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="proxyToClientEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="root" optional="YES" attributeType="String" defaultValueString="msh"/>
|
||||
<attribute name="tlsEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ntpServer" optional="YES" attributeType="String"/>
|
||||
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="favorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="firstHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hopsAway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="viaMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
|
||||
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
|
||||
<relationship name="pax" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PaxCounterEntity" inverseName="paxNode" inverseEntity="PaxCounterEntity"/>
|
||||
<relationship name="paxCounterConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PaxCounterConfigEntity" inverseName="paxCounterConfigNode" inverseEntity="PaxCounterConfigEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="powerConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PowerConfigEntity" inverseName="powerConfigNode" inverseEntity="PowerConfigEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="rtttlConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RTTTLConfigEntity" inverseName="rtttlConfigNode" inverseEntity="RTTTLConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="storeForwardConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreForwardConfigEntity" inverseName="storeForwardConfigNode" inverseEntity="StoreForwardConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="traceRoutes" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteEntity" inverseName="node" inverseEntity="TraceRouteEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PaxCounterConfigEntity" representedClassName="PaxCounterConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleThreshold" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiThreshold" optional="YES" attributeType="Integer 32" defaultValueString="-80" usesScalarValueType="YES"/>
|
||||
<relationship name="paxCounterConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="paxCounterConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PaxCounterEntity" representedClassName="PaxCounterEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ble" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="paxNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="pax" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="broadcastSmartMinimumDistance" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="broadcastSmartMinimumIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsEnGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latest" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="precisionBits" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PowerConfigEntity" representedClassName="PowerConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adcMultiplierOverride" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceBatteryInaAddress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isPowerSaving" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="lsSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minWakeSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="onBatteryShutdownAfterSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="waitBluetoothSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="powerConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="powerConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RouteEntity" representedClassName="RouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="color" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="distance" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="elevationGain" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="endDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="notes" optional="YES" attributeType="String"/>
|
||||
<relationship name="locations" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="LocationEntity" inverseName="routeLocation" inverseEntity="LocationEntity"/>
|
||||
</entity>
|
||||
<entity name="RTTTLConfigEntity" representedClassName="RTTTLConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ringtone" optional="YES" attributeType="String" maxValueString="228" defaultValueString=""/>
|
||||
<relationship name="rtttlConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rtttlConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideConsoleSerialPort" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="StoreForwardConfigEntity" representedClassName="StoreForwardConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="powerMeasurementEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="powerScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="powerUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="iaq" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptimeSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="weight" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windDirection" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="windGust" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windLull" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windSpeed" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteEntity" representedClassName="TraceRouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="route" optional="YES" attributeType="Transformable" customClassName="[UInt32]"/>
|
||||
<attribute name="routeText" optional="YES" attributeType="String"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteHopEntity" inverseName="traceRoute" inverseEntity="TraceRouteHopEntity"/>
|
||||
<relationship name="node" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="traceRoutes" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteHopEntity" representedClassName="TraceRouteHopEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="traceRoute" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TraceRouteEntity" inverseName="hops" inverseEntity="TraceRouteEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="hwModelId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastMessage" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numString" optional="YES" attributeType="String"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="id"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -93,7 +93,7 @@ struct MeshtasticAppleApp: App {
|
|||
if url.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.addChannels = Bool(self.incomingUrl?["add"] ?? "false") ?? false
|
||||
if ((self.incomingUrl?.absoluteString.lowercased().contains("?")) != nil) {
|
||||
if self.incomingUrl?.absoluteString.lowercased().contains("?") != nil {
|
||||
guard let cs = components.last!.components(separatedBy: "?").first else {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,39 +103,6 @@ extension NSPersistentContainer {
|
|||
case invalidSource(String)
|
||||
}
|
||||
|
||||
/// Restore a persistent store for a URL `backupURL`.
|
||||
/// **Be very careful with this**. To restore a persistent store, the current persistent store must be removed from the container. When that happens, **all currently loaded Core Data objects** will become invalid. Using them after restoring will cause your app to crash. When calling this method you **must** ensure that you do not continue to use any previously fetched managed objects or existing fetched results controllers. **If this method does not throw, that does not mean your app is safe.** You need to take extra steps to prevent crashes. The details vary depending on the nature of your app.
|
||||
/// - Parameter backupURL: A file URL containing backup copies of all currently loaded persistent stores.
|
||||
/// - Throws: `CopyPersistentStoreError` in various situations.
|
||||
/// - Returns: Nothing. If no errors are thrown, the restore is complete.
|
||||
// func restorePersistentStore(from backupURL: URL) throws -> Void {
|
||||
// guard backupURL.isFileURL else {
|
||||
// throw CopyPersistentStoreErrors.invalidSource("Backup URL must be a file URL")
|
||||
// }
|
||||
//
|
||||
// for persistentStoreDescription in persistentStoreDescriptions {
|
||||
// guard let loadedStoreURL = persistentStoreDescription.url else {
|
||||
// continue
|
||||
// }
|
||||
// guard FileManager.default.fileExists(atPath: backupURL.path) else {
|
||||
// throw CopyPersistentStoreErrors.invalidSource("Missing backup store for \(backupURL)")
|
||||
// }
|
||||
// do {
|
||||
// let storeOptions = persistentStoreDescription.options
|
||||
// let configurationName = persistentStoreDescription.configuration
|
||||
// let storeType = persistentStoreDescription.type
|
||||
//
|
||||
// // Replace the current store with the backup copy. This has a side effect of removing the current store from the Core Data stack.
|
||||
// // When restoring, it's necessary to use the current persistent store coordinator.
|
||||
// try persistentStoreCoordinator.replacePersistentStore(at: loadedStoreURL, destinationOptions: storeOptions, withPersistentStoreFrom: backupURL, sourceOptions: storeOptions, ofType: storeType)
|
||||
// // Add the persistent store at the same location we've been using, because it was removed in the previous step.
|
||||
// try persistentStoreCoordinator.addPersistentStore(ofType: storeType, configurationName: configurationName, at: loadedStoreURL, options: storeOptions)
|
||||
// } catch {
|
||||
// throw CopyPersistentStoreErrors.copyStoreError("Could not restore: \(error.localizedDescription)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
/// Restore backup persistent stores located in the directory referenced by `backupURL`.
|
||||
///
|
||||
/// **Be very careful with this**. To restore a persistent store, the current persistent store must be removed from the container. When that happens, **all currently loaded Core Data objects** will become invalid. Using them after restoring will cause your app to crash. When calling this method you **must** ensure that you do not continue to use any previously fetched managed objects or existing fetched results controllers. **If this method does not throw, that does not mean your app is safe.** You need to take extra steps to prevent crashes. The details vary depending on the nature of your app.
|
||||
|
|
|
|||
|
|
@ -177,6 +177,13 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
newUser.shortName = newUserMessage.shortName
|
||||
newUser.role = Int32(newUserMessage.role.rawValue)
|
||||
newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased()
|
||||
newUser.hwModelId = Int32(newUserMessage.hwModel.rawValue)
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == newUser.hwModelId })
|
||||
newUser.hwDisplayName = dh?.displayName
|
||||
}
|
||||
}
|
||||
newNode.user = newUser
|
||||
|
||||
if UserDefaults.newNodeNotifications {
|
||||
|
|
@ -257,6 +264,13 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
fetchedNode[0].user!.shortName = nodeInfoMessage.user.shortName
|
||||
fetchedNode[0].user!.role = Int32(nodeInfoMessage.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfoMessage.user.hwModel.rawValue)
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == fetchedNode[0].user?.hwModelId ?? 0 })
|
||||
fetchedNode[0].user!.hwDisplayName = dh?.displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if packet.hopStart != 0 && packet.hopLimit <= packet.hopStart {
|
||||
fetchedNode[0].hopsAway = Int32(packet.hopStart - packet.hopLimit)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"platformioTarget": "tlora-v2",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": false,
|
||||
"displayName": "T-LoRa V2"
|
||||
"displayName": "LILYGO T-LoRa V2"
|
||||
},
|
||||
{
|
||||
"hwModel": 2,
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"platformioTarget": "tlora-v1",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": false,
|
||||
"displayName": "T-LoRa V1"
|
||||
"displayName": "LILYGO T-LoRa V1"
|
||||
},
|
||||
{
|
||||
"hwModel": 3,
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
"platformioTarget": "tlora-v2-1-1_6",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-LoRa V2.1-1.6"
|
||||
"displayName": "LILYGO T-LoRa V2.1-1.6"
|
||||
},
|
||||
{
|
||||
"hwModel": 4,
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
"platformioTarget": "tbeam",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-Beam"
|
||||
"displayName": "LILYGO T-Beam"
|
||||
},
|
||||
{
|
||||
"hwModel": 5,
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
"platformioTarget": "tbeam0_7",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": false,
|
||||
"displayName": "T-Beam V0.7"
|
||||
"displayName": "LILYGO T-Beam V0.7"
|
||||
},
|
||||
{
|
||||
"hwModel": 7,
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
"platformioTarget": "t-echo",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-Echo"
|
||||
"displayName": "LILYGO T-Echo"
|
||||
},
|
||||
{
|
||||
"hwModel": 8,
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
"platformioTarget": "tlora-v1_3",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": false,
|
||||
"displayName": "T-LoRa V1.1-1.3"
|
||||
"displayName": "LILYGO T-LoRa V1.1-1.3"
|
||||
},
|
||||
{
|
||||
"hwModel": 9,
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
"platformioTarget": "rak4631",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"displayName": "RAK4631"
|
||||
"displayName": "RAK WisBlock 4631"
|
||||
},
|
||||
{
|
||||
"hwModel": 10,
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
"platformioTarget": "tbeam-s3-core",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-Beam S3 Core"
|
||||
"displayName": "LILYGO T-Beam S3 Core"
|
||||
},
|
||||
{
|
||||
"hwModel": 13,
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
"platformioTarget": "rak11200",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": false,
|
||||
"displayName": "RAK11200"
|
||||
"displayName": "RAK WisBlock 11200"
|
||||
},
|
||||
{
|
||||
"hwModel": 14,
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
"platformioTarget": "tlora-v2-1-1_8",
|
||||
"architecture": "esp32",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-LoRa V2.1-1.8"
|
||||
"displayName": "LILYGO T-LoRa V2.1-1.8"
|
||||
},
|
||||
{
|
||||
"hwModel": 16,
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
"platformioTarget": "tlora-t3s3-v1",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-LoRa T3-S3"
|
||||
"displayName": "LILYGO T-LoRa T3-S3"
|
||||
},
|
||||
{
|
||||
"hwModel": 17,
|
||||
|
|
@ -143,6 +143,14 @@
|
|||
"activelySupported": true,
|
||||
"displayName": "Nano G2 Ultra"
|
||||
},
|
||||
{
|
||||
"hwModel": 21,
|
||||
"hwModelSlug": "WIO_WM1110",
|
||||
"platformioTarget": "wio-tracker-wm1110",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"displayName": "Seeed Wio WM1110 Tracker"
|
||||
},
|
||||
{
|
||||
"hwModel": 25,
|
||||
"hwModelSlug": "STATION_G1",
|
||||
|
|
@ -157,7 +165,7 @@
|
|||
"platformioTarget": "rak11310",
|
||||
"architecture": "rp2040",
|
||||
"activelySupported": true,
|
||||
"displayName": "RAK11310"
|
||||
"displayName": "RAK WisBlock 11310"
|
||||
},
|
||||
{
|
||||
"hwModel": 29,
|
||||
|
|
@ -165,7 +173,7 @@
|
|||
"platformioTarget": "canaryone",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"displayName": "CanaryOne"
|
||||
"displayName": "Canary One"
|
||||
},
|
||||
{
|
||||
"hwModel": 30,
|
||||
|
|
@ -277,7 +285,7 @@
|
|||
"platformioTarget": "t-deck",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-Deck"
|
||||
"displayName": "LILYGO T-Deck"
|
||||
},
|
||||
{
|
||||
"hwModel": 51,
|
||||
|
|
@ -285,7 +293,7 @@
|
|||
"platformioTarget": "t-watch-s3",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "T-Watch S3"
|
||||
"displayName": "LILYGO T-Watch S3"
|
||||
},
|
||||
{
|
||||
"hwModel": 52,
|
||||
|
|
@ -360,11 +368,35 @@
|
|||
"displayName": "RadioMaster 900 Bandit Nano"
|
||||
},
|
||||
{
|
||||
"hwModel": 21,
|
||||
"hwModelSlug": "WIO_WM1110",
|
||||
"platformioTarget": "wio-tracker-wm1110",
|
||||
"hwModel": 66,
|
||||
"hwModelSlug": "HELTEC_VISION_MASTER_T190",
|
||||
"platformioTarget": "heltec-vision-master-T190",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "Heltec Vision Master T190"
|
||||
},
|
||||
{
|
||||
"hwModel": 67,
|
||||
"hwModelSlug": "HELTEC_VISION_MASTER_E213",
|
||||
"platformioTarget": "heltec-vision-master-e213",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "Heltec Vision Master E213"
|
||||
},
|
||||
{
|
||||
"hwModel": 68,
|
||||
"hwModelSlug": "HELTEC_VISION_MASTER_E290",
|
||||
"platformioTarget": "heltec-vision-master-e290",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"displayName": "Heltec Vision Master E290"
|
||||
},
|
||||
{
|
||||
"hwModel": 71,
|
||||
"hwModelSlug": "TRACKER_T1000_E",
|
||||
"platformioTarget": "tracker-t1000-e",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"displayName": "Seeed Wio WM1110 Tracker"
|
||||
"displayName": "Seeed Card Tracker T1000-E"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,36 +14,34 @@ struct LoRaSignalStrengthMeter: View {
|
|||
var compact: Bool
|
||||
var body: some View {
|
||||
|
||||
if snr != 0.0 && rssi != 0 {
|
||||
let signalStrength = getLoRaSignalStrength(snr: snr, rssi: rssi, preset: preset)
|
||||
let gradient = Gradient(colors: [.red, .orange, .yellow, .green])
|
||||
if !compact {
|
||||
VStack {
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: snr, preset: ModemPresets.longFast))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: rssi))
|
||||
.font(.caption2)
|
||||
}
|
||||
} else {
|
||||
VStack {
|
||||
Gauge(value: Double(signalStrength.rawValue), in: 0...3) {
|
||||
} currentValueLabel: {
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
.font(.callout)
|
||||
.frame(width: 30)
|
||||
Text("Signal \(signalStrength.description)")
|
||||
.font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
.gaugeStyle(.accessoryLinear)
|
||||
.tint(gradient)
|
||||
.font(.caption)
|
||||
let signalStrength = getLoRaSignalStrength(snr: snr, rssi: rssi, preset: preset)
|
||||
let gradient = Gradient(colors: [.red, .orange, .yellow, .green])
|
||||
if !compact {
|
||||
VStack {
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: snr, preset: ModemPresets.longFast))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: rssi))
|
||||
.font(.caption2)
|
||||
}
|
||||
} else {
|
||||
VStack {
|
||||
Gauge(value: Double(signalStrength.rawValue), in: 0...3) {
|
||||
} currentValueLabel: {
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
.font(.callout)
|
||||
.frame(width: 30)
|
||||
Text("Signal \(signalStrength.description)")
|
||||
.font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
}
|
||||
.gaugeStyle(.accessoryLinear)
|
||||
.tint(gradient)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
overlay.minimumZ = UserDefaults.mapTileServer.zoomRange.startIndex
|
||||
overlay.maximumZ = UserDefaults.mapTileServer.zoomRange.endIndex
|
||||
mapView.addOverlay(overlay, level: UserDefaults.mapTilesAboveLabels ? .aboveLabels : .aboveRoads)
|
||||
|
||||
case .satellite:
|
||||
mapView.mapType = .satellite
|
||||
case .hybrid:
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ struct UserList: View {
|
|||
private func searchUserList() {
|
||||
|
||||
/// Case Insensitive Search Text Predicates
|
||||
let searchPredicates = ["userId", "numString", "hwModel", "longName", "shortName"].map { property in
|
||||
let searchPredicates = ["userId", "numString", "hwModel", "hwDisplayName", "longName", "shortName"].map { property in
|
||||
return NSPredicate(format: "%K CONTAINS[c] %@", property, searchText)
|
||||
}
|
||||
/// Create a compound predicate using each text search preicate as an OR
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct UserMessageList: View {
|
|||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
||||
|
||||
// Keyboard State
|
||||
@FocusState var messageFieldFocused: Bool
|
||||
// View State Items
|
||||
|
|
|
|||
|
|
@ -108,6 +108,57 @@ struct DeviceMetricsLog: View {
|
|||
.chartLegend(position: .automatic, alignment: .bottom)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
Chart {
|
||||
ForEach(chartData, id: \.self) { point in
|
||||
Plot {
|
||||
LineMark(
|
||||
x: .value("x", point.time!),
|
||||
y: .value("y", point.batteryLevel)
|
||||
)
|
||||
}
|
||||
.accessibilityLabel("Line Series")
|
||||
.accessibilityValue("X: \(point.time!), Y: \(point.batteryLevel)")
|
||||
.foregroundStyle(batteryChartColor)
|
||||
.interpolationMethod(.linear)
|
||||
Plot {
|
||||
PointMark(
|
||||
x: .value("x", point.time!),
|
||||
y: .value("y", point.channelUtilization)
|
||||
)
|
||||
.symbolSize(25)
|
||||
}
|
||||
.accessibilityLabel("Line Series")
|
||||
.accessibilityValue("X: \(point.time!), Y: \(point.channelUtilization)")
|
||||
.foregroundStyle(channelUtilizationChartColor)
|
||||
RuleMark(y: .value("Network Status Orange", 25))
|
||||
.lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10]))
|
||||
.foregroundStyle(.orange)
|
||||
RuleMark(y: .value("Network Status Red", 50))
|
||||
.lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10]))
|
||||
.foregroundStyle(.red)
|
||||
Plot {
|
||||
PointMark(
|
||||
x: .value("x", point.time!),
|
||||
y: .value("y", point.airUtilTx)
|
||||
)
|
||||
.symbolSize(25)
|
||||
}
|
||||
.accessibilityLabel("Line Series")
|
||||
.accessibilityValue("X: \(point.time!), Y: \(point.airUtilTx)")
|
||||
.foregroundStyle(airtimeChartColor)
|
||||
}
|
||||
}
|
||||
.chartXAxis(content: {
|
||||
AxisMarks(position: .top)
|
||||
})
|
||||
.chartXAxis(.automatic)
|
||||
.chartYScale(domain: 0...100)
|
||||
.chartForegroundStyleScale([
|
||||
idiom == .phone ? "Battery" : "Battery Level": batteryChartColor,
|
||||
"Channel Utilization": channelUtilizationChartColor,
|
||||
"Airtime": airtimeChartColor
|
||||
])
|
||||
.chartLegend(position: .automatic, alignment: .bottom)
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 240)
|
||||
|
|
|
|||
|
|
@ -3,16 +3,12 @@ import OSLog
|
|||
import SwiftUI
|
||||
|
||||
struct DeleteNodeButton: View {
|
||||
|
||||
var bleManager: BLEManager
|
||||
|
||||
var context: NSManagedObjectContext
|
||||
|
||||
var connectedNode: NodeInfoEntity
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var isPresentingAlert = false
|
||||
|
||||
var body: some View {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import MapKit
|
|||
|
||||
@available(iOS 17.0, macOS 14.0, *)
|
||||
struct PositionPopover: View {
|
||||
|
||||
@ObservedObject var locationsHandler = LocationsHandler.shared
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
|
@ -74,8 +75,17 @@ struct PositionPopover: View {
|
|||
.padding(.bottom, 5)
|
||||
/// Altitude
|
||||
Label {
|
||||
Text("Altitude: \(distanceFormatter.string(fromDistance: Double(position.altitude)))")
|
||||
.foregroundColor(.primary)
|
||||
let formatter = MeasurementFormatter()
|
||||
let distanceInMeters = Measurement(value: Double(position.altitude), unit: UnitLength.meters)
|
||||
let distanceInFeet = distanceInMeters.converted(to: UnitLength.feet)
|
||||
if Locale.current.measurementSystem == .metric {
|
||||
Text(altitudeFormatter.string(from: distanceInMeters))
|
||||
.foregroundColor(.primary)
|
||||
} else {
|
||||
Text(altitudeFormatter.string(from: distanceInFeet))
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
|
||||
} icon: {
|
||||
Image(systemName: "mountain.2.fill")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ struct NodeDetail: View {
|
|||
Section("Hardware") {
|
||||
NodeInfoItem(node: node)
|
||||
}
|
||||
|
||||
Section("Node") {
|
||||
HStack {
|
||||
Label {
|
||||
|
|
@ -65,7 +64,7 @@ struct NodeDetail: View {
|
|||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
Spacer()
|
||||
Text(node.user?.userId ?? "?")
|
||||
Text(node.num.toHex())
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,64 +11,64 @@ import MapKit
|
|||
|
||||
struct NodeInfoItem: View {
|
||||
|
||||
@ObservedObject
|
||||
var node: NodeInfoEntity
|
||||
@ObservedObject var node: NodeInfoEntity
|
||||
|
||||
var modemPreset: ModemPresets = ModemPresets(
|
||||
rawValue: UserDefaults.modemPreset
|
||||
) ?? ModemPresets.longFast
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
CircleText(
|
||||
text: node.user?.shortName ?? "?",
|
||||
color: Color(UIColor(hex: UInt32(node.num))),
|
||||
circleSize: 65
|
||||
)
|
||||
if let user = node.user {
|
||||
VStack(alignment: .center) {
|
||||
if user.hwModel != "UNSET" {
|
||||
Image(user.hardwareImage ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 65, height: 65)
|
||||
.cornerRadius(5)
|
||||
Text(String(node.user!.hwModel ?? "unset".localized))
|
||||
.font(.caption2)
|
||||
.frame(maxWidth: 80)
|
||||
} else {
|
||||
Image(systemName: "person.crop.circle.badge.questionmark")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 65, height: 65)
|
||||
.cornerRadius(5)
|
||||
Text(String("incomplete".localized))
|
||||
.font(.caption)
|
||||
.frame(maxWidth: 80)
|
||||
ViewThatFits(in: .horizontal) {
|
||||
VStack {
|
||||
if let user = node.user {
|
||||
HStack(alignment: .center) {
|
||||
if user.hwModel != "UNSET" {
|
||||
Image(user.hardwareImage ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 65, height: 65)
|
||||
.cornerRadius(5)
|
||||
Text(String(node.user?.hwDisplayName ?? (node.user?.hwModel ?? "unset".localized)))
|
||||
.font(.callout)
|
||||
} else {
|
||||
Image(systemName: "person.crop.circle.badge.questionmark")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 65, height: 65)
|
||||
.cornerRadius(5)
|
||||
Text(String("incomplete".localized))
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node.snr != 0 && !node.viaMqtt {
|
||||
VStack(alignment: .center) {
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: modemPreset)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: node.snr, preset: modemPreset))
|
||||
.font(.caption2)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
.font(.caption)
|
||||
HStack(alignment: .center) {
|
||||
Spacer()
|
||||
CircleText(
|
||||
text: node.user?.shortName ?? "?",
|
||||
color: Color(UIColor(hex: UInt32(node.num))),
|
||||
circleSize: 75
|
||||
)
|
||||
if node.snr != 0 && !node.viaMqtt {
|
||||
Spacer()
|
||||
VStack {
|
||||
let signalStrength = getLoRaSignalStrength(snr: node.snr, rssi: node.rssi, preset: modemPreset)
|
||||
LoRaSignalStrengthIndicator(signalStrength: signalStrength)
|
||||
Text("Signal \(signalStrength.description)").font(.footnote)
|
||||
Text("SNR \(String(format: "%.2f", node.snr))dB")
|
||||
.foregroundColor(getSnrColor(snr: node.snr, preset: modemPreset))
|
||||
.font(.caption)
|
||||
Text("RSSI \(node.rssi)dB")
|
||||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
if node.telemetries?.count ?? 0 > 0 {
|
||||
Spacer()
|
||||
BatteryGauge(node: node)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.frame(minWidth: 110, maxWidth: 175)
|
||||
}
|
||||
|
||||
if node.telemetries?.count ?? 0 > 0 {
|
||||
BatteryGauge(node: node)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ struct NodeListItem: View {
|
|||
LazyVStack(alignment: .leading) {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 70)
|
||||
.padding(.trailing, 5)
|
||||
BatteryLevelCompact(node: node, font: .caption, iconFont: .callout, color: .accentColor)
|
||||
|
|
@ -99,6 +98,17 @@ struct NodeListItem: View {
|
|||
DistanceText(meters: metersAway)
|
||||
.font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption)
|
||||
.foregroundColor(.gray)
|
||||
let trueBearing = getBearingBetweenTwoPoints(point1: myCoord, point2: CLLocation(latitude: lastPostion.coordinate.latitude, longitude: lastPostion.coordinate.longitude))
|
||||
let headingDegrees = Angle.degrees(trueBearing)
|
||||
Image(systemName: "location.north")
|
||||
.font(.callout)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
let heading = Measurement(value: trueBearing, unit: UnitAngle.degrees)
|
||||
Text("\(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))")
|
||||
.font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -114,6 +124,17 @@ struct NodeListItem: View {
|
|||
DistanceText(meters: metersAway)
|
||||
.font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption)
|
||||
.foregroundColor(.secondary)
|
||||
let trueBearing = getBearingBetweenTwoPoints(point1: myCoord, point2: CLLocation(latitude: lastPostion.coordinate.latitude, longitude: lastPostion.coordinate.longitude))
|
||||
let headingDegrees = Angle.degrees(trueBearing)
|
||||
Image(systemName: "location.north")
|
||||
.font(.callout)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
let heading = Measurement(value: trueBearing, unit: UnitAngle.degrees)
|
||||
Text("\(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))")
|
||||
.font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +185,7 @@ struct NodeListItem: View {
|
|||
.frame(width: 30)
|
||||
}
|
||||
if node.hasEnvironmentMetrics {
|
||||
Image(systemName: "cloud.sun.rain.fill")
|
||||
Image(systemName: "cloud.sun.rain")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.callout)
|
||||
.frame(width: 30)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ struct MeshMap: View {
|
|||
}
|
||||
.onChange(of: router.navigationState) {
|
||||
guard case .map(let selectedNodeNum) = router.navigationState else { return }
|
||||
//TODO: handle deep link for waypoints
|
||||
// TODO: handle deep link for waypoints
|
||||
}
|
||||
.onChange(of: (selectedMapLayer)) { newMapLayer in
|
||||
switch selectedMapLayer {
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ struct NodeList: View {
|
|||
@State private var hopsAway: Double = -1.0
|
||||
@State private var roleFilter = false
|
||||
@State private var deviceRoles: Set<Int> = []
|
||||
|
||||
@State private var isPresentingTraceRouteSentAlert = false
|
||||
@State private var isPresentingPositionSentAlert = false
|
||||
@State private var isPresentingPositionFailedAlert = false
|
||||
@State private var isPresentingDeleteNodeAlert = false
|
||||
@State private var deleteNodeId: Int64 = 0
|
||||
|
||||
|
||||
var boolFilters: [Bool] {[
|
||||
isOnline,
|
||||
isFavorite,
|
||||
|
|
@ -344,7 +344,7 @@ struct NodeList: View {
|
|||
|
||||
private func searchNodeList() async {
|
||||
/// Case Insensitive Search Text Predicates
|
||||
let searchPredicates = ["user.userId", "user.numString", "user.hwModel", "user.longName", "user.shortName"].map { property in
|
||||
let searchPredicates = ["user.userId", "user.numString", "user.hwModel", "user.hwDisplayName", "user.longName", "user.shortName"].map { property in
|
||||
return NSPredicate(format: "%K CONTAINS[c] %@", property, searchText)
|
||||
}
|
||||
/// Create a compound predicate using each text search preicate as an OR
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ struct TraceRouteLog: View {
|
|||
}
|
||||
.font(.title2)
|
||||
}
|
||||
|
||||
if selectedRoute?.response ?? false {
|
||||
if selectedRoute?.hasPositions ?? false {
|
||||
Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) {
|
||||
|
|
|
|||
|
|
@ -243,13 +243,11 @@ struct Settings: View {
|
|||
|
||||
var loggingSection: some View {
|
||||
Section(header: Text("logging")) {
|
||||
if #available (iOS 17.0, *) {
|
||||
NavigationLink(value: SettingsNavigationState.debugLogs) {
|
||||
Label {
|
||||
Text("Logs")
|
||||
} icon: {
|
||||
Image(systemName: "scroll")
|
||||
}
|
||||
NavigationLink(value: SettingsNavigationState.debugLogs) {
|
||||
Label {
|
||||
Text("Logs")
|
||||
} icon: {
|
||||
Image(systemName: "scroll")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -401,7 +399,9 @@ struct Settings: View {
|
|||
radioConfigurationSection
|
||||
deviceConfigurationSection
|
||||
moduleConfigurationSection
|
||||
loggingSection
|
||||
if #available (iOS 17.0, *) {
|
||||
loggingSection
|
||||
}
|
||||
#if DEBUG
|
||||
developersSection
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue