mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #105 from meshtastic/feature/user_settings
Feature/user settings
This commit is contained in:
commit
3d6e0c8ab7
15 changed files with 339 additions and 59 deletions
|
|
@ -70,6 +70,8 @@
|
|||
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC3B273283F411B00AC321C /* LastHeardText.swift */; };
|
||||
DDC4D568275499A500A4208E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4D567275499A500A4208E /* Persistence.swift */; };
|
||||
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; };
|
||||
DDCE4E2E286B7BC400BE9F8F /* StoreForwaredConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2D286B7BC400BE9F8F /* StoreForwaredConfig.swift */; };
|
||||
DDCE4E30286B7BD800BE9F8F /* InputBrokerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2F286B7BD800BE9F8F /* InputBrokerConfig.swift */; };
|
||||
DDCFF601285453A7005FA625 /* localonly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCFF600285453A7005FA625 /* localonly.pb.swift */; };
|
||||
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; };
|
||||
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; };
|
||||
|
|
@ -165,6 +167,8 @@
|
|||
DDC3B273283F411B00AC321C /* LastHeardText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastHeardText.swift; sourceTree = "<group>"; };
|
||||
DDC4D567275499A500A4208E /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserConfig.swift; sourceTree = "<group>"; };
|
||||
DDCE4E2D286B7BC400BE9F8F /* StoreForwaredConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForwaredConfig.swift; sourceTree = "<group>"; };
|
||||
DDCE4E2F286B7BD800BE9F8F /* InputBrokerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputBrokerConfig.swift; sourceTree = "<group>"; };
|
||||
DDCFF600285453A7005FA625 /* localonly.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = localonly.pb.swift; sourceTree = "<group>"; };
|
||||
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = "<group>"; };
|
||||
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -243,8 +247,8 @@
|
|||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */,
|
||||
DD8169FE272476C700F4AB02 /* LogDocument.swift */,
|
||||
DD6B85A728009258000ACD6B /* ShareChannel.swift */,
|
||||
DD61937A2863876A00E59241 /* Config */,
|
||||
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */,
|
||||
DD61937A2863876A00E59241 /* Config */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -269,6 +273,8 @@
|
|||
DD41582928585C32009B0E59 /* RangeTestConfig.swift */,
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */,
|
||||
DD415827285859C4009B0E59 /* TelemetryConfig.swift */,
|
||||
DDCE4E2D286B7BC400BE9F8F /* StoreForwaredConfig.swift */,
|
||||
DDCE4E2F286B7BD800BE9F8F /* InputBrokerConfig.swift */,
|
||||
);
|
||||
path = Module;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -636,6 +642,7 @@
|
|||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
DDCE4E30286B7BD800BE9F8F /* InputBrokerConfig.swift in Sources */,
|
||||
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
|
||||
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
|
||||
|
|
@ -665,6 +672,7 @@
|
|||
DDAF8C6726ED0C8C0058C060 /* remote_hardware.pb.swift in Sources */,
|
||||
DDAF8C6526ED0A490058C060 /* channel.pb.swift in Sources */,
|
||||
DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */,
|
||||
DDCE4E2E286B7BC400BE9F8F /* StoreForwaredConfig.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1037,4 +1037,109 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
public func getModuleConfig (configType: AdminMessage.ModuleConfigType, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.getModuleConfigRequest = configType
|
||||
|
||||
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 saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setModuleConfig.rangeTest = 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 saveSerialModuleConfig(config: ModuleConfig.SerialConfig, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setModuleConfig.serial = 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,19 +13,6 @@ 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.power.jsonString() == "{\"lsSecs\":300}") {
|
||||
//
|
||||
// print("📍 Default Power config")
|
||||
// print(try! config.power.jsonString())
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// print("📍 Has Power config")
|
||||
// print(try! config.power.jsonString())
|
||||
// }
|
||||
//
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) {
|
||||
|
||||
var isDefault = false
|
||||
|
|
@ -341,6 +328,11 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
}
|
||||
}
|
||||
|
||||
func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObject, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
func myInfoPacket (myInfo: MyNodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> MyInfoEntity? {
|
||||
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:meshtastic.org/e/</string>
|
||||
<string>applinks:meshtastic.org/*</string>
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
|
||||
|
|
@ -136,16 +136,16 @@
|
|||
</fetchedProperty>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
|
||||
<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="239"/>
|
||||
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
|
||||
<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"/>
|
||||
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -19,7 +19,22 @@ struct MeshtasticAppleApp: App {
|
|||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
.environmentObject(bleManager)
|
||||
.environmentObject(userSettings)
|
||||
.onOpenURL(perform: { (url) in
|
||||
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
|
||||
|
||||
print("Continue activity \(userActivity)")
|
||||
guard let url = userActivity.webpageURL else {
|
||||
return
|
||||
}
|
||||
|
||||
print("User wants to open URL: \(url)")
|
||||
// TODO same handling as done in onOpenURL()
|
||||
|
||||
}
|
||||
.onOpenURL(perform: { (url) in
|
||||
|
||||
print("URL OPENED")
|
||||
print(url)
|
||||
|
||||
//we are expecting a .mbtiles map file that contains raster data
|
||||
//save it to the documents directory, and name it offline_map.mbtiles
|
||||
let fileManager = FileManager.default
|
||||
|
|
@ -43,9 +58,7 @@ struct MeshtasticAppleApp: App {
|
|||
} else {
|
||||
print("💥 Didn't save the map file")
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
.onChange(of: scenePhase) { (newScenePhase) in
|
||||
switch newScenePhase {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ struct DisplayConfig: View {
|
|||
|
||||
Text("How long the screen remains on after the user button is pressed or messages are received.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Picker("Carousel Interval", selection: $screenCarouselInterval ) {
|
||||
ForEach(ScreenCarouselSeconds.allCases) { scs in
|
||||
|
|
|
|||
|
|
@ -297,9 +297,12 @@ struct LoRaConfig: View {
|
|||
}
|
||||
.onChange(of: region) { newRegion in
|
||||
|
||||
if newRegion != node.loRaConfig!.regionCode {
|
||||
if node.loRaConfig != nil {
|
||||
|
||||
hasChanges = true
|
||||
if newRegion != node.loRaConfig!.regionCode {
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: modemPreset) { newModemPreset in
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// InputBrokerConfig.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 6/28/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
|
@ -11,6 +11,12 @@ struct RangeTestConfig: 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 enabled = false
|
||||
@State var sender = false
|
||||
@State var save = false
|
||||
|
|
@ -46,7 +52,46 @@ struct RangeTestConfig: View {
|
|||
Text("Saves a CSV with the range test message details, only available on ESP32 devices with a web server.")
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 Range Test Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var rtc = ModuleConfig.RangeTestConfig()
|
||||
rtc.enabled = enabled
|
||||
rtc.save = save
|
||||
rtc.sender = sender ? 1 : 0
|
||||
|
||||
if bleManager.saveRangeTestModuleConfig(config: rtc, 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("Range Test Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
|
|
@ -56,7 +101,41 @@ struct RangeTestConfig: View {
|
|||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
// self.enabled = node.rangeTestConfig?.enabled ?? false
|
||||
// self.save = node.rangeTestConfig?.save ?? false
|
||||
//
|
||||
// if node.rangeTestConfig?.sender != nil {
|
||||
//
|
||||
// self.sender = node.rangeTestConfig!.sender == 1 ? true : false
|
||||
//
|
||||
// } else {
|
||||
// self.sender = false
|
||||
// }
|
||||
// self.sender = node.rangeTestConfig?.sender != nil
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
//if newEnabled != node.rangeTestConfig!.enabled {
|
||||
|
||||
hasChanges = true
|
||||
//}
|
||||
}
|
||||
.onChange(of: save) { newSave in
|
||||
|
||||
//if newSave != node.rangeTestConfig!.save {
|
||||
|
||||
hasChanges = true
|
||||
//}
|
||||
}
|
||||
.onChange(of: sender) { newSender in
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ struct SerialConfig: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges = false
|
||||
|
||||
@State var enabled = false
|
||||
@State var echo = false
|
||||
@State var rxd = 0
|
||||
|
|
@ -154,6 +158,8 @@ struct SerialConfig: View {
|
|||
Label("Echo", systemImage: "repeat")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("If set, any packets you send will be echoed back to your device.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("Baud Rate", selection: $baudRate ) {
|
||||
ForEach(SerialBaudRates.allCases) { sbr in
|
||||
|
|
@ -168,6 +174,8 @@ struct SerialConfig: View {
|
|||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("The amount of time to wait before we consider your packet as done.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("Mode", selection: $mode ) {
|
||||
ForEach(SerialModeTypes.allCases) { smt in
|
||||
|
|
@ -207,8 +215,48 @@ struct SerialConfig: View {
|
|||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Set the GPIO pins for RXD and TXD.")
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
||||
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 Range Test Module Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var sc = ModuleConfig.SerialConfig()
|
||||
sc.enabled = enabled
|
||||
//sc.save = save
|
||||
//sc.sender = sender ? 1 : 0
|
||||
|
||||
if bleManager.saveSerialModuleConfig(config: sc, 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("Serial Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
//
|
||||
// StoreForwaredConfig.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 6/28/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
|
@ -192,20 +192,16 @@ struct PositionConfig: View {
|
|||
Label("Smart Position Broadcast", systemImage: "location.fill.viewfinder")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
if !smartPositionEnabled {
|
||||
|
||||
Picker("Position Broadcast Interval", selection: $positionBroadcastSeconds) {
|
||||
ForEach(PositionBroadcastIntervals.allCases) { at in
|
||||
Text(at.description)
|
||||
}
|
||||
Picker("Position Broadcast Interval", selection: $positionBroadcastSeconds) {
|
||||
ForEach(PositionBroadcastIntervals.allCases) { at in
|
||||
Text(at.description)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
Text("We should send our position this often (but only if it has changed significantly)")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
Text("We should send our position this often (but only if it has changed significantly)")
|
||||
.font(.caption)
|
||||
}
|
||||
Section(header: Text("Position Flags - Non Functional")) {
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ struct Settings: View {
|
|||
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
|
||||
animation: .default)
|
||||
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
|
@ -34,6 +34,14 @@ struct Settings: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
Text("App Settings")
|
||||
}
|
||||
Text("Apple app specific settings and features, app username, share position with mesh options, map and keyboard type, mesh log.")
|
||||
.font(.caption)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
}
|
||||
|
||||
Section("Radio Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
ShareChannel()
|
||||
} label: {
|
||||
|
|
@ -41,13 +49,9 @@ struct Settings: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Share Channel QR Code")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Radio Configuration") {
|
||||
|
||||
Text("Radio config views will be be enabled when there is a connected node. Save buttons will be enabled when there are config changes to save.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
NavigationLink {
|
||||
|
|
@ -110,15 +114,16 @@ struct Settings: View {
|
|||
}
|
||||
Section("Module Configuration - Non Functional interaction preview.") {
|
||||
|
||||
// NavigationLink {
|
||||
// PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
// } label: {
|
||||
//
|
||||
// Image(systemName: "list.bullet.rectangle.fill")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
//
|
||||
// Text("Canned Messages")
|
||||
// }
|
||||
NavigationLink {
|
||||
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "list.bullet.rectangle.fill")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
Text("Canned Messages")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
ExternalNotificationConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
|
|
@ -131,15 +136,17 @@ struct Settings: View {
|
|||
}
|
||||
|
||||
NavigationLink {
|
||||
RangeTestConfig()
|
||||
RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
|
||||
Image(systemName: "point.3.connected.trianglepath.dotted")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
Text("Range Test")
|
||||
Text("Range Test (ESP32 Only)")
|
||||
}
|
||||
//.disabled(!(nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) || bleManager.connectedPeripheral == nil)
|
||||
.disabled(!(bleManager.connectedPeripheral == nil))
|
||||
//nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) ||
|
||||
// nodes.first(where: { $0.num == connectedNodeNum })!.rangeTestConfig != nil)
|
||||
|
||||
NavigationLink {
|
||||
SerialConfig()
|
||||
|
|
@ -170,6 +177,18 @@ struct Settings: View {
|
|||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.navigationTitle("Settings")
|
||||
.onAppear {
|
||||
|
||||
//
|
||||
|
||||
// let connectedNode =
|
||||
|
||||
// if nodes.first(where: { $0.num == (bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0) })!.rangeTestConfig == nil {
|
||||
//
|
||||
// // We have no Range Test Config, go get it
|
||||
// self.bleManager.getModuleConfig(configType: AdminMessage.ModuleConfigType.rangetestConfig, destNum: bleManager.connectedPeripheral.num, wantResponse: tr)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,16 +68,18 @@ struct UserConfig: View {
|
|||
}
|
||||
.keyboardType(.default)
|
||||
.disableAutocorrection(true)
|
||||
.listRowSeparator(.visible)
|
||||
Text("Long name can me 40 bytes long.")
|
||||
.font(.caption)
|
||||
|
||||
HStack {
|
||||
Label("Short Name", systemImage: "circlebadge.fill")
|
||||
TextField("Long Name", text: $shortName)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
.keyboardType(.asciiCapable)
|
||||
.disableAutocorrection(true)
|
||||
.listRowSeparator(.visible)
|
||||
Text("The short name is used in maps and messaging and will be appended to the last 4 of the device MAC address to set the device's BLE Name. It can be up to 5 bytes long.")
|
||||
.font(.caption)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue