diff --git a/Meshtastic Apple.xcodeproj/project.pbxproj b/Meshtastic Apple.xcodeproj/project.pbxproj index 1a556d63..d2c2e1c9 100644 --- a/Meshtastic Apple.xcodeproj/project.pbxproj +++ b/Meshtastic Apple.xcodeproj/project.pbxproj @@ -34,6 +34,9 @@ DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; }; DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; }; DD539502276DAA6A00AD86B1 /* MapLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD539501276DAA6A00AD86B1 /* MapLocation.swift */; }; + DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */; }; + DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */; }; + DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; }; DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B85A728009258000ACD6B /* ShareChannel.swift */; }; DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; }; DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; }; @@ -117,6 +120,9 @@ DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = ""; }; DD539501276DAA6A00AD86B1 /* MapLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLocation.swift; sourceTree = ""; }; DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 4.xcdatamodel"; sourceTree = ""; }; + DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalNotificationConfig.swift; sourceTree = ""; }; + DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = ""; }; + DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = ""; }; DD6B85A728009258000ACD6B /* ShareChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannel.swift; sourceTree = ""; }; DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = ""; }; DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = ""; }; @@ -232,17 +238,36 @@ children = ( DD3501882852FC3B000FC853 /* Settings.swift */, DD4A911D2708C65400501B7E /* AppSettings.swift */, + DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, + DD8169FE272476C700F4AB02 /* LogDocument.swift */, DD6B85A728009258000ACD6B /* ShareChannel.swift */, + DD61937A2863876A00E59241 /* Config */, + ); + path = Settings; + sourceTree = ""; + }; + DD61937A2863876A00E59241 /* Config */ = { + isa = PBXGroup; + children = ( DD41582528582E9B009B0E59 /* DeviceConfig.swift */, DD8EBF42285058FA00426DCA /* DisplayConfig.swift */, DD2553562855B02500E55709 /* LoRaConfig.swift */, DD2553582855B52700E55709 /* PositionConfig.swift */, - DD41582928585C32009B0E59 /* RangeTestConfig.swift */, - DD415827285859C4009B0E59 /* TelemetryConfig.swift */, - DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, - DD8169FE272476C700F4AB02 /* LogDocument.swift */, + DD61937B2863877A00E59241 /* Module */, ); - path = Settings; + path = Config; + sourceTree = ""; + }; + DD61937B2863877A00E59241 /* Module */ = { + isa = PBXGroup; + children = ( + DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */, + DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */, + DD41582928585C32009B0E59 /* RangeTestConfig.swift */, + DD6193782863875F00E59241 /* SerialConfig.swift */, + DD415827285859C4009B0E59 /* TelemetryConfig.swift */, + ); + path = Module; sourceTree = ""; }; DD8EDE9226F97A2B00A5A10B /* Frameworks */ = { @@ -591,6 +616,7 @@ DDC4D568275499A500A4208E /* Persistence.swift in Sources */, DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */, DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */, + DD6193792863875F00E59241 /* SerialConfig.swift in Sources */, DDAF8C6926ED0D070058C060 /* deviceonly.pb.swift in Sources */, DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */, DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */, @@ -599,6 +625,7 @@ DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */, DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */, DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */, + DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */, DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */, DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */, DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */, @@ -621,6 +648,7 @@ DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */, DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */, + DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */, DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */, diff --git a/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents b/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents index c32d279b..db1356bd 100644 --- a/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents +++ b/MeshtasticApple/Meshtastic.xcdatamodeld/MeshtasticDataModel v 4.xcdatamodel/contents @@ -73,6 +73,7 @@ + @@ -99,6 +100,13 @@ + + + + + + + @@ -131,12 +139,13 @@ - + + \ No newline at end of file diff --git a/MeshtasticApple/Protobufs/config.pb.swift b/MeshtasticApple/Protobufs/config.pb.swift index b93e2a68..d37e9615 100644 --- a/MeshtasticApple/Protobufs/config.pb.swift +++ b/MeshtasticApple/Protobufs/config.pb.swift @@ -811,11 +811,11 @@ struct Config { /// /// Medium Range - Slow - case midSlow // = 3 + case medSlow // = 3 /// /// Medium Range - Fast - case midFast // = 4 + case medFast // = 4 /// /// Short Range - Slow @@ -835,8 +835,8 @@ struct Config { case 0: self = .longFast case 1: self = .longSlow case 2: self = .vlongSlow - case 3: self = .midSlow - case 4: self = .midFast + case 3: self = .medSlow + case 4: self = .medFast case 5: self = .shortSlow case 6: self = .shortFast default: self = .UNRECOGNIZED(rawValue) @@ -848,8 +848,8 @@ struct Config { case .longFast: return 0 case .longSlow: return 1 case .vlongSlow: return 2 - case .midSlow: return 3 - case .midFast: return 4 + case .medSlow: return 3 + case .medFast: return 4 case .shortSlow: return 5 case .shortFast: return 6 case .UNRECOGNIZED(let i): return i @@ -953,8 +953,8 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable { .longFast, .longSlow, .vlongSlow, - .midSlow, - .midFast, + .medSlow, + .medFast, .shortSlow, .shortFast, ] @@ -1588,8 +1588,8 @@ extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding { 0: .same(proto: "LongFast"), 1: .same(proto: "LongSlow"), 2: .same(proto: "VLongSlow"), - 3: .same(proto: "MidSlow"), - 4: .same(proto: "MidFast"), + 3: .same(proto: "MedSlow"), + 4: .same(proto: "MedFast"), 5: .same(proto: "ShortSlow"), 6: .same(proto: "ShortFast"), ] diff --git a/MeshtasticApple/Views/Nodes/NodeDetail.swift b/MeshtasticApple/Views/Nodes/NodeDetail.swift index 6369b514..3a1f7c39 100644 --- a/MeshtasticApple/Views/Nodes/NodeDetail.swift +++ b/MeshtasticApple/Views/Nodes/NodeDetail.swift @@ -304,6 +304,9 @@ struct NodeDetail: View { Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline) Text("Lat/Long:").font(.caption) Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))") + + + .foregroundColor(.gray) .font(.caption) diff --git a/MeshtasticApple/Views/Settings/DeviceConfig.swift b/MeshtasticApple/Views/Settings/Config/DeviceConfig.swift similarity index 99% rename from MeshtasticApple/Views/Settings/DeviceConfig.swift rename to MeshtasticApple/Views/Settings/Config/DeviceConfig.swift index 2e76f1ba..0ecf8732 100644 --- a/MeshtasticApple/Views/Settings/DeviceConfig.swift +++ b/MeshtasticApple/Views/Settings/Config/DeviceConfig.swift @@ -75,7 +75,7 @@ struct DeviceConfig: View { Text(dr.description) } } - .pickerStyle(InlinePickerStyle()) + .pickerStyle(DefaultPickerStyle()) .padding(.top, 10) .padding(.bottom, 10) } diff --git a/MeshtasticApple/Views/Settings/DisplayConfig.swift b/MeshtasticApple/Views/Settings/Config/DisplayConfig.swift similarity index 97% rename from MeshtasticApple/Views/Settings/DisplayConfig.swift rename to MeshtasticApple/Views/Settings/Config/DisplayConfig.swift index e7467bbf..7192dac0 100644 --- a/MeshtasticApple/Views/Settings/DisplayConfig.swift +++ b/MeshtasticApple/Views/Settings/Config/DisplayConfig.swift @@ -151,7 +151,7 @@ struct DisplayConfig: View { } .pickerStyle(DefaultPickerStyle()) - Text("The number of seconds the screen remains on after the user button is pressed or messages are received.") + Text("How long the screen remains on after the user button is pressed or messages are received.") .font(.caption) .listRowSeparator(.visible) @@ -175,7 +175,7 @@ struct DisplayConfig: View { } .pickerStyle(DefaultPickerStyle()) - Text("The format used to display GPS coordinates on the screen.") + Text("The format used to display GPS coordinates on the device screen.") .font(.caption) .listRowSeparator(.visible) } diff --git a/MeshtasticApple/Views/Settings/LoRaConfig.swift b/MeshtasticApple/Views/Settings/Config/LoRaConfig.swift similarity index 98% rename from MeshtasticApple/Views/Settings/LoRaConfig.swift rename to MeshtasticApple/Views/Settings/Config/LoRaConfig.swift index ac72e8d6..c1da8dfb 100644 --- a/MeshtasticApple/Views/Settings/LoRaConfig.swift +++ b/MeshtasticApple/Views/Settings/Config/LoRaConfig.swift @@ -134,9 +134,9 @@ enum ModemPresets : Int, CaseIterable, Identifiable { case .VLongSlow: return Config.LoRaConfig.ModemPreset.vlongSlow case .MidSlow: - return Config.LoRaConfig.ModemPreset.midSlow + return Config.LoRaConfig.ModemPreset.medSlow case .MidFast: - return Config.LoRaConfig.ModemPreset.midFast + return Config.LoRaConfig.ModemPreset.medFast case .ShortSlow: return Config.LoRaConfig.ModemPreset.shortSlow case .ShortFast: diff --git a/MeshtasticApple/Views/Settings/Config/Module/CannedMessagesConfig.swift b/MeshtasticApple/Views/Settings/Config/Module/CannedMessagesConfig.swift new file mode 100644 index 00000000..c3aea46d --- /dev/null +++ b/MeshtasticApple/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -0,0 +1,8 @@ +// +// CannedMessagesConfig.swift +// MeshtasticApple +// +// Created by Garth Vander Houwen on 6/22/22. +// + +import Foundation diff --git a/MeshtasticApple/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/MeshtasticApple/Views/Settings/Config/Module/ExternalNotificationConfig.swift new file mode 100644 index 00000000..538f46ef --- /dev/null +++ b/MeshtasticApple/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -0,0 +1,94 @@ +// +// External Notification Config.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 6/22/22. +// +import SwiftUI + +struct ExternalNotificationConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + var node: NodeInfoEntity + + @State var enabled = false + @State var outputMilliseconds = 0 + @State var output = 0 + @State var active = false + @State var alertMessage = false + @State var alertBell = false + + var body: some View { + + VStack { + + Form { + + Section(header: Text("Options")) { + + Toggle(isOn: $enabled) { + + Label("Enabled", systemImage: "megaphone") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $alertBell) { + + Label("Alert when receiving a bell", systemImage: "bell") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $alertMessage) { + + Label("Alert when receiving a message", systemImage: "message") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + } + + Section(header: Text("GPIO")) { + + Toggle(isOn: $active) { + + Label("Active", systemImage: "togglepower") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Specifies whether the external circuit is triggered when the device's GPIO is low or high.") + .font(.caption) + .listRowSeparator(.visible) + + Picker("GPIO to monitor", selection: $output) { + ForEach(0..<40) { + + if $0 == 0 { + + Text("Unset") + + } else { + + Text("Pin \($0)") + } + } + } + .pickerStyle(DefaultPickerStyle()) + Text("Specifies the GPIO that your external circuit is attached to on the device.") + .font(.caption) + .listRowSeparator(.visible) + } + } + .navigationTitle("External Notification 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()) + } + } +} diff --git a/MeshtasticApple/Views/Settings/RangeTestConfig.swift b/MeshtasticApple/Views/Settings/Config/Module/RangeTestConfig.swift similarity index 86% rename from MeshtasticApple/Views/Settings/RangeTestConfig.swift rename to MeshtasticApple/Views/Settings/Config/Module/RangeTestConfig.swift index ef86378a..03851530 100644 --- a/MeshtasticApple/Views/Settings/RangeTestConfig.swift +++ b/MeshtasticApple/Views/Settings/Config/Module/RangeTestConfig.swift @@ -27,28 +27,25 @@ struct RangeTestConfig: View { Label("Enabled", systemImage: "figure.walk") } - .toggleStyle(DefaultToggleStyle()) - .listRowSeparator(.visible) + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Toggle(isOn: $sender) { Label("Sender", systemImage: "paperplane") } - .toggleStyle(DefaultToggleStyle()) + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Text("This device will send out range test messages.") .font(.caption) - .listRowSeparator(.visible) Toggle(isOn: $save) { Label("Save", systemImage: "square.and.arrow.down.fill") } - .toggleStyle(DefaultToggleStyle()) + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Saves a CSV with the range test message details, only available on ESP32 devices with a web server.") .font(.caption) - .listRowSeparator(.visible) } - } .navigationTitle("Range Test Config") .navigationBarItems(trailing: diff --git a/MeshtasticApple/Views/Settings/Config/Module/SerialConfig.swift b/MeshtasticApple/Views/Settings/Config/Module/SerialConfig.swift new file mode 100644 index 00000000..ad855f57 --- /dev/null +++ b/MeshtasticApple/Views/Settings/Config/Module/SerialConfig.swift @@ -0,0 +1,226 @@ +// +// SerialConfig.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 6/22/22. +// +import SwiftUI + +enum SerialBaudRates: Int, CaseIterable, Identifiable { + + case baudDefault = 0 + case baud110 = 1 + case baud300 = 2 + case baud600 = 3 + case baud1200 = 4 + case baud2400 = 5 + case baud4800 = 6 + case baud9600 = 7 + case baud19200 = 8 + case baud38400 = 9 + case baud57600 = 10 + case baud115200 = 11 + case baud230400 = 12 + case baud460800 = 13 + case baud576000 = 14 + case baud921600 = 15 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + case .baudDefault: + return "Unset" + case .baud110: + return "110 Baud" + case .baud300: + return "300 Baud" + case .baud600: + return "600 Baud" + case .baud1200: + return "1200 Baud" + case .baud2400: + return "2400 Baud" + case .baud4800: + return "4800 Baud" + case .baud9600: + return "9600 Baud" + case .baud19200: + return "19200 Baud" + case .baud38400: + return "38400 Baud" + case .baud57600: + return "57600 Baud" + case .baud115200: + return "115200 Baud" + case .baud230400: + return "230400 Baud" + case .baud460800: + return "460800 Baud" + case .baud576000: + return "576000 Baud" + case .baud921600: + return "921600 Baud" + } + } + } +} + +enum SerialModeTypes: Int, CaseIterable, Identifiable { + + case modeDefault = 0 + case modeSimple = 1 + case modeProto = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .modeDefault: + return "Default" + case .modeSimple: + return "Simple" + case .modeProto: + return "Protobufs" + } + } + } +} + +enum SerialTimeoutIntervals: Int, CaseIterable, Identifiable { + + case unset = 0 + case fiveSeconds = 5 + case tenSeconds = 10 + case fifteenSeconds = 15 + case thirtySeconds = 30 + case oneMinute = 60 + case fiveMinutes = 300 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .unset: + return "Unset" + case .fiveSeconds: + return "Five Seconds" + case .tenSeconds: + return "Ten Seconds" + case .fifteenSeconds: + return "Fifteen Seconds" + case .thirtySeconds: + return "Thirty Seconds" + case .oneMinute: + return "One Minute" + case .fiveMinutes: + return "Five Minutes" + + } + } + } +} + +struct SerialConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @State var enabled = false + @State var echo = false + @State var rxd = 0 + @State var txd = 0 + @State var baudRate = 0 + @State var timeout = 0 + @State var mode = 0 + + var body: some View { + + VStack { + + Form { + + Section(header: Text("Options")) { + + Toggle(isOn: $enabled) { + + Label("Enabled", systemImage: "terminal") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $echo) { + + Label("Echo", systemImage: "repeat") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Picker("Baud Rate", selection: $baudRate ) { + ForEach(SerialBaudRates.allCases) { sbr in + Text(sbr.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Picker("Timeout", selection: $timeout ) { + ForEach(SerialTimeoutIntervals.allCases) { sti in + Text(sti.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Picker("Mode", selection: $mode ) { + ForEach(SerialModeTypes.allCases) { smt in + Text(smt.description) + } + } + .pickerStyle(DefaultPickerStyle()) + } + Section(header: Text("GPIO")) { + + Picker("Receive data (rxd) GPIO pin", selection: $rxd) { + ForEach(0..<40) { + + if $0 == 0 { + + Text("Unset") + + } else { + + Text("Pin \($0)") + } + } + } + .pickerStyle(DefaultPickerStyle()) + + Picker("Transmit data (txd) GPIO pin", selection: $txd) { + ForEach(0..<40) { + + if $0 == 0 { + + Text("Unset") + + } else { + + Text("Pin \($0)") + } + } + } + .pickerStyle(DefaultPickerStyle()) + } + } + .navigationTitle("Serial 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()) + } + } +} diff --git a/MeshtasticApple/Views/Settings/Config/Module/TelemetryConfig.swift b/MeshtasticApple/Views/Settings/Config/Module/TelemetryConfig.swift new file mode 100644 index 00000000..01cae42c --- /dev/null +++ b/MeshtasticApple/Views/Settings/Config/Module/TelemetryConfig.swift @@ -0,0 +1,311 @@ +// +// TelemetryConfig.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 6/13/22. +// +import SwiftUI + +enum SensorTypes: Int, CaseIterable, Identifiable { + + /// No external telemetry sensor explicitly set + case notSet = 0 + + /// Moderate accuracy temperature + case dht11 = 1 + + /// High accuracy temperature + case ds18B20 = 2 + + /// Moderate accuracy temperature and humidity + case dht12 = 3 + + /// Moderate accuracy temperature and humidity + case dht21 = 4 + + /// Moderate accuracy temperature and humidity + case dht22 = 5 + + /// High accuracy temperature, pressure, humidity + case bme280 = 6 + + /// High accuracy temperature, pressure, humidity, and air resistance + case bme680 = 7 + + /// Very high accuracy temperature + case mcp9808 = 8 + + /// Moderate accuracy temperature and humidity + case shtc3 = 9 + + /// Moderate accuracy current and voltage + case ina260 = 10 + + /// Moderate accuracy current and voltage + case ina219 = 11 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + case .notSet: + return "Not Set" + case .dht11: + return "DHT11 - Temperature" + case .ds18B20: + return "DS18B20 - Temperature" + case .dht12: + return "DHT12 - Temperature & humidity" + case .dht21: + return "DHT21 - Temperature & humidity" + case .dht22: + return "DHT22 - Temperature & humidity" + case .bme280: + return "BME280 - Temp, pressure & humidity" + case .bme680: + return "BME680 - Temp, pressure, humidity & air resistance" + case .mcp9808: + return "MCP9808 - Temperature" + case .shtc3: + return "SHTC3 - Temperature & humidity" + case .ina260: + return "INA260 - Current & voltage" + case .ina219: + return "INA219 - Current & voltage" + } + } + } +} + +// Default of 0 is off +enum ErrorRecoveryIntervals: Int, CaseIterable, Identifiable { + + case off = 0 + case fifteenSeconds = 15 + case thirtySeconds = 30 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 900 + case thirtyMinutes = 1800 + case oneHour = 3600 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .off: + return "Unset" + 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" + case .thirtyMinutes: + return "Thirty Minutes" + case .oneHour: + return "One Hour" + } + } + } +} + +enum UpdateIntervals: Int, CaseIterable, Identifiable { + + case fifteenSeconds = 15 + case thirtySeconds = 30 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 0 + case thirtyMinutes = 1800 + case oneHour = 3600 + case twoHours = 7200 + case threeHours = 10800 + case fourHours = 14400 + case fiveHours = 18000 + case sixHours = 21600 + case twelveHours = 43200 + case eighteenHours = 64800 + case twentyFourHours = 86400 + + 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" + case .thirtyMinutes: + return "Thirty Minutes" + case .oneHour: + return "One Hour" + case .twoHours: + return "Two Hours" + case .threeHours: + return "Three Hours" + case .fourHours: + return "Four Hours" + case .fiveHours: + return "Five Hours" + case .sixHours: + return "Six Hours" + case .twelveHours: + return "Twelve Hours" + case .eighteenHours: + return "Eighteen Hours" + case .twentyFourHours: + return "Twenty Four Hours" + } + } + } +} + +struct TelemetryConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @State var deviceUpdateInterval = 0 + @State var environmentUpdateInterval = 0 + + @State var environmentMeasurementEnabled = false + @State var environmentSensorType = 0 + @State var environmentScreenEnabled = false + @State var environmentDisplayFahrenheit = false + @State var environmentSensorPin = 0 + @State var environmentRecoveryInterval = 0 + @State var environmentReadErrorCountThreshold = 0 + + var body: some View { + + VStack { + + Form { + + Section(header: Text("Update Intervals")) { + + Picker("Device Metrics", selection: $deviceUpdateInterval ) { + ForEach(UpdateIntervals.allCases) { ui in + Text(ui.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("How often device metrics are sent out over the mesh. Default is 15 minutes.") + .font(.caption) + + Picker("Sensor Metrics", selection: $environmentUpdateInterval ) { + ForEach(UpdateIntervals.allCases) { ui in + Text(ui.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("How often sensor metrics are sent out over the mesh. Default is 15 minutes.") + .font(.caption) + } + + Section(header: Text("Sensor Options")) { + + Toggle(isOn: $environmentMeasurementEnabled) { + + Label("Enabled", systemImage: "chart.xyaxis.line") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Picker("Sensor", selection: $environmentSensorType ) { + ForEach(SensorTypes.allCases) { st in + Text(st.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Toggle(isOn: $environmentScreenEnabled) { + + Label("Show on device screen", systemImage: "display") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Toggle(isOn: $environmentDisplayFahrenheit) { + + Label("Display Fahrenheit", systemImage: "thermometer") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Picker("GPIO Pin for sensor readings", selection: $environmentSensorPin) { + ForEach(0..<40) { + + if $0 == 0 { + + Text("Unset") + + } else { + + Text("Pin \($0)") + } + } + } + .pickerStyle(DefaultPickerStyle()) + } + + Section(header: Text("Errors")) { + + Picker("Error Count Threshold", selection: $environmentReadErrorCountThreshold) { + ForEach(0..<101) { + + if $0 == 0 { + + Text("Unset") + + } else if $0 % 5 == 0 { + + Text("\($0)") + } + } + } + .pickerStyle(DefaultPickerStyle()) + Text("Sometimes sensor reads can fail. If this happens, we will retry a configurable number of attempts, each attempt will be delayed by the minimum required refresh rate for that sensor") + .font(.caption) + + Picker("Error Recovery Interval", selection: $environmentRecoveryInterval ) { + ForEach(ErrorRecoveryIntervals.allCases) { eri in + Text(eri.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("Sometimes we can end up with more failures than our error count threshold. In this case, we will stop trying to read from the sensor for a while. Wait this long until trying to read from the sensor again") + .font(.caption) + } + } + .navigationTitle("Telemetry 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()) + } + } +} diff --git a/MeshtasticApple/Views/Settings/PositionConfig.swift b/MeshtasticApple/Views/Settings/Config/PositionConfig.swift similarity index 100% rename from MeshtasticApple/Views/Settings/PositionConfig.swift rename to MeshtasticApple/Views/Settings/Config/PositionConfig.swift diff --git a/MeshtasticApple/Views/Settings/Settings.swift b/MeshtasticApple/Views/Settings/Settings.swift index 4c632a53..06562980 100644 --- a/MeshtasticApple/Views/Settings/Settings.swift +++ b/MeshtasticApple/Views/Settings/Settings.swift @@ -47,9 +47,10 @@ struct Settings: View { Section("Radio Configuration") { - Text("Radio config values will be be enabled when there is a connected node. Save buttons will enable when there is a connected node and config changes to save.") + 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 { DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) @@ -93,19 +94,33 @@ struct Settings: View { Text("Position") } .disabled(bleManager.connectedPeripheral == nil) + + Text("Default settings values are prefered whenever possible as they consume no bandwidth when sent over the mesh.") + .font(.caption2) + .fixedSize(horizontal: false, vertical: true) } - Section("Module Configuration") { + 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()) + ExternalNotificationConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity()) } label: { - Image(systemName: "list.bullet.rectangle.fill") + Image(systemName: "megaphone") .symbolRenderingMode(.hierarchical) - Text("Canned Messages") + Text("External Notification") } - .disabled(true) + NavigationLink { RangeTestConfig() } label: { @@ -115,7 +130,18 @@ struct Settings: View { Text("Range Test") } - .disabled(!(nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) || bleManager.connectedPeripheral == nil) + //.disabled(!(nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) || bleManager.connectedPeripheral == nil) + + NavigationLink { + SerialConfig() + } label: { + + Image(systemName: "terminal") + .symbolRenderingMode(.hierarchical) + + Text("Serial") + } + .disabled(false) NavigationLink { TelemetryConfig() @@ -126,11 +152,9 @@ struct Settings: View { Text("Telemetry (Sensors)") } - .disabled(true) + .disabled(false) } // Not Implemented: - // 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 diff --git a/MeshtasticApple/Views/Settings/TelemetryConfig.swift b/MeshtasticApple/Views/Settings/TelemetryConfig.swift deleted file mode 100644 index 78eb78a6..00000000 --- a/MeshtasticApple/Views/Settings/TelemetryConfig.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// TelemetryConfig.swift -// Meshtastic Apple -// -// Copyright (c) Garth Vander Houwen 6/13/22. -// -import SwiftUI - -struct TelemetryConfig: View { - - @Environment(\.managedObjectContext) var context - @EnvironmentObject var bleManager: BLEManager - - @State var isPowerSaving = false - @State var isAlwaysPowered = false - - var body: some View { - - VStack { - - Form { - - } - .navigationTitle("Telemetry 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()) - } - } -}