mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #96 from meshtastic/feature/lora_config
Add Display Config and Position Config
This commit is contained in:
commit
b64b3b1d94
9 changed files with 699 additions and 76 deletions
|
|
@ -688,6 +688,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
|
||||
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
|
||||
positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed)
|
||||
positionPacket.groundTrack = UInt32(LocationHelper.currentHeading)
|
||||
|
||||
var meshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(destNum)
|
||||
|
|
@ -849,6 +851,76 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return false
|
||||
}
|
||||
|
||||
public func saveDeviceConfig(config: Config.DeviceConfig, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setConfig.device = config
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = wantResponse
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public func saveDisplayConfig(config: Config.DisplayConfig, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setConfig.display = config
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = wantResponse
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public func saveLoRaConfig(config: Config.LoRaConfig, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
|
|
@ -883,4 +955,39 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
public func savePositionConfig(config: Config.PositionConfig, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setConfig.position = config
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = wantResponse
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090)
|
||||
|
||||
static let DefaultAltitude = CLLocationDistance(integerLiteral: 0)
|
||||
static let DefaultSpeed = CLLocationSpeed(integerLiteral: 0)
|
||||
static let DefaultHeading = CLLocationDirection(integerLiteral: 0)
|
||||
|
||||
static var currentLocation: CLLocationCoordinate2D {
|
||||
|
||||
guard let location = shared.locationManager.location else {
|
||||
guard let location = shared.locationManager.location else {
|
||||
return DefaultLocation
|
||||
}
|
||||
return location.coordinate
|
||||
|
|
@ -25,6 +27,22 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
return altitude
|
||||
}
|
||||
|
||||
static var currentSpeed: CLLocationSpeed {
|
||||
|
||||
guard let speed = shared.locationManager.location?.speed else {
|
||||
return DefaultSpeed
|
||||
}
|
||||
return speed
|
||||
}
|
||||
|
||||
static var currentHeading: CLLocationDirection {
|
||||
|
||||
guard let speed = shared.locationManager.location?.course else {
|
||||
return DefaultHeading
|
||||
}
|
||||
return speed
|
||||
}
|
||||
|
||||
static var currentTimestamp: Date {
|
||||
|
||||
guard let timestamp = shared.locationManager.location?.timestamp else {
|
||||
|
|
|
|||
|
|
@ -14,25 +14,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
// We don't care about any of the Power settings
|
||||
// We don't want to manage wifi from the phone app and disconnect our device
|
||||
//if meshlogging { MeshLogger.log("⚙️ Local Config version \(config.version) received for \(nodeLongName)") }
|
||||
|
||||
// if (try! config.device.jsonString()) == "{}" {
|
||||
//
|
||||
// print("📟 Default Device config")
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// print("📟 Has Device config")
|
||||
// }
|
||||
//
|
||||
// if (try! config.position.jsonString()) == "{}" {
|
||||
//
|
||||
// print("📍 Default Position config")
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// print("📍 Has Position config")
|
||||
// }
|
||||
//
|
||||
|
||||
// if (try! config.power.jsonString() == "{\"lsSecs\":300}") {
|
||||
//
|
||||
// print("📍 Default Power config")
|
||||
|
|
@ -44,14 +26,152 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
// print(try! config.power.jsonString())
|
||||
// }
|
||||
//
|
||||
// if (try! config.display.jsonString()) == "{}" {
|
||||
//
|
||||
// print("🖥️ Default Display config")
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// print("🖥️ Has Display config")
|
||||
// }
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) {
|
||||
|
||||
var isDefault = false
|
||||
|
||||
if (try! config.device.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
print("📟 Default Device config")
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
if fetchedNode[0].deviceConfig == nil {
|
||||
|
||||
let newDeviceConfig = DeviceConfigEntity(context: context)
|
||||
|
||||
if isDefault {
|
||||
|
||||
// Client default protobuf value of 0
|
||||
newDeviceConfig.role = 0
|
||||
newDeviceConfig.serialEnabled = true
|
||||
newDeviceConfig.debugLogEnabled = false
|
||||
|
||||
} else {
|
||||
|
||||
// Client default protobuf value of 0
|
||||
newDeviceConfig.role = Int32(config.device.role.rawValue)
|
||||
newDeviceConfig.serialEnabled = !config.device.serialDisabled
|
||||
newDeviceConfig.debugLogEnabled = config.device.debugLogEnabled
|
||||
}
|
||||
fetchedNode[0].deviceConfig = newDeviceConfig
|
||||
|
||||
} else {
|
||||
|
||||
if isDefault {
|
||||
|
||||
// Client default protobuf value of 0
|
||||
fetchedNode[0].deviceConfig?.role = 0
|
||||
fetchedNode[0].deviceConfig?.serialEnabled = true
|
||||
fetchedNode[0].deviceConfig?.debugLogEnabled = false
|
||||
|
||||
} else {
|
||||
// Client default protobuf value of 0
|
||||
fetchedNode[0].deviceConfig?.role = Int32(config.device.role.rawValue)
|
||||
fetchedNode[0].deviceConfig?.serialEnabled = !config.device.serialDisabled
|
||||
fetchedNode[0].deviceConfig?.debugLogEnabled = config.device.debugLogEnabled
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated Device Config for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data DeviceConfigEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) {
|
||||
|
||||
var isDefault = false
|
||||
|
||||
if (try! config.display.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
print("🖥️ Default Display config")
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
if fetchedNode[0].displayConfig == nil {
|
||||
|
||||
let newDisplayConfig = DisplayConfigEntity(context: context)
|
||||
|
||||
if isDefault {
|
||||
|
||||
newDisplayConfig.screenOnSeconds = 0
|
||||
newDisplayConfig.screenCarouselInterval = 0
|
||||
newDisplayConfig.gpsFormat = 0
|
||||
|
||||
} else {
|
||||
|
||||
newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue)
|
||||
newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs)
|
||||
newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs)
|
||||
}
|
||||
fetchedNode[0].displayConfig = newDisplayConfig
|
||||
|
||||
} else {
|
||||
|
||||
if isDefault {
|
||||
|
||||
fetchedNode[0].displayConfig?.screenOnSeconds = 0
|
||||
fetchedNode[0].displayConfig?.screenCarouselInterval = 0
|
||||
fetchedNode[0].displayConfig?.gpsFormat = 0
|
||||
|
||||
} else {
|
||||
|
||||
fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue)
|
||||
fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs)
|
||||
fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated Display Config for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) {
|
||||
|
||||
|
|
@ -119,14 +239,99 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated LoRaConfig for node number: \(String(nodeNum))") }
|
||||
if meshlogging { MeshLogger.log("💾 Updated LoRa Config for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data MyInfoEntity: \(nsError)")
|
||||
print("💥 Error Updating Core Data LoRaConfigEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) {
|
||||
|
||||
var isDefault = false
|
||||
|
||||
if (try! config.position.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
// Found a node, save LoRa Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
if fetchedNode[0].positionConfig == nil {
|
||||
|
||||
let newPositionConfig = PositionConfigEntity(context: context)
|
||||
|
||||
if isDefault {
|
||||
|
||||
newPositionConfig.smartPositionEnabled = true
|
||||
newPositionConfig.deviceGpsEnabled = true
|
||||
newPositionConfig.fixedPosition = false
|
||||
newPositionConfig.gpsUpdateInterval = 0
|
||||
newPositionConfig.gpsAttemptTime = 0
|
||||
newPositionConfig.positionBroadcastSeconds = 0
|
||||
|
||||
} else {
|
||||
|
||||
newPositionConfig.smartPositionEnabled = !config.position.positionBroadcastSmartDisabled
|
||||
newPositionConfig.deviceGpsEnabled = !config.position.gpsDisabled
|
||||
newPositionConfig.fixedPosition = config.position.fixedPosition
|
||||
newPositionConfig.gpsUpdateInterval = Int32(config.position.gpsUpdateInterval)
|
||||
newPositionConfig.gpsAttemptTime = Int32(config.position.gpsAttemptTime)
|
||||
newPositionConfig.positionBroadcastSeconds = Int32(config.position.positionBroadcastSecs)
|
||||
}
|
||||
|
||||
fetchedNode[0].positionConfig = newPositionConfig
|
||||
|
||||
} else {
|
||||
|
||||
if isDefault {
|
||||
|
||||
fetchedNode[0].positionConfig?.smartPositionEnabled = true
|
||||
fetchedNode[0].positionConfig?.deviceGpsEnabled = true
|
||||
fetchedNode[0].positionConfig?.fixedPosition = false
|
||||
fetchedNode[0].positionConfig?.gpsUpdateInterval = 0
|
||||
fetchedNode[0].positionConfig?.gpsAttemptTime = 0
|
||||
fetchedNode[0].positionConfig?.positionBroadcastSeconds = 0
|
||||
|
||||
} else {
|
||||
|
||||
fetchedNode[0].positionConfig?.smartPositionEnabled = !config.position.positionBroadcastSmartDisabled
|
||||
fetchedNode[0].positionConfig?.deviceGpsEnabled = !config.position.gpsDisabled
|
||||
fetchedNode[0].positionConfig?.fixedPosition = config.position.fixedPosition
|
||||
fetchedNode[0].positionConfig?.gpsUpdateInterval = Int32(config.position.gpsUpdateInterval)
|
||||
fetchedNode[0].positionConfig?.gpsAttemptTime = Int32(config.position.gpsAttemptTime)
|
||||
fetchedNode[0].positionConfig?.positionBroadcastSeconds = Int32(config.position.positionBroadcastSecs)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated Position Config for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data PositionConfigEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" 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"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" 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"/>
|
||||
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
|
|
@ -53,8 +67,11 @@
|
|||
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<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="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
|
|
@ -64,6 +81,16 @@
|
|||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<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="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" 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="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
@ -104,9 +131,12 @@
|
|||
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="215"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="179"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="224"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="194"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="200"/>
|
||||
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
|
||||
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -20,29 +20,48 @@ enum DeviceRoles: Int, CaseIterable, Identifiable {
|
|||
switch self {
|
||||
|
||||
case .client:
|
||||
return "Client (default)"
|
||||
return "Client (default) - App connected client."
|
||||
case .clientMute:
|
||||
return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh."
|
||||
case .router:
|
||||
return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."
|
||||
return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."
|
||||
case .routerClient:
|
||||
return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client."
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DeviceConfig.Role {
|
||||
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return Config.DeviceConfig.Role.client
|
||||
case .clientMute:
|
||||
return Config.DeviceConfig.Role.clientMute
|
||||
case .router:
|
||||
return Config.DeviceConfig.Role.router
|
||||
case .routerClient:
|
||||
return Config.DeviceConfig.Role.routerClient
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@State private var isPresentingFactoryResetConfirm: Bool = false
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
||||
@State var deviceRole = 0
|
||||
@State var serialEnabled = true
|
||||
@State var debugLogEnabled = false
|
||||
|
||||
@State private var isPresentingFactoryResetConfirm: Bool = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
|
@ -77,24 +96,64 @@ struct DeviceConfig: View {
|
|||
}
|
||||
}
|
||||
|
||||
Button("Factory Reset", role: .destructive) {
|
||||
HStack {
|
||||
|
||||
isPresentingFactoryResetConfirm = true
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingFactoryResetConfirm
|
||||
) {
|
||||
Button("Erase all device settings?", role: .destructive) {
|
||||
Button {
|
||||
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
) {
|
||||
Button("Save Device Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
print("Factory Reset Failed")
|
||||
var dc = Config.DeviceConfig()
|
||||
dc.role = DeviceRoles(rawValue: deviceRole)!.protoEnumValue()
|
||||
dc.serialDisabled = !serialEnabled
|
||||
dc.debugLogEnabled = debugLogEnabled
|
||||
|
||||
if bleManager.saveDeviceConfig(config: dc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button("Factory Reset", role: .destructive) {
|
||||
|
||||
isPresentingFactoryResetConfirm = true
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingFactoryResetConfirm
|
||||
) {
|
||||
Button("Erase all device settings?", role: .destructive) {
|
||||
|
||||
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
|
||||
print("Factory Reset Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +169,37 @@ struct DeviceConfig: View {
|
|||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.deviceRole = Int(node.deviceConfig?.role ?? 0)
|
||||
self.serialEnabled = (node.deviceConfig?.serialEnabled ?? true)
|
||||
self.debugLogEnabled = node.deviceConfig?.debugLogEnabled ?? false
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceRole) { newRole in
|
||||
|
||||
if newRole != node.deviceConfig!.role {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: serialEnabled) { newSerial in
|
||||
|
||||
if newSerial != node.deviceConfig!.serialEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: debugLogEnabled) { newDebugLog in
|
||||
|
||||
if newDebugLog != node.deviceConfig!.debugLogEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum GpsFormat: Int, CaseIterable, Identifiable {
|
||||
enum GpsFormats: Int, CaseIterable, Identifiable {
|
||||
|
||||
case gpsFormatDec = 0
|
||||
case gpsFormatDms = 1
|
||||
|
|
@ -35,6 +35,24 @@ enum GpsFormat: Int, CaseIterable, Identifiable {
|
|||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
|
||||
|
||||
switch self {
|
||||
|
||||
case .gpsFormatDec:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec
|
||||
case .gpsFormatDms:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms
|
||||
case .gpsFormatUtm:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm
|
||||
case .gpsFormatMgrs:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs
|
||||
case .gpsFormatOlc:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc
|
||||
case .gpsFormatOsgr:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default of 0 is One Minute
|
||||
|
|
@ -109,6 +127,11 @@ struct DisplayConfig: View {
|
|||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
||||
@State var screenOnSeconds = 0
|
||||
@State var screenCarouselInterval = 0
|
||||
|
|
@ -146,7 +169,7 @@ struct DisplayConfig: View {
|
|||
}
|
||||
Section(header: Text("Format")) {
|
||||
Picker("GPS Format", selection: $gpsFormat ) {
|
||||
ForEach(GpsFormat.allCases) { lu in
|
||||
ForEach(GpsFormats.allCases) { lu in
|
||||
Text(lu.description)
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +180,43 @@ struct DisplayConfig: View {
|
|||
.listRowSeparator(.visible)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
) {
|
||||
Button("Save Display Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var dc = Config.DisplayConfig()
|
||||
dc.gpsFormat = GpsFormats(rawValue: gpsFormat)!.protoEnumValue()
|
||||
dc.screenOnSecs = UInt32(screenOnSeconds)
|
||||
dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval)
|
||||
|
||||
if bleManager.saveDisplayConfig(config: dc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Display Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
|
@ -167,7 +227,37 @@ struct DisplayConfig: View {
|
|||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.gpsFormat = Int(node.displayConfig?.gpsFormat ?? 0)
|
||||
self.screenOnSeconds = Int(node.displayConfig?.screenOnSeconds ?? 0)
|
||||
self.screenCarouselInterval = Int(node.displayConfig?.screenCarouselInterval ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: screenOnSeconds) { newScreenSecs in
|
||||
|
||||
if newScreenSecs != node.displayConfig!.screenOnSeconds {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: screenCarouselInterval) { newCarouselSecs in
|
||||
|
||||
if newCarouselSecs != node.displayConfig!.screenCarouselInterval {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: gpsFormat) { newGpsFormat in
|
||||
|
||||
if newGpsFormat != node.displayConfig!.gpsFormat {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,11 +189,11 @@ struct LoRaConfig: View {
|
|||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
||||
@State var region = 0
|
||||
@State var modemPreset = 0
|
||||
@State var hopLimit = 0
|
||||
@State var hasChanges = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -242,6 +242,7 @@ struct LoRaConfig: View {
|
|||
isPresentingSaveConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
|
|
@ -250,6 +251,7 @@ struct LoRaConfig: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
) {
|
||||
|
|
@ -285,7 +287,6 @@ struct LoRaConfig: View {
|
|||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
print("got hops \(node.loRaConfig?.hopLimit ?? 0)")
|
||||
self.hopLimit = Int(node.loRaConfig?.hopLimit ?? 0)
|
||||
self.region = Int(node.loRaConfig?.regionCode ?? 0)
|
||||
self.modemPreset = Int(node.loRaConfig?.modemPreset ?? 0)
|
||||
|
|
@ -314,7 +315,6 @@ struct LoRaConfig: View {
|
|||
hasChanges = true
|
||||
}
|
||||
}
|
||||
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,11 @@ struct PositionConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
||||
@State var smartPositionEnabled = true
|
||||
@State var deviceGpsEnabled = true
|
||||
@State var fixedPosition = false
|
||||
|
|
@ -125,8 +130,6 @@ struct PositionConfig: View {
|
|||
@State var includePosSpeed = false
|
||||
@State var includePosHeading = false
|
||||
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
|
@ -181,6 +184,8 @@ struct PositionConfig: View {
|
|||
|
||||
}
|
||||
}
|
||||
.disabled(!(node.myInfo?.hasGps ?? true))
|
||||
|
||||
Section(header: Text("Position Packet")) {
|
||||
|
||||
Toggle(isOn: $smartPositionEnabled) {
|
||||
|
|
@ -203,7 +208,7 @@ struct PositionConfig: View {
|
|||
|
||||
}
|
||||
}
|
||||
Section(header: Text("Position Flags")) {
|
||||
Section(header: Text("Position Flags - Non Functional")) {
|
||||
|
||||
Text("Optional fields to include when assembling position messages. the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss")
|
||||
.font(.caption)
|
||||
|
|
@ -213,44 +218,84 @@ struct PositionConfig: View {
|
|||
|
||||
Label("Altitude", systemImage: "arrow.up")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosSatsinview) {
|
||||
|
||||
Label("Number of satellites", systemImage: "skew")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.disabled(!(node.myInfo?.hasGps ?? true))
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosSeqNos) { //64
|
||||
|
||||
Label("Sequence number", systemImage: "number")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosTimestamp) { //128
|
||||
|
||||
Label("Timestamp", systemImage: "clock")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosHeading) { //128
|
||||
|
||||
Label("Vehicle heading", systemImage: "location.circle")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosSpeed) { //128
|
||||
|
||||
Label("Vehicle speed", systemImage: "speedometer")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
"Are you sure?",
|
||||
isPresented: $isPresentingSaveConfirm
|
||||
) {
|
||||
Button("Save Position Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var pc = Config.PositionConfig()
|
||||
pc.positionBroadcastSmartDisabled = !smartPositionEnabled
|
||||
pc.gpsDisabled = !deviceGpsEnabled
|
||||
pc.fixedPosition = fixedPosition
|
||||
pc.gpsUpdateInterval = UInt32(gpsUpdateInterval)
|
||||
pc.gpsAttemptTime = UInt32(gpsAttemptTime)
|
||||
pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds)
|
||||
|
||||
if bleManager.savePositionConfig(config: pc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +308,39 @@ struct PositionConfig: View {
|
|||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
self.smartPositionEnabled = node.positionConfig?.smartPositionEnabled ?? true
|
||||
self.deviceGpsEnabled = node.positionConfig?.deviceGpsEnabled ?? true
|
||||
self.fixedPosition = node.positionConfig?.fixedPosition ?? false
|
||||
self.gpsUpdateInterval = Int(node.positionConfig?.gpsUpdateInterval ?? 0)
|
||||
self.gpsAttemptTime = Int(node.positionConfig?.gpsAttemptTime ?? 0)
|
||||
self.positionBroadcastSeconds = Int(node.positionConfig?.positionBroadcastSeconds ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: smartPositionEnabled) { newSmartPosition in
|
||||
|
||||
if newSmartPosition != node.positionConfig!.smartPositionEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceGpsEnabled) { newDeviceGps in
|
||||
|
||||
if newDeviceGps != node.positionConfig!.deviceGpsEnabled {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.onChange(of: fixedPosition) { newFixed in
|
||||
|
||||
if newFixed != node.positionConfig!.fixedPosition {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ struct Settings: View {
|
|||
NavigationView {
|
||||
List {
|
||||
|
||||
let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0
|
||||
|
||||
Section("General") {
|
||||
NavigationLink {
|
||||
AppSettings()
|
||||
|
|
@ -43,26 +45,27 @@ struct Settings: View {
|
|||
}
|
||||
}
|
||||
|
||||
Section("Radio Configuration (Non-Functional Interaction Previews except for LoRa Config)") {
|
||||
Section("Radio Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
DeviceConfig()
|
||||
DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "flipphone")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Device")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
DisplayConfig()
|
||||
DisplayConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "display")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Display (Device Screen)")
|
||||
}
|
||||
|
||||
let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink() {
|
||||
|
||||
|
|
@ -77,7 +80,7 @@ struct Settings: View {
|
|||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
PositionConfig()
|
||||
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "location")
|
||||
|
|
@ -85,11 +88,12 @@ struct Settings: View {
|
|||
|
||||
Text("Position")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
}
|
||||
Section("Module Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
DisplayConfig()
|
||||
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "list.bullet.rectangle.fill")
|
||||
|
|
@ -107,6 +111,8 @@ struct Settings: View {
|
|||
|
||||
Text("Range Test")
|
||||
}
|
||||
.disabled(!(nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) || bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
TelemetryConfig()
|
||||
} label: {
|
||||
|
|
@ -116,6 +122,7 @@ struct Settings: View {
|
|||
|
||||
Text("Telemetry (Sensors)")
|
||||
}
|
||||
.disabled(true)
|
||||
}
|
||||
// Not Implemented:
|
||||
// External Notifications - Not Working
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue