Scaffolding for new settings and config

This commit is contained in:
Garth Vander Houwen 2022-06-12 01:25:42 -07:00
parent 9e8d629e3b
commit 90e7ac6f75
12 changed files with 614 additions and 117 deletions

View file

@ -16,6 +16,8 @@
DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */; };
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; };
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; };
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553562855B02500E55709 /* LoRaConfig.swift */; };
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; };
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; };
DD3501892852FC3B000FC853 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3501882852FC3B000FC853 /* Settings.swift */; };
DD35018B2852FC79000FC853 /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD35018A2852FC79000FC853 /* UserSettings.swift */; };
@ -35,7 +37,7 @@
DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FE272476C700F4AB02 /* LogDocument.swift */; };
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AE626F6B38600ABCC23 /* Connect.swift */; };
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; };
DD8EBF43285058FA00426DCA /* DeviceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DeviceSettings.swift */; };
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; };
DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; };
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; };
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
@ -94,6 +96,8 @@
DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = storeforward.pb.swift; sourceTree = "<group>"; };
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
DD2553562855B02500E55709 /* LoRaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaConfig.swift; sourceTree = "<group>"; };
DD2553582855B52700E55709 /* PositionConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfig.swift; sourceTree = "<group>"; };
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = "<group>"; };
DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
DD35018A2852FC79000FC853 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = "<group>"; };
@ -113,7 +117,7 @@
DD8169FE272476C700F4AB02 /* LogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDocument.swift; sourceTree = "<group>"; };
DD836AE626F6B38600ABCC23 /* Connect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connect.swift; sourceTree = "<group>"; };
DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
DD8EBF42285058FA00426DCA /* DeviceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceSettings.swift; sourceTree = "<group>"; };
DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = "<group>"; };
DD90860A26F645B700DC5189 /* MeshtasticApple.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticApple.entitlements; sourceTree = "<group>"; };
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = "<group>"; };
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
@ -221,12 +225,14 @@
DD4A911C2708C57100501B7E /* Settings */ = {
isa = PBXGroup;
children = (
DD3501882852FC3B000FC853 /* Settings.swift */,
DD4A911D2708C65400501B7E /* AppSettings.swift */,
DD8EBF42285058FA00426DCA /* DeviceSettings.swift */,
DD6B85A728009258000ACD6B /* ShareChannel.swift */,
DD8EBF42285058FA00426DCA /* DisplayConfig.swift */,
DD2553562855B02500E55709 /* LoRaConfig.swift */,
DD2553582855B52700E55709 /* PositionConfig.swift */,
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */,
DD8169FE272476C700F4AB02 /* LogDocument.swift */,
DD6B85A728009258000ACD6B /* ShareChannel.swift */,
DD3501882852FC3B000FC853 /* Settings.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -234,7 +240,6 @@
DD8EDE9226F97A2B00A5A10B /* Frameworks */ = {
isa = PBXGroup;
children = (
DDCA31312826009C00207175 /* PassKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -593,12 +598,14 @@
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
DD8EBF43285058FA00426DCA /* DeviceSettings.swift in Sources */,
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */,
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */,
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */,
C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */,
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */,
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */,
C9483F6D2773017500998F6B /* MapView.swift in Sources */,
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */,

View file

@ -444,7 +444,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
case .routingApp:
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .adminApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Admin App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
adminAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .replyApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Reply App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .ipTunnelApp:
@ -799,13 +799,42 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func getConfig(destNum: Int64, wantResponse: Bool) -> Bool {
var newConfig = Config.LoRaConfig()
var channel = ChannelSettings()
// var newPrefs = (value.loraConfig).toBuilder()
// newConfig.
var adminPacket = AdminMessage()
adminPacket.getConfigRequest = AdminMessage.ConfigType.deviceConfig
adminPacket.variant = AdminMessage.OneOf_Variant.getConfigRequest(AdminMessage.ConfigType.loraConfig)
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = false
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
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
}
}

View file

@ -7,6 +7,8 @@
import Foundation
import CoreData
import SwiftUI
func myInfoPacket (myInfo: MyNodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> MyInfoEntity? {
@ -322,6 +324,49 @@ func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManage
}
}
func adminAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
if let deviceConfig = try? MeshtasticApple.Config.DeviceConfig(serializedData: packet.decoded.payload) {
print(try! deviceConfig.jsonString())
} else if let displayConfig = try? MeshtasticApple.Config.DisplayConfig(serializedData: packet.decoded.payload) {
print(try! displayConfig.jsonUTF8Data())
print(displayConfig.gpsFormat)
} else if let loraConfig = try? MeshtasticApple.Config.LoRaConfig(serializedData: packet.decoded.payload) {
print(try! loraConfig.jsonUTF8Data())
print(loraConfig.region)
} else if let positionConfig = try? MeshtasticApple.Config.PositionConfig(serializedData: packet.decoded.payload) {
print(try! positionConfig.jsonUTF8Data())
print(positionConfig.positionBroadcastSecs)
} else if let powerConfig = try? MeshtasticApple.Config.PowerConfig(serializedData: packet.decoded.payload) {
print(try! powerConfig.jsonUTF8Data())
print(powerConfig.meshSdsTimeoutSecs)
}
if meshLogging { MeshLogger.log(" MESH PACKET received for Admin App UNHANDLED \(try! packet.jsonString())") }
//PowerConfig
//WiFiConfig
//if let loraConfig = try? MeshtasticApple.Config.LoRaConfig(serializedData: packet.serializedData) {
// print(loraConfig)
//}
}
func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) {
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")

View file

@ -4,7 +4,7 @@
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:meshtastic.org</string>
<string>applinks:meshtastic.org/e/</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>

View file

@ -1,3 +1,5 @@
// Copyright (C) 2022 Garth Vander Houwen
import SwiftUI
import CoreData

View file

@ -1,5 +1,5 @@
/*
Abstract: Default App View
Copyright (c) Garth Vander Houwen 2021
*/
import SwiftUI
@ -36,7 +36,7 @@ struct ContentView: View {
// .tag(Tab.messages)
Connect()
.tabItem {
Label("Bluetooth", systemImage: "dot.radiowaves.left.and.right")
Label("Bluetooth", systemImage: "antenna.radiowaves.left.and.right")
.symbolRenderingMode(.hierarchical)
.symbolVariant(.none)
}
@ -55,7 +55,7 @@ struct ContentView: View {
.symbolVariant(.none)
}
.tag(Tab.map)
AppSettings()
Settings()
.tabItem {
Label("Settings", systemImage: "gear")
.symbolRenderingMode(.hierarchical)

View file

@ -91,110 +91,97 @@ struct AppSettings: View {
var body: some View {
NavigationView {
VStack {
GeometryReader { _ in
Form {
Section(header: Text("USER DETAILS")) {
Form {
Section(header: Text("USER DETAILS")) {
HStack {
Label("Name", systemImage: "person.crop.rectangle.fill")
TextField("Username", text: $userSettings.meshtasticUsername)
.foregroundColor(.gray)
}
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
.listRowSeparator(.visible)
HStack {
Label("Radio", systemImage: "flipphone")
Text(userSettings.preferredPeripheralName)
.foregroundColor(.gray)
}
Text("This option is set via the preferred radio toggle for the connected device on the bluetooth tab.")
.font(.caption)
.listRowSeparator(.hidden)
Text("The preferred radio will automatically reconnect if it becomes disconnected and is still within range.")
.font(.caption2)
HStack {
Label("Name", systemImage: "person.crop.rectangle.fill")
TextField("Username", text: $userSettings.meshtasticUsername)
.foregroundColor(.gray)
}
Section(header: Text("LOCATION OPTIONS")) {
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
.listRowSeparator(.visible)
HStack {
Label("Radio", systemImage: "flipphone")
Text(userSettings.preferredPeripheralName)
.foregroundColor(.gray)
Toggle(isOn: $userSettings.provideLocation) {
Label("Provide location to mesh", systemImage: "location.circle.fill")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if userSettings.provideLocation {
Picker(" Update Interval", selection: $userSettings.provideLocationInterval) {
ForEach(LocationUpdateInterval.allCases) { lu in
Text(lu.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("How frequently your phone will send your location to the device, location updates to the mesh are managed by the device.")
.font(.caption)
.listRowSeparator(.visible)
}
}
Section(header: Text("MESH OPTIONS")) {
NavigationLink(destination: ShareChannel()) {
Text("Share Your Channel vis QR Code")
}
}
Section(header: Text("MESSAGING OPTIONS")) {
Text("This option is set via the preferred radio toggle for the connected device on the bluetooth tab.")
.font(.caption)
.listRowSeparator(.hidden)
Text("The preferred radio will automatically reconnect if it becomes disconnected and is still within range.")
.font(.caption2)
.foregroundColor(.gray)
Picker("Keyboard Type", selection: $userSettings.keyboardType) {
ForEach(KeyboardType.allCases) { kb in
Text(kb.description)
}
Section(header: Text("OPTIONS")) {
Toggle(isOn: $userSettings.provideLocation) {
Label("Provide location to mesh", systemImage: "location.circle.fill")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if userSettings.provideLocation {
Picker(" Update Interval", selection: $userSettings.provideLocationInterval) {
ForEach(LocationUpdateInterval.allCases) { lu in
Text(lu.description)
}
}
.pickerStyle(DefaultPickerStyle())
}
Section(header: Text("MAP OPTIONS")) {
Picker("Map Type", selection: $userSettings.meshMapType) {
ForEach(MeshMapType.allCases) { map in
Text(map.description)
}
}
.pickerStyle(DefaultPickerStyle())
// TextField("Custom Tile Server", text: $userSettings.meshMapCustomTileServer)
}
Section(header: Text("DEBUG OPTIONS")) {
Toggle(isOn: $userSettings.meshActivityLog) {
Label("Log all Mesh activity", systemImage: "network")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if userSettings.meshActivityLog {
NavigationLink(destination: MeshLog()) {
Text("View Mesh Log")
}
Text("How frequently your phone will send your location to the device, location updates to the mesh are managed by the device.")
.font(.caption)
.listRowSeparator(.visible)
}
Picker("Keyboard Type", selection: $userSettings.keyboardType) {
ForEach(KeyboardType.allCases) { kb in
Text(kb.description)
}
}
.pickerStyle(DefaultPickerStyle())
Picker("Map Type", selection: $userSettings.meshMapType) {
ForEach(MeshMapType.allCases) { map in
Text(map.description)
}
}
.pickerStyle(DefaultPickerStyle())
}
Section(header: Text("DEBUG")) {
Toggle(isOn: $userSettings.meshActivityLog) {
Label("Log all Mesh activity", systemImage: "network")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if userSettings.meshActivityLog {
NavigationLink(destination: MeshLog()) {
Text("View Mesh Log")
}
.listRowSeparator(.visible)
}
}
}
.navigationTitle("App Settings")
.navigationBarItems(trailing:
}
.navigationTitle("App Settings")
.navigationBarItems(trailing:
ZStack {
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
})
.onAppear {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
})
.onAppear {
self.bleManager.context = context
}
}
self.bleManager.context = context
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -1,8 +0,0 @@
//
// DeviceSettings.swift
// MeshtasticApple
//
// Created by Garth Vander Houwen on 6/7/22.
//
import Foundation

View file

@ -0,0 +1,171 @@
//
// DeviceSettings.swift
// Meshtastic Apple
//
// Copyright (c) Garth Vander Houwen 6/7/22.
//
import SwiftUI
enum GpsFormat: Int, CaseIterable, Identifiable {
case gpsFormatDec = 0
case gpsFormatDms = 1
case gpsFormatUtm = 2
case gpsFormatMgrs = 3
case gpsFormatOlc = 4
case gpsFormatOsgr = 5
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .gpsFormatDec:
return "Decimal Degrees Format"
case .gpsFormatDms:
return "Degrees Minutes Seconds"
case .gpsFormatUtm:
return "Universal Transverse Mercator"
case .gpsFormatMgrs:
return "Military Grid Reference System"
case .gpsFormatOlc:
return "Open Location Code (aka Plus Codes)"
case .gpsFormatOsgr:
return "Ordnance Survey Grid Reference"
}
}
}
}
// Default of 0 is One Minute
enum ScreenOnSeconds: Int, CaseIterable, Identifiable {
case fifteenSeconds = 15
case thirtySeconds = 30
case oneMinute = 0
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .fifteenSeconds:
return "Fifteen Seconds"
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
}
}
}
}
// Default of 0 is off
enum ScreenCarouselSeconds: Int, CaseIterable, Identifiable {
case off = 0
case fifteenSeconds = 15
case thirtySeconds = 30
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .off:
return "Off"
case .fifteenSeconds:
return "Fifteen Seconds"
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
}
}
}
}
struct DisplayConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@State var isConnected: Bool = false
@State var gpsFormat: Config.DisplayConfig.GpsCoordinateFormat = .gpsFormatDec
var body: some View {
VStack {
Form {
Section(header: Text("Timing")) {
Picker("Screen on for", selection: $gpsFormat ) {
ForEach(ScreenOnSeconds.allCases) { lu in
Text(lu.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("The number of seconds the screen remains on after the user button is pressed or messages are received.")
.font(.caption)
.listRowSeparator(.visible)
Picker("Carousel Interval", selection: $gpsFormat ) {
ForEach(ScreenCarouselSeconds.allCases) { lu in
Text(lu.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.")
.font(.caption)
.listRowSeparator(.visible)
}
Section(header: Text("Format")) {
Picker("GPS Format", selection: $gpsFormat ) {
ForEach(GpsFormat.allCases) { lu in
Text(lu.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("The format used to display GPS coordinates on the screen.")
.font(.caption)
.listRowSeparator(.visible)
}
}
}
.navigationTitle("Display Config")
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
})
.onAppear {
self.bleManager.context = context
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -0,0 +1,150 @@
//
// LoRaConfig.swift
// Meshtastic Apple
//
// Copyright (c) by Garth Vander Houwen 6/11/22.
//
import SwiftUI
enum RegionCodes : Int, CaseIterable, Identifiable {
case unset = 0
case us = 1
case eu433 = 2
case eu868 = 3
case cn = 4
case jp = 5
case anz = 6
case kr = 7
case tw = 8
case ru = 9
//case in = 10
case nz865
case th
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .unset:
return "UNSET - Please set a Region"
case .us:
return "United States"
case .eu433:
return "European Union 433mhz"
case .eu868:
return "European Union 868mhz"
case .cn:
return "CN"
case .jp:
return "Japan"
case .anz:
return "Australia / New Zealand"
case .kr:
return "Korea"
case .tw:
return "Taiwan"
case .ru:
return "Russia"
case .nz865:
return "New Zealand 865mhz"
case .th:
return "TH"
}
}
}
}
enum ModemPresets : Int, CaseIterable, Identifiable {
case LongFast = 0
case LongSlow = 1
case VLongSlow = 2
case MidSlow = 3
case MidFast = 4
case ShortSlow = 5
case ShortFast = 6
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .LongFast:
return "Long Fast"
case .LongSlow:
return "Long Slow"
case .VLongSlow:
return "Very Long Slow"
case .MidSlow:
return "Mid Slow"
case .MidFast:
return "Mid Fast"
case .ShortSlow:
return "Short Slow"
case .ShortFast:
return "Short Fast"
}
}
}
}
struct LoRaConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@State var region: Config.LoRaConfig.RegionCode = .us
@State var modemPreset: Config.LoRaConfig.ModemPreset = .longFast
var body: some View {
VStack {
Form {
Section(header: Text("Region")) {
Picker("Region", selection: $region ) {
ForEach(RegionCodes.allCases) { r in
Text(r.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("The region where you will be using your Meshtastic LoRa radios.")
.font(.caption)
.listRowSeparator(.visible)
.listRowSeparator(.visible)
}
Section(header: Text("Modem")) {
Picker("Presets", selection: $region ) {
ForEach(ModemPresets.allCases) { m in
Text(m.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("Available modem presets.")
.font(.caption)
.listRowSeparator(.visible)
.listRowSeparator(.visible)
}
}
}
.navigationTitle("LoRa Config")
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????")
})
.onAppear {
self.bleManager.context = context
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -2,7 +2,123 @@
// Settings.swift
// MeshtasticApple
//
// Created by Garth Vander Houwen on 6/9/22.
// Copyright (c) Garth Vander Houwen 6/9/22.
//
import Foundation
import SwiftUI
struct Settings: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
var body: some View {
NavigationView {
List {
Section("General") {
NavigationLink {
AppSettings()
} label: {
Image(systemName: "gearshape")
.symbolRenderingMode(.hierarchical)
Text("App Settings")
}
NavigationLink {
ShareChannel()
} label: {
Image(systemName: "qrcode")
.symbolRenderingMode(.hierarchical)
Text("Share Channel QR Code")
}
}
Section("Radio Configuration") {
NavigationLink {
DisplayConfig()
} label: {
Image(systemName: "display")
.symbolRenderingMode(.hierarchical)
Text("Display (Device Screen)")
}
NavigationLink {
LoRaConfig()
} label: {
Image(systemName: "dot.radiowaves.left.and.right")
.symbolRenderingMode(.hierarchical)
Text("LoRa")
}
NavigationLink {
PositionConfig()
} label: {
Image(systemName: "location")
.symbolRenderingMode(.hierarchical)
Text("Position")
}
NavigationLink {
PositionConfig()
} label: {
Image(systemName: "bolt")
.symbolRenderingMode(.hierarchical)
Text("Power")
}
.disabled(true)
}
Section("Module Configuration") {
NavigationLink {
DisplayConfig()
} label: {
Image(systemName: "list.bullet.rectangle.fill")
.symbolRenderingMode(.hierarchical)
Text("Canned Messages")
}
.disabled(true)
NavigationLink {
DisplayConfig()
} label: {
Image(systemName: "point.3.connected.trianglepath.dotted")
.symbolRenderingMode(.hierarchical)
Text("Range Test")
}
.disabled(true)
NavigationLink {
DisplayConfig()
} label: {
Image(systemName: "chart.xyaxis.line")
.symbolRenderingMode(.hierarchical)
Text("Telemetry (Sensors)")
}
.disabled(true)
}
// Not Implemented:
// Device Config - No interesting settings for end users
// Power Config - All confusion, should delete most and have sensible defaults
// External Notifications - Not Working
// Serial Config - Not sure what the point is
// Store Forward Config - Not Working
// WiFi Config - Would break connection to device
// MQTT Config - Part of WiFi
}
.listStyle(GroupedListStyle())
.navigationTitle("Settings")
}
}
}

View file

@ -38,7 +38,7 @@ struct ShareChannel: View {
let channelSet = ChannelSet()
@State private var text = "https://meshtastic.org/e/#"
@State private var text = "https://meshtastic.org/e/#test"
var qrCodeImage = QrCodeImage()
var body: some View {
@ -52,11 +52,9 @@ struct ShareChannel: View {
ScrollView {
VStack {
Text("Scan the QR code below with the Apple or Android device you would like to share with your channel settings with.")
Text("Scan the QR code below with the Apple or Android device you would like to share your channel settings with.")
.fixedSize(horizontal: false, vertical: true)
.font(.callout)
.padding()
Spacer()
let image = qrCodeImage.generateQRCode(from: text)
Image(uiImage: image)