mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #92 from meshtastic/feature/lora_config
Implement LoRa Config
This commit is contained in:
commit
7202289ad1
23 changed files with 635 additions and 270 deletions
|
|
@ -117,6 +117,7 @@
|
|||
DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = cannedmessages.pb.swift; sourceTree = "<group>"; };
|
||||
DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DD539501276DAA6A00AD86B1 /* MapLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLocation.swift; sourceTree = "<group>"; };
|
||||
DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD6B85A728009258000ACD6B /* ShareChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannel.swift; sourceTree = "<group>"; };
|
||||
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
|
||||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -809,7 +810,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.3.19;
|
||||
MARKETING_VERSION = 1.3.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -840,7 +841,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.3.19;
|
||||
MARKETING_VERSION = 1.3.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1013,11 +1014,12 @@
|
|||
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */,
|
||||
DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */,
|
||||
DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */,
|
||||
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */;
|
||||
currentVersion = DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = MeshtasticApple/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
peripheralName = name
|
||||
}
|
||||
|
||||
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: String(peripheralName.suffix(3)), longName: peripheralName, lastFourCode: last4Code, firmwareVersion: "Unknown", rssi: RSSI.intValue, bitrate: nil, channelUtilization: nil, airTime: nil, lastUpdate: Date(), subscribed: false, peripheral: peripheral)
|
||||
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: last4Code, longName: peripheralName, lastFourCode: last4Code, firmwareVersion: "Unknown", rssi: RSSI.intValue, bitrate: nil, channelUtilization: nil, airTime: nil, lastUpdate: Date(), subscribed: false, peripheral: peripheral)
|
||||
let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id })
|
||||
|
||||
if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected {
|
||||
|
|
@ -409,6 +409,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
lastConnnectionVersion = myInfo?.firmwareVersion ?? myInfo!.firmwareVersion ?? "Unknown"
|
||||
self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown"
|
||||
self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown"
|
||||
|
||||
}
|
||||
|
||||
} else if decodedInfo.nodeInfo.num != 0 {
|
||||
|
|
@ -425,17 +426,22 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if nodeInfo!.user != nil {
|
||||
|
||||
connectedPeripheral.name = nodeInfo!.user!.longName ?? "Unknown"
|
||||
connectedPeripheral.shortName = nodeInfo!.user!.shortName ?? "?????"
|
||||
connectedPeripheral.longName = nodeInfo!.user!.longName ?? "Unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if decodedInfo.config.version == 13 {
|
||||
} else if decodedInfo.config.isInitialized {
|
||||
|
||||
localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeLongName: self.connectedPeripheral.longName)
|
||||
localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
||||
} else {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
if decodedInfo.configCompleteID == 0 {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
}
|
||||
}
|
||||
case .textMessageApp:
|
||||
textMessageAppPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), meshLogging: meshLoggingEnabled, context: context!)
|
||||
|
|
@ -805,26 +811,60 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return false
|
||||
}
|
||||
|
||||
public func getConfig(destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
public func sendFactoryReset(destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var deviceConfig = Config.DeviceConfig()
|
||||
deviceConfig.factoryReset = true
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.getConfigRequest = AdminMessage.ConfigType.deviceConfig
|
||||
|
||||
adminPacket.variant = AdminMessage.OneOf_Variant.getConfigRequest(AdminMessage.ConfigType.loraConfig)
|
||||
|
||||
adminPacket.setConfig.device = deviceConfig
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 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 = false
|
||||
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()
|
||||
adminPacket.setConfig.lora = 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
|
||||
dataMessage.wantResponse = true
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
|
|
|
|||
|
|
@ -9,46 +9,130 @@ import Foundation
|
|||
import CoreData
|
||||
import SwiftUI
|
||||
|
||||
func localConfig (config: LocalConfig, meshlogging: Bool, context:NSManagedObjectContext, nodeLongName: String) {
|
||||
func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
// 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 meshlogging { MeshLogger.log("⚙️ Local Config version \(config.version) received for \(nodeLongName)") }
|
||||
|
||||
if (try! config.device.jsonString()) == "{}" {
|
||||
// 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")
|
||||
// print(try! config.power.jsonString())
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// print("📍 Has Power config")
|
||||
// print(try! config.power.jsonString())
|
||||
// }
|
||||
//
|
||||
// if (try! config.display.jsonString()) == "{}" {
|
||||
//
|
||||
// print("🖥️ Default Display config")
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// print("🖥️ Has Display config")
|
||||
// }
|
||||
|
||||
print("📟 Default Device config")
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) {
|
||||
|
||||
} else {
|
||||
var isDefault = false
|
||||
|
||||
print("📟 Has Device config")
|
||||
}
|
||||
|
||||
if (try! config.display.jsonString()) == "{}" {
|
||||
if (try! config.lora.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
}
|
||||
|
||||
print("🖥️ Default Display config")
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
} else {
|
||||
|
||||
print("🖥️ Has Display config")
|
||||
}
|
||||
|
||||
if (try! config.lora.jsonString()) == "{}" {
|
||||
|
||||
print("📡 Default LoRa config")
|
||||
|
||||
} else {
|
||||
|
||||
print("📡 Has LoRa config")
|
||||
}
|
||||
|
||||
if (try! config.position.jsonString()) == "{}" {
|
||||
|
||||
print("📍 Default Position config")
|
||||
|
||||
} else {
|
||||
|
||||
print("📍 Has Position config")
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
// Found a node, save LoRa Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
if fetchedNode[0].loRaConfig == nil {
|
||||
|
||||
let newLoRaConfig = LoRaConfigEntity(context: context)
|
||||
|
||||
if isDefault {
|
||||
|
||||
// UNSET default protobuf value of 0
|
||||
newLoRaConfig.regionCode = 0
|
||||
// LongFast default protobuf value of 0
|
||||
newLoRaConfig.modemPreset = 0
|
||||
// 3 Hops default protobuf value of 0
|
||||
newLoRaConfig.hopLimit = 0
|
||||
} else {
|
||||
|
||||
// UNSET default protobuf value of 0
|
||||
newLoRaConfig.regionCode = Int32(config.lora.region.rawValue)
|
||||
// LongFast default protobuf value of 0
|
||||
newLoRaConfig.modemPreset = Int32(config.lora.modemPreset.rawValue)
|
||||
// 3 Hops default protobuf value of 0
|
||||
newLoRaConfig.hopLimit = Int32(config.lora.hopLimit)
|
||||
}
|
||||
|
||||
fetchedNode[0].loRaConfig = newLoRaConfig
|
||||
|
||||
} else {
|
||||
|
||||
if isDefault {
|
||||
|
||||
// UNSET default protobuf value of 0
|
||||
fetchedNode[0].loRaConfig?.regionCode = 0
|
||||
// LongFast default protobuf value of 0
|
||||
fetchedNode[0].loRaConfig?.modemPreset = 0
|
||||
// 3 Hops default protobuf value of 0
|
||||
fetchedNode[0].loRaConfig?.hopLimit = 0
|
||||
|
||||
} else {
|
||||
// UNSET default protobuf value of 0
|
||||
fetchedNode[0].loRaConfig?.regionCode = Int32(config.lora.region.rawValue)
|
||||
// LongFast default protobuf value of 0
|
||||
fetchedNode[0].loRaConfig?.modemPreset = Int32(config.lora.modemPreset.rawValue)
|
||||
// 3 Hops default protobuf value of 0
|
||||
fetchedNode[0].loRaConfig?.hopLimit = Int32(config.lora.hopLimit)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated LoRaConfig for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data MyInfoEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModel v 3.xcdatamodel</string>
|
||||
<string>MeshtasticDataModel v 4.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?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="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="modemPreset" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="String"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
@ -104,6 +99,5 @@
|
|||
<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="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="74"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<?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="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="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>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<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="direction" attributeType="String"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<fetchedProperty name="tapbacks" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="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="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"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</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"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" 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="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="relativeHumidity" 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="voltage" 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="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="team" optional="YES" 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"/>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isEmoji == false"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<elements>
|
||||
<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="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"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -26,11 +26,9 @@ struct Config {
|
|||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Payload Variant
|
||||
var payloadVariant: Config.OneOf_PayloadVariant? = nil
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var device: Config.DeviceConfig {
|
||||
get {
|
||||
if case .device(let v)? = payloadVariant {return v}
|
||||
|
|
@ -39,8 +37,6 @@ struct Config {
|
|||
set {payloadVariant = .device(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var position: Config.PositionConfig {
|
||||
get {
|
||||
if case .position(let v)? = payloadVariant {return v}
|
||||
|
|
@ -49,8 +45,6 @@ struct Config {
|
|||
set {payloadVariant = .position(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var power: Config.PowerConfig {
|
||||
get {
|
||||
if case .power(let v)? = payloadVariant {return v}
|
||||
|
|
@ -59,8 +53,6 @@ struct Config {
|
|||
set {payloadVariant = .power(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var wifi: Config.WiFiConfig {
|
||||
get {
|
||||
if case .wifi(let v)? = payloadVariant {return v}
|
||||
|
|
@ -69,8 +61,6 @@ struct Config {
|
|||
set {payloadVariant = .wifi(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var display: Config.DisplayConfig {
|
||||
get {
|
||||
if case .display(let v)? = payloadVariant {return v}
|
||||
|
|
@ -79,8 +69,6 @@ struct Config {
|
|||
set {payloadVariant = .display(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var lora: Config.LoRaConfig {
|
||||
get {
|
||||
if case .lora(let v)? = payloadVariant {return v}
|
||||
|
|
@ -92,25 +80,13 @@ struct Config {
|
|||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Payload Variant
|
||||
enum OneOf_PayloadVariant: Equatable {
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case device(Config.DeviceConfig)
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case position(Config.PositionConfig)
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case power(Config.PowerConfig)
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case wifi(Config.WiFiConfig)
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case display(Config.DisplayConfig)
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case lora(Config.LoRaConfig)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
|
|
@ -166,7 +142,6 @@ struct Config {
|
|||
|
||||
///
|
||||
/// This setting is never saved to disk, but if set, all device settings will be returned to factory defaults.
|
||||
/// (Region, serial number etc... will be preserved)
|
||||
var factoryReset: Bool = false
|
||||
|
||||
///
|
||||
|
|
@ -182,7 +157,6 @@ struct Config {
|
|||
|
||||
///
|
||||
/// Defines the device's role on the Mesh network
|
||||
/// unset - 0
|
||||
enum Role: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
|
||||
|
|
@ -191,24 +165,19 @@ struct Config {
|
|||
case client // = 0
|
||||
|
||||
///
|
||||
/// ClientMute device role
|
||||
/// This is like the client but packets will not hop over this node. Would be
|
||||
/// useful if you want to save power by not contributing to the mesh.
|
||||
/// Client Mute device role
|
||||
/// Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh.
|
||||
case clientMute // = 1
|
||||
|
||||
///
|
||||
/// Router device role.
|
||||
/// Uses an agressive algirithem for the flood networking so packets will
|
||||
/// prefer to be routed over this node. Also assume that this will be
|
||||
/// unattended and so will turn off the wifi/ble radio as well as the oled screen.
|
||||
/// 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 router // = 2
|
||||
|
||||
///
|
||||
/// RouterClient device role
|
||||
/// Uses an agressive algirithem for the flood networking so packets will
|
||||
/// prefer to be routed over this node. Similiar power management as a regular
|
||||
/// client, so the RouterClient can be used as both a Router and a Client. Useful
|
||||
/// as a well placed base station that you could also use to send messages.
|
||||
/// Router Client device role
|
||||
/// 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.
|
||||
case routerClient // = 3
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
|
|
@ -331,6 +300,18 @@ struct Config {
|
|||
///
|
||||
/// Include positional timestamp (from GPS solution)
|
||||
case posTimestamp // = 128
|
||||
|
||||
///
|
||||
/// Include positional heading
|
||||
/// Intended for use with vehicle not walking speeds
|
||||
/// walking speeds are likely to be error prone like the compass
|
||||
case posHeading // = 256
|
||||
|
||||
///
|
||||
/// Include positional speed
|
||||
/// Intended for use with vehicle not walking speeds
|
||||
/// walking speeds are likely to be error prone like the compass
|
||||
case posSpeed // = 512
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
init() {
|
||||
|
|
@ -348,6 +329,8 @@ struct Config {
|
|||
case 32: self = .posSatinview
|
||||
case 64: self = .posSeqNos
|
||||
case 128: self = .posTimestamp
|
||||
case 256: self = .posHeading
|
||||
case 512: self = .posSpeed
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -363,6 +346,8 @@ struct Config {
|
|||
case .posSatinview: return 32
|
||||
case .posSeqNos: return 64
|
||||
case .posTimestamp: return 128
|
||||
case .posHeading: return 256
|
||||
case .posSpeed: return 512
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -389,14 +374,9 @@ struct Config {
|
|||
/// If set, we are powered from a low-current source (i.e. solar), so even if it looks like we have power flowing in
|
||||
/// we should try to minimize power consumption as much as possible.
|
||||
/// YOU DO NOT NEED TO SET THIS IF YOU'VE set is_router (it is implied in that case).
|
||||
/// CLI Only Option
|
||||
/// Advanced Option
|
||||
var isPowerSaving: Bool = false
|
||||
|
||||
///
|
||||
/// Circumvents the logic block for determining whether the device is powered or not.
|
||||
/// Useful for devices with finicky ADC issues on the battery sense pins.
|
||||
var isAlwaysPowered: Bool = false
|
||||
|
||||
///
|
||||
/// If non-zero, the device will fully power off this many seconds after external power is removed.
|
||||
var onBatteryShutdownAfterSecs: UInt32 = 0
|
||||
|
|
@ -404,11 +384,13 @@ struct Config {
|
|||
///
|
||||
/// Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k)
|
||||
/// Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation.
|
||||
/// Should be set to floating point value between 2 and 4
|
||||
/// Fixes issues on Heltec v2
|
||||
var adcMultiplierOverride: Float = 0
|
||||
|
||||
///
|
||||
/// Wait Bluetooth Seconds
|
||||
/// The number of seconds for to wait before turning of BLE in No Bluetooth states\
|
||||
/// The number of seconds for to wait before turning off BLE in No Bluetooth states
|
||||
/// 0 for default of 1 minute
|
||||
var waitBluetoothSecs: UInt32 = 0
|
||||
|
||||
|
|
@ -430,7 +412,7 @@ struct Config {
|
|||
/// Light Sleep Seconds
|
||||
/// In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on
|
||||
/// ESP32 Only
|
||||
/// 0 for default of 3600
|
||||
/// 0 for default of 300
|
||||
var lsSecs: UInt32 = 0
|
||||
|
||||
///
|
||||
|
|
@ -446,73 +428,22 @@ struct Config {
|
|||
/// **TBEAM 1.1 Only**
|
||||
enum ChargeCurrent: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case maunset // = 0
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma100 // = 1
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma190 // = 2
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma280 // = 3
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma360 // = 4
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma450 // = 5
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma550 // = 6
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma630 // = 7
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma700 // = 8
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma780 // = 9
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma880 // = 10
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma960 // = 11
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma1000 // = 12
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma1080 // = 13
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma1160 // = 14
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma1240 // = 15
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case ma1320 // = 16
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
|
|
@ -572,7 +503,7 @@ struct Config {
|
|||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// WiFi Config
|
||||
struct WiFiConfig {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
|
|
@ -609,13 +540,12 @@ struct Config {
|
|||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// Power management state machine option.
|
||||
/// See [power management](/docs/software/other/power) for details.
|
||||
/// 0 for default of one minute
|
||||
/// Number of seconds the screen stays on after pressing the user button or receiving a message
|
||||
/// 0 for default of one minute MAXUINT for always on
|
||||
var screenOnSecs: UInt32 = 0
|
||||
|
||||
///
|
||||
/// How the GPS coordinates are displayed on the OLED screen.
|
||||
/// How the GPS coordinates are formatted on the OLED screen.
|
||||
var gpsFormat: Config.DisplayConfig.GpsCoordinateFormat = .gpsFormatDec
|
||||
|
||||
///
|
||||
|
|
@ -641,24 +571,24 @@ struct Config {
|
|||
case gpsFormatDms // = 1
|
||||
|
||||
///
|
||||
/// GPS coordinates are displayed in Universal Transverse Mercator format:
|
||||
/// Universal Transverse Mercator format:
|
||||
/// ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
|
||||
case gpsFormatUtm // = 2
|
||||
|
||||
///
|
||||
/// GPS coordinates are displayed in Military Grid Reference System format:
|
||||
/// Military Grid Reference System format:
|
||||
/// ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
|
||||
/// E is easting, N is northing
|
||||
case gpsFormatMgrs // = 3
|
||||
|
||||
///
|
||||
/// GPS coordinates are displayed in Open Location Code (aka Plus Codes).
|
||||
/// Open Location Code (aka Plus Codes).
|
||||
case gpsFormatOlc // = 4
|
||||
|
||||
///
|
||||
/// GPS coordinates are displayed in Ordnance Survey Grid Reference (the National Grid System of the UK).
|
||||
/// Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, E is the easting,
|
||||
/// N is the northing
|
||||
/// Ordnance Survey Grid Reference (the National Grid System of the UK).
|
||||
/// Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
|
||||
/// E is the easting, N is the northing
|
||||
case gpsFormatOsgr // = 5
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
|
|
@ -696,7 +626,7 @@ struct Config {
|
|||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Lora Config
|
||||
struct LoRaConfig {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
|
|
@ -710,7 +640,6 @@ struct Config {
|
|||
var txPower: Int32 = 0
|
||||
|
||||
///
|
||||
/// Note: This is the 'old' mechanism for specifying channel parameters.
|
||||
/// Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH.
|
||||
/// As a heuristic: If bandwidth is specified, do not use modem_config.
|
||||
/// Because protobufs take ZERO space when the value is zero this works out nicely.
|
||||
|
|
@ -741,11 +670,12 @@ struct Config {
|
|||
var frequencyOffset: Float = 0
|
||||
|
||||
///
|
||||
/// The region code for my radio (US, CN, EU433, etc...)
|
||||
/// The region code for the radio (US, CN, EU433, etc...)
|
||||
var region: Config.LoRaConfig.RegionCode = .unset
|
||||
|
||||
///
|
||||
/// Overrides HOPS_RELIABLE and sets the maximum number of hops. This can't be greater than 7.
|
||||
/// 0 for default of 3
|
||||
var hopLimit: UInt32 = 0
|
||||
|
||||
///
|
||||
|
|
@ -761,66 +691,59 @@ struct Config {
|
|||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
/// The frequency/regulatory region the user has selected.
|
||||
/// Note: In 1.0 builds (which must still be supported by the android app for a
|
||||
/// long time) this field will be unpopulated.
|
||||
/// If firmware is ever upgraded from an old 1.0ish build, the old
|
||||
/// MyNodeInfo.region string will be used to set UserPreferences.region and the
|
||||
/// old value will be no longer set.
|
||||
enum RegionCode: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Region is not set
|
||||
case unset // = 0
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// United States
|
||||
case us // = 1
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// European Union 433mhz
|
||||
case eu433 // = 2
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// European Union 433mhz
|
||||
case eu868 // = 3
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// China
|
||||
case cn // = 4
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Japan
|
||||
case jp // = 5
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Australia / New Zealand
|
||||
case anz // = 6
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Korea
|
||||
case kr // = 7
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Taiwan
|
||||
case tw // = 8
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Russia
|
||||
case ru // = 9
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// India
|
||||
case `in` // = 10
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// New Zealand 865mhz
|
||||
case nz865 // = 11
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Thailand
|
||||
case th // = 12
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
|
|
@ -875,31 +798,31 @@ struct Config {
|
|||
typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Long Range - Fast
|
||||
case longFast // = 0
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Long Range - Slow
|
||||
case longSlow // = 1
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Very Long Range - Slow
|
||||
case vlongSlow // = 2
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Medium Range - Slow
|
||||
case midSlow // = 3
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Medium Range - Fast
|
||||
case midFast // = 4
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Short Range - Slow
|
||||
case shortSlow // = 5
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
/// Short Range - Fast
|
||||
case shortFast // = 6
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
|
|
@ -965,6 +888,8 @@ extension Config.PositionConfig.PositionFlags: CaseIterable {
|
|||
.posSatinview,
|
||||
.posSeqNos,
|
||||
.posTimestamp,
|
||||
.posHeading,
|
||||
.posSpeed,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1342,6 +1267,8 @@ extension Config.PositionConfig.PositionFlags: SwiftProtobuf._ProtoNameProviding
|
|||
32: .same(proto: "POS_SATINVIEW"),
|
||||
64: .same(proto: "POS_SEQ_NOS"),
|
||||
128: .same(proto: "POS_TIMESTAMP"),
|
||||
256: .same(proto: "POS_HEADING"),
|
||||
512: .same(proto: "POS_SPEED"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1350,7 +1277,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "charge_current"),
|
||||
2: .standard(proto: "is_power_saving"),
|
||||
3: .standard(proto: "is_always_powered"),
|
||||
4: .standard(proto: "on_battery_shutdown_after_secs"),
|
||||
6: .standard(proto: "adc_multiplier_override"),
|
||||
7: .standard(proto: "wait_bluetooth_secs"),
|
||||
|
|
@ -1368,7 +1294,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularEnumField(value: &self.chargeCurrent) }()
|
||||
case 2: try { try decoder.decodeSingularBoolField(value: &self.isPowerSaving) }()
|
||||
case 3: try { try decoder.decodeSingularBoolField(value: &self.isAlwaysPowered) }()
|
||||
case 4: try { try decoder.decodeSingularUInt32Field(value: &self.onBatteryShutdownAfterSecs) }()
|
||||
case 6: try { try decoder.decodeSingularFloatField(value: &self.adcMultiplierOverride) }()
|
||||
case 7: try { try decoder.decodeSingularUInt32Field(value: &self.waitBluetoothSecs) }()
|
||||
|
|
@ -1388,9 +1313,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if self.isPowerSaving != false {
|
||||
try visitor.visitSingularBoolField(value: self.isPowerSaving, fieldNumber: 2)
|
||||
}
|
||||
if self.isAlwaysPowered != false {
|
||||
try visitor.visitSingularBoolField(value: self.isAlwaysPowered, fieldNumber: 3)
|
||||
}
|
||||
if self.onBatteryShutdownAfterSecs != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.onBatteryShutdownAfterSecs, fieldNumber: 4)
|
||||
}
|
||||
|
|
@ -1418,7 +1340,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
static func ==(lhs: Config.PowerConfig, rhs: Config.PowerConfig) -> Bool {
|
||||
if lhs.chargeCurrent != rhs.chargeCurrent {return false}
|
||||
if lhs.isPowerSaving != rhs.isPowerSaving {return false}
|
||||
if lhs.isAlwaysPowered != rhs.isAlwaysPowered {return false}
|
||||
if lhs.onBatteryShutdownAfterSecs != rhs.onBatteryShutdownAfterSecs {return false}
|
||||
if lhs.adcMultiplierOverride != rhs.adcMultiplierOverride {return false}
|
||||
if lhs.waitBluetoothSecs != rhs.waitBluetoothSecs {return false}
|
||||
|
|
|
|||
|
|
@ -1810,11 +1810,11 @@ struct FromRadio {
|
|||
}
|
||||
|
||||
///
|
||||
/// Include the entire config (was: RadioConfig radio)
|
||||
var config: LocalConfig {
|
||||
/// Include a part of the config (was: RadioConfig radio)
|
||||
var config: Config {
|
||||
get {
|
||||
if case .config(let v)? = _storage._payloadVariant {return v}
|
||||
return LocalConfig()
|
||||
return Config()
|
||||
}
|
||||
set {_uniqueStorage()._payloadVariant = .config(newValue)}
|
||||
}
|
||||
|
|
@ -1872,8 +1872,8 @@ struct FromRadio {
|
|||
/// starts over with the first node in our DB
|
||||
case nodeInfo(NodeInfo)
|
||||
///
|
||||
/// Include the entire config (was: RadioConfig radio)
|
||||
case config(LocalConfig)
|
||||
/// Include a part of the config (was: RadioConfig radio)
|
||||
case config(Config)
|
||||
///
|
||||
/// Set to send debug console output over our protobuf stream
|
||||
case logRecord(LogRecord)
|
||||
|
|
@ -3356,7 +3356,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
}
|
||||
}()
|
||||
case 6: try {
|
||||
var v: LocalConfig?
|
||||
var v: Config?
|
||||
var hadOneofValue = false
|
||||
if let current = _storage._payloadVariant {
|
||||
hadOneofValue = true
|
||||
|
|
|
|||
|
|
@ -265,8 +265,8 @@ struct Connect: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: self.bleManager.isSwitchedOn,
|
||||
deviceConnected: self.bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.lastFourCode :
|
||||
"????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.shortName :
|
||||
"?????")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ struct ConnectedDevice: View {
|
|||
if bluetoothOn {
|
||||
if deviceConnected {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.circle.fill")
|
||||
.imageScale(.large)
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.green)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text(name!).font(.subheadline).foregroundColor(.gray)
|
||||
Text(name!).font(.callout).foregroundColor(.gray)
|
||||
} else {
|
||||
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@ struct UserMessageList: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ struct NodeDetail: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
}
|
||||
)
|
||||
.onAppear(perform: {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ struct NodeList: View {
|
|||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
||||
if nodes.count > 0 {
|
||||
selection = "0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ struct NodeMap: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode :
|
||||
"????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName :
|
||||
"?????")
|
||||
})
|
||||
.onAppear(perform: {
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ struct AppSettings: View {
|
|||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ enum DeviceRoles: Int, CaseIterable, Identifiable {
|
|||
case .client:
|
||||
return "Client (default)"
|
||||
case .clientMute:
|
||||
return "Client Mute - Packets will not hop over this node, does not contribute to routing packets for mesh."
|
||||
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."
|
||||
case .routerClient:
|
||||
|
|
@ -41,8 +41,10 @@ struct DeviceConfig: View {
|
|||
@State var serialEnabled = true
|
||||
@State var debugLogEnabled = false
|
||||
|
||||
@State private var isPresentingFactoryResetConfirm: Bool = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
|
||||
Form {
|
||||
|
|
@ -74,18 +76,42 @@ struct DeviceConfig: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
}
|
||||
.navigationTitle("Device Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
|
||||
Button("Factory Reset", role: .destructive) {
|
||||
|
||||
isPresentingFactoryResetConfirm = true
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.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")
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
||||
.navigationTitle("Device Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ struct DisplayConfig: View {
|
|||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ enum RegionCodes : Int, CaseIterable, Identifiable {
|
|||
case kr = 7
|
||||
case tw = 8
|
||||
case ru = 9
|
||||
//case in = 10
|
||||
case `in` = 10
|
||||
case nz865
|
||||
case th
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ enum RegionCodes : Int, CaseIterable, Identifiable {
|
|||
get {
|
||||
switch self {
|
||||
case .unset:
|
||||
return "UNSET - Please set a Region"
|
||||
return "Please set a region"
|
||||
case .us:
|
||||
return "United States"
|
||||
case .eu433:
|
||||
|
|
@ -47,13 +47,48 @@ enum RegionCodes : Int, CaseIterable, Identifiable {
|
|||
return "Taiwan"
|
||||
case .ru:
|
||||
return "Russia"
|
||||
case .in:
|
||||
return "India"
|
||||
case .nz865:
|
||||
return "New Zealand 865mhz"
|
||||
case .th:
|
||||
return "TH"
|
||||
return "Thailand"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func protoEnumValue() -> Config.LoRaConfig.RegionCode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return Config.LoRaConfig.RegionCode.unset
|
||||
case .us:
|
||||
return Config.LoRaConfig.RegionCode.us
|
||||
case .eu433:
|
||||
return Config.LoRaConfig.RegionCode.eu433
|
||||
case .eu868:
|
||||
return Config.LoRaConfig.RegionCode.eu868
|
||||
case .cn:
|
||||
return Config.LoRaConfig.RegionCode.cn
|
||||
case .jp:
|
||||
return Config.LoRaConfig.RegionCode.jp
|
||||
case .anz:
|
||||
return Config.LoRaConfig.RegionCode.anz
|
||||
case .kr:
|
||||
return Config.LoRaConfig.RegionCode.kr
|
||||
case .tw:
|
||||
return Config.LoRaConfig.RegionCode.tw
|
||||
case .ru:
|
||||
return Config.LoRaConfig.RegionCode.ru
|
||||
case .in:
|
||||
return Config.LoRaConfig.RegionCode.in
|
||||
case .nz865:
|
||||
return Config.LoRaConfig.RegionCode.nz865
|
||||
case .th:
|
||||
return Config.LoRaConfig.RegionCode.th
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ModemPresets : Int, CaseIterable, Identifiable {
|
||||
|
|
@ -72,19 +107,74 @@ enum ModemPresets : Int, CaseIterable, Identifiable {
|
|||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return "Long Fast"
|
||||
return "Long Range - Fast"
|
||||
case .LongSlow:
|
||||
return "Long Slow"
|
||||
return "Long Range - Slow"
|
||||
case .VLongSlow:
|
||||
return "Very Long Slow"
|
||||
return "Very Long Range - Slow"
|
||||
case .MidSlow:
|
||||
return "Mid Slow"
|
||||
return "Medium Range - Slow"
|
||||
case .MidFast:
|
||||
return "Mid Fast"
|
||||
return "Medium Range - Fast"
|
||||
case .ShortSlow:
|
||||
return "Short Slow"
|
||||
return "Short Range - Slow"
|
||||
case .ShortFast:
|
||||
return "Short Fast"
|
||||
return "Short Range - Fast"
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.LoRaConfig.ModemPreset {
|
||||
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return Config.LoRaConfig.ModemPreset.longFast
|
||||
case .LongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.longSlow
|
||||
case .VLongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.vlongSlow
|
||||
case .MidSlow:
|
||||
return Config.LoRaConfig.ModemPreset.midSlow
|
||||
case .MidFast:
|
||||
return Config.LoRaConfig.ModemPreset.midFast
|
||||
case .ShortSlow:
|
||||
return Config.LoRaConfig.ModemPreset.shortSlow
|
||||
case .ShortFast:
|
||||
return Config.LoRaConfig.ModemPreset.shortFast
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HopValues : Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneHop = 1
|
||||
case twoHops = 2
|
||||
case threeHops = 0
|
||||
case fourHops = 4
|
||||
case fiveHops = 5
|
||||
case sixHops = 6
|
||||
case sevenHops = 7
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .oneHop:
|
||||
return "One Hop"
|
||||
case .twoHops:
|
||||
return "Two Hops"
|
||||
case .threeHops:
|
||||
return "Three Hops"
|
||||
case .fourHops:
|
||||
return "Four Hops"
|
||||
case .fiveHops:
|
||||
return "Five Hops"
|
||||
case .sixHops:
|
||||
return "Six Hops"
|
||||
case .sevenHops:
|
||||
return "Seven Hops"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,9 +185,13 @@ struct LoRaConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@State var region = 1
|
||||
@State var modemPreset = 0
|
||||
@State var numberOfHops = 0
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var region = 0
|
||||
@State var modemPreset = 0
|
||||
@State var hopLimit = 0
|
||||
@State var hasChanges = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -112,49 +206,106 @@ struct LoRaConfig: View {
|
|||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("The region where you will be using your Meshtastic LoRa radios.")
|
||||
Text("The region where you will be using your radios.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
Section(header: Text("Modem")) {
|
||||
Picker("Presets", selection: $region ) {
|
||||
Picker("Presets", selection: $modemPreset ) {
|
||||
ForEach(ModemPresets.allCases) { m in
|
||||
Text(m.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Available modem presets.")
|
||||
Text("Available modem presets, default is Long Fast.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
Section(header: Text("Mesh Options")) {
|
||||
|
||||
Picker("Number of hops", selection: $numberOfHops) {
|
||||
ForEach(0..<8) {
|
||||
if $0 == 0 {
|
||||
Text("Default")
|
||||
} else {
|
||||
Text("\($0) Hops")
|
||||
}
|
||||
Picker("Number of hops", selection: $hopLimit) {
|
||||
ForEach(HopValues.allCases) { hop in
|
||||
Text(hop.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Sets the maximum number of hops, default is 3.")
|
||||
.font(.caption)
|
||||
.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 LoRa Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var lc = Config.LoRaConfig()
|
||||
lc.hopLimit = UInt32(hopLimit)
|
||||
lc.region = RegionCodes(rawValue: region)!.protoEnumValue()
|
||||
lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue()
|
||||
|
||||
if bleManager.saveLoRaConfig(config: lc, 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("LoRa Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
}
|
||||
.task {
|
||||
do {
|
||||
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)
|
||||
self.hasChanges = false
|
||||
} catch {
|
||||
print("Failed to load node data")
|
||||
}
|
||||
}
|
||||
.onChange(of: region) { newRegion in
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: modemPreset) { newModemPreset in
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: hopLimit) { newHopLimit in
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,8 +118,14 @@ struct PositionConfig: View {
|
|||
@State var gpsAttemptTime = 0
|
||||
@State var positionBroadcastSeconds = 0
|
||||
|
||||
@State var includeAltitude = false
|
||||
@State var includeSatInView = false
|
||||
@State var includePosAltitude = false
|
||||
@State var includePosSatsinview = false
|
||||
@State var includePosSeqNos = false
|
||||
@State var includePosTimestamp = false
|
||||
@State var includePosSpeed = false
|
||||
@State var includePosHeading = false
|
||||
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -203,30 +209,44 @@ struct PositionConfig: View {
|
|||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includeAltitude) {
|
||||
Toggle(isOn: $includePosAltitude) {
|
||||
|
||||
Label("Include Altitude", systemImage: "arrow.up")
|
||||
Label("Altitude", systemImage: "arrow.up")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includeSatInView) {
|
||||
Toggle(isOn: $includePosSatsinview) {
|
||||
|
||||
Label("Include number of satellites in view", systemImage: "skew")
|
||||
Label("Number of satellites", systemImage: "skew")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includeSatInView) { //64
|
||||
Toggle(isOn: $includePosSeqNos) { //64
|
||||
|
||||
Label("Include a sequence number incremented per packet", systemImage: "number")
|
||||
Label("Sequence number", systemImage: "number")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includeSatInView) { //128
|
||||
Toggle(isOn: $includePosTimestamp) { //128
|
||||
|
||||
Label("Include positional timestamp", systemImage: "clock")
|
||||
Label("Timestamp", systemImage: "clock")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosHeading) { //128
|
||||
|
||||
Label("Vehicle heading", systemImage: "location.circle")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Toggle(isOn: $includePosSpeed) { //128
|
||||
|
||||
Label("Vehicle speed", systemImage: "speedometer")
|
||||
}
|
||||
.toggleStyle(DefaultToggleStyle())
|
||||
.listRowSeparator(.visible)
|
||||
|
|
@ -239,7 +259,7 @@ struct PositionConfig: View {
|
|||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ struct RangeTestConfig: View {
|
|||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ struct Settings: View {
|
|||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
|
||||
animation: .default)
|
||||
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
|
|
@ -53,8 +61,12 @@ struct Settings: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Display (Device Screen)")
|
||||
}
|
||||
NavigationLink {
|
||||
LoRaConfig()
|
||||
|
||||
let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0
|
||||
|
||||
NavigationLink() {
|
||||
|
||||
LoRaConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
|
|
@ -62,6 +74,8 @@ struct Settings: View {
|
|||
|
||||
Text("LoRa")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
PositionConfig()
|
||||
} label: {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ struct ShareChannel: View {
|
|||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ struct TelemetryConfig: View {
|
|||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue