From 661f349e9c13bf6fd1ccd0a0d6e3c3cfc94988b4 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 1 Aug 2022 07:11:03 -0700 Subject: [PATCH 01/36] Telemetry cleanup --- Meshtastic.xcodeproj/project.pbxproj | 4 +- Meshtastic/Export/WriteCsvFile.swift | 25 ++- Meshtastic/Helpers/MeshPackets.swift | 15 -- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 210 ++++++++++++++++++ Meshtastic/Protobufs/module_config.pb.swift | 44 ---- Meshtastic/Protobufs/telemetry.pb.swift | 96 +++----- Meshtastic/Views/Nodes/TelemetryLog.swift | 36 +-- .../Config/Module/TelemetryConfig.swift | 141 +----------- 9 files changed, 278 insertions(+), 295 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index be06040b..f8a5d04a 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCsvFile.swift; sourceTree = ""; }; DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = ""; }; DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = ""; }; + DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 5.xcdatamodel"; sourceTree = ""; }; DD90860A26F645B700DC5189 /* Meshtastic.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meshtastic.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -1080,12 +1081,13 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */, DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */, DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */, DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */; + currentVersion = DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Export/WriteCsvFile.swift b/Meshtastic/Export/WriteCsvFile.swift index 09c679b5..4e1f2b49 100644 --- a/Meshtastic/Export/WriteCsvFile.swift +++ b/Meshtastic/Export/WriteCsvFile.swift @@ -35,8 +35,29 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin } } else { - // Create Device Telemetry Header - csvString = "Battery Level, Voltage, Channel Utilization, Airtime, Timestamp" + // Create Environment Telemetry Header + csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, Voltage, Current" + + for dm in telemetry{ + + if dm.metricsType == 0 { + + csvString += "\n" + csvString += String("\(dm.temperature)°") + csvString += ", " + csvString += String(dm.relativeHumidity) + csvString += ", " + csvString += String(dm.barometricPressure) + csvString += ", " + csvString += String(dm.gasResistance) + csvString += ", " + csvString += String(dm.voltage) + csvString += ", " + csvString += String(dm.current) + csvString += ", " + csvString += dm.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? "Unknown Age" + } + } } return csvString diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 3a7670b7..29bfea89 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -723,25 +723,17 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newTelemetryConfig.deviceUpdateInterval = 0 newTelemetryConfig.environmentUpdateInterval = 0 newTelemetryConfig.environmentMeasurementEnabled = false - newTelemetryConfig.environmentSensorType = 0 newTelemetryConfig.environmentScreenEnabled = false newTelemetryConfig.environmentDisplayFahrenheit = false - newTelemetryConfig.environmentRecoveryInterval = 0 - newTelemetryConfig.environmentReadErrorCountThreshold = 0 } else { newTelemetryConfig.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) newTelemetryConfig.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) newTelemetryConfig.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled - newTelemetryConfig.environmentSensorType = Int32(config.telemetry.environmentSensorType.rawValue) newTelemetryConfig.environmentScreenEnabled = config.telemetry.environmentScreenEnabled newTelemetryConfig.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit - newTelemetryConfig.environmentRecoveryInterval = Int32(config.telemetry.environmentRecoveryInterval) - newTelemetryConfig.environmentReadErrorCountThreshold = Int32(config.telemetry.environmentReadErrorCountThreshold) - } - fetchedNode[0].telemetryConfig = newTelemetryConfig } else { @@ -751,23 +743,16 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj fetchedNode[0].telemetryConfig?.deviceUpdateInterval = 0 fetchedNode[0].telemetryConfig?.environmentUpdateInterval = 0 fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = false - fetchedNode[0].telemetryConfig?.environmentSensorType = 0 fetchedNode[0].telemetryConfig?.environmentScreenEnabled = false fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = false - fetchedNode[0].telemetryConfig?.environmentRecoveryInterval = 0 - fetchedNode[0].telemetryConfig?.environmentReadErrorCountThreshold = 0 - } else { fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval) fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval) fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled - fetchedNode[0].telemetryConfig?.environmentSensorType = Int32(config.telemetry.environmentSensorType.rawValue) fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.telemetry.environmentScreenEnabled fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit - fetchedNode[0].telemetryConfig?.environmentRecoveryInterval = Int32(config.telemetry.environmentRecoveryInterval) - fetchedNode[0].telemetryConfig?.environmentReadErrorCountThreshold = Int32(config.telemetry.environmentReadErrorCountThreshold) } } diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 5d3a865e..5664b468 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 4.xcdatamodel + MeshtasticDataModel v 5.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents new file mode 100644 index 00000000..cc2670e5 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Protobufs/module_config.pb.swift b/Meshtastic/Protobufs/module_config.pb.swift index 58459b06..63b87be2 100644 --- a/Meshtastic/Protobufs/module_config.pb.swift +++ b/Meshtastic/Protobufs/module_config.pb.swift @@ -473,31 +473,11 @@ struct ModuleConfig { /// Enable/Disable the telemetry measurement module on-device display var environmentScreenEnabled: Bool = false - /// - /// 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 - var environmentReadErrorCountThreshold: UInt32 = 0 - - /// - /// Sometimes we can end up with more than read_error_count_threshold failures. - /// 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 - var environmentRecoveryInterval: UInt32 = 0 - /// /// We'll always read the sensor in Celsius, but sometimes we might want to /// display the results in Fahrenheit as a "user preference". var environmentDisplayFahrenheit: Bool = false - /// - /// Specify the sensor type - var environmentSensorType: TelemetrySensorType = .notSet - - /// - /// Specify the peferred GPIO Pin for sensor readings - var environmentSensorPin: UInt32 = 0 - var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1184,11 +1164,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me 2: .standard(proto: "environment_update_interval"), 3: .standard(proto: "environment_measurement_enabled"), 4: .standard(proto: "environment_screen_enabled"), - 5: .standard(proto: "environment_read_error_count_threshold"), - 6: .standard(proto: "environment_recovery_interval"), 7: .standard(proto: "environment_display_fahrenheit"), - 8: .standard(proto: "environment_sensor_type"), - 9: .standard(proto: "environment_sensor_pin"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1201,11 +1177,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me case 2: try { try decoder.decodeSingularUInt32Field(value: &self.environmentUpdateInterval) }() case 3: try { try decoder.decodeSingularBoolField(value: &self.environmentMeasurementEnabled) }() case 4: try { try decoder.decodeSingularBoolField(value: &self.environmentScreenEnabled) }() - case 5: try { try decoder.decodeSingularUInt32Field(value: &self.environmentReadErrorCountThreshold) }() - case 6: try { try decoder.decodeSingularUInt32Field(value: &self.environmentRecoveryInterval) }() case 7: try { try decoder.decodeSingularBoolField(value: &self.environmentDisplayFahrenheit) }() - case 8: try { try decoder.decodeSingularEnumField(value: &self.environmentSensorType) }() - case 9: try { try decoder.decodeSingularUInt32Field(value: &self.environmentSensorPin) }() default: break } } @@ -1224,21 +1196,9 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me if self.environmentScreenEnabled != false { try visitor.visitSingularBoolField(value: self.environmentScreenEnabled, fieldNumber: 4) } - if self.environmentReadErrorCountThreshold != 0 { - try visitor.visitSingularUInt32Field(value: self.environmentReadErrorCountThreshold, fieldNumber: 5) - } - if self.environmentRecoveryInterval != 0 { - try visitor.visitSingularUInt32Field(value: self.environmentRecoveryInterval, fieldNumber: 6) - } if self.environmentDisplayFahrenheit != false { try visitor.visitSingularBoolField(value: self.environmentDisplayFahrenheit, fieldNumber: 7) } - if self.environmentSensorType != .notSet { - try visitor.visitSingularEnumField(value: self.environmentSensorType, fieldNumber: 8) - } - if self.environmentSensorPin != 0 { - try visitor.visitSingularUInt32Field(value: self.environmentSensorPin, fieldNumber: 9) - } try unknownFields.traverse(visitor: &visitor) } @@ -1247,11 +1207,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me if lhs.environmentUpdateInterval != rhs.environmentUpdateInterval {return false} if lhs.environmentMeasurementEnabled != rhs.environmentMeasurementEnabled {return false} if lhs.environmentScreenEnabled != rhs.environmentScreenEnabled {return false} - if lhs.environmentReadErrorCountThreshold != rhs.environmentReadErrorCountThreshold {return false} - if lhs.environmentRecoveryInterval != rhs.environmentRecoveryInterval {return false} if lhs.environmentDisplayFahrenheit != rhs.environmentDisplayFahrenheit {return false} - if lhs.environmentSensorType != rhs.environmentSensorType {return false} - if lhs.environmentSensorPin != rhs.environmentSensorPin {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/telemetry.pb.swift b/Meshtastic/Protobufs/telemetry.pb.swift index 0188c1c2..c523086c 100644 --- a/Meshtastic/Protobufs/telemetry.pb.swift +++ b/Meshtastic/Protobufs/telemetry.pb.swift @@ -29,49 +29,29 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { /// 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 + case bme280 // = 1 /// /// High accuracy temperature, pressure, humidity, and air resistance - case bme680 // = 7 + case bme680 // = 2 /// /// Very high accuracy temperature - case mcp9808 // = 8 - - /// - /// Moderate accuracy temperature and humidity - case shtc3 // = 9 + case mcp9808 // = 3 /// /// Moderate accuracy current and voltage - case ina260 // = 10 + case ina260 // = 4 /// /// Moderate accuracy current and voltage - case ina219 // = 11 + case ina219 // = 5 + + /// + /// High accuracy temperature and pressure + case bmp280 // = 6 case UNRECOGNIZED(Int) init() { @@ -81,17 +61,12 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { init?(rawValue: Int) { switch rawValue { case 0: self = .notSet - case 1: self = .dht11 - case 2: self = .ds18B20 - case 3: self = .dht12 - case 4: self = .dht21 - case 5: self = .dht22 - case 6: self = .bme280 - case 7: self = .bme680 - case 8: self = .mcp9808 - case 9: self = .shtc3 - case 10: self = .ina260 - case 11: self = .ina219 + case 1: self = .bme280 + case 2: self = .bme680 + case 3: self = .mcp9808 + case 4: self = .ina260 + case 5: self = .ina219 + case 6: self = .bmp280 default: self = .UNRECOGNIZED(rawValue) } } @@ -99,17 +74,12 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { var rawValue: Int { switch self { case .notSet: return 0 - case .dht11: return 1 - case .ds18B20: return 2 - case .dht12: return 3 - case .dht21: return 4 - case .dht22: return 5 - case .bme280: return 6 - case .bme680: return 7 - case .mcp9808: return 8 - case .shtc3: return 9 - case .ina260: return 10 - case .ina219: return 11 + case .bme280: return 1 + case .bme680: return 2 + case .mcp9808: return 3 + case .ina260: return 4 + case .ina219: return 5 + case .bmp280: return 6 case .UNRECOGNIZED(let i): return i } } @@ -122,17 +92,12 @@ extension TelemetrySensorType: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [TelemetrySensorType] = [ .notSet, - .dht11, - .ds18B20, - .dht12, - .dht21, - .dht22, .bme280, .bme680, .mcp9808, - .shtc3, .ina260, .ina219, + .bmp280, ] } @@ -285,17 +250,12 @@ extension Telemetry.OneOf_Variant: @unchecked Sendable {} extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 0: .same(proto: "NotSet"), - 1: .same(proto: "DHT11"), - 2: .same(proto: "DS18B20"), - 3: .same(proto: "DHT12"), - 4: .same(proto: "DHT21"), - 5: .same(proto: "DHT22"), - 6: .same(proto: "BME280"), - 7: .same(proto: "BME680"), - 8: .same(proto: "MCP9808"), - 9: .same(proto: "SHTC3"), - 10: .same(proto: "INA260"), - 11: .same(proto: "INA219"), + 1: .same(proto: "BME280"), + 2: .same(proto: "BME680"), + 3: .same(proto: "MCP9808"), + 4: .same(proto: "INA260"), + 5: .same(proto: "INA219"), + 6: .same(proto: "BMP280"), ] } diff --git a/Meshtastic/Views/Nodes/TelemetryLog.swift b/Meshtastic/Views/Nodes/TelemetryLog.swift index 0c7cfdf1..b6e5fd05 100644 --- a/Meshtastic/Views/Nodes/TelemetryLog.swift +++ b/Meshtastic/Views/Nodes/TelemetryLog.swift @@ -86,14 +86,10 @@ struct TelemetryLog: View { Text("Environment Metrics") .font(.title) - let sensor = SensorTypes(rawValue: Int(node.telemetryConfig?.environmentSensorType ?? 0)) let tempReadingType = (!(node.telemetryConfig?.environmentDisplayFahrenheit ?? true)) ? "°C" : "°F" - if sensor == SensorTypes.bme280 || - sensor == SensorTypes.bme680 || - sensor == SensorTypes.shtc3 || - sensor == SensorTypes.mcp9808 { + if tel.temperature > 0 { Image(systemName: "thermometer") .font(.callout) @@ -104,9 +100,7 @@ struct TelemetryLog: View { .font(.callout) } - if sensor == SensorTypes.bme280 || - sensor == SensorTypes.bme680 || - sensor == SensorTypes.shtc3 { + if tel.relativeHumidity > 0 { Image(systemName: "humidity") .font(.callout) @@ -117,8 +111,7 @@ struct TelemetryLog: View { .font(.callout) } - if sensor == SensorTypes.bme280 || - sensor == SensorTypes.bme680 { + if tel.barometricPressure > 0 { Image(systemName: "barometer") .font(.callout) @@ -129,7 +122,7 @@ struct TelemetryLog: View { .font(.callout) } - if sensor == SensorTypes.bme680 { + if tel.gasResistance > 0 { Image(systemName: "aqi.medium") .font(.callout) @@ -140,8 +133,7 @@ struct TelemetryLog: View { .font(.callout) } - if sensor == SensorTypes.ina219 || - sensor == SensorTypes.ina260 { + if tel.current > 0 { Image(systemName: "directcurrent") .font(.callout) @@ -242,7 +234,6 @@ struct TelemetryLog: View { } else if tel.metricsType == 1 { // Environment Metrics - let sensor = SensorTypes(rawValue: Int(node.telemetryConfig?.environmentSensorType ?? 0)) let tempReadingType = (!(node.telemetryConfig?.environmentDisplayFahrenheit ?? true)) ? "°C" : "°F" @@ -260,10 +251,7 @@ struct TelemetryLog: View { HStack { - if sensor == SensorTypes.bme280 || - sensor == SensorTypes.bme680 || - sensor == SensorTypes.shtc3 || - sensor == SensorTypes.mcp9808 { + if tel.temperature > 0 { Image(systemName: "thermometer") .font(.callout) @@ -277,9 +265,7 @@ struct TelemetryLog: View { HStack { - if sensor == SensorTypes.bme280 || - sensor == SensorTypes.bme680 || - sensor == SensorTypes.shtc3 { + if tel.relativeHumidity > 0 { Image(systemName: "humidity") .font(.callout) @@ -291,8 +277,7 @@ struct TelemetryLog: View { } } - if sensor == SensorTypes.ina219 || - sensor == SensorTypes.ina260 { + if tel.current > 0 { HStack { @@ -317,8 +302,7 @@ struct TelemetryLog: View { } } - if sensor == SensorTypes.bme280 || - sensor == SensorTypes.bme680 { + if tel.barometricPressure > 0 { HStack { @@ -332,7 +316,7 @@ struct TelemetryLog: View { } } - if sensor == SensorTypes.bme680 { + if tel.gasResistance > 0 { HStack { diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 8818d68b..32896e4c 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -6,74 +6,6 @@ // import SwiftUI -enum SensorTypes: Int, CaseIterable, Identifiable { - - /// No external telemetry sensor explicitly set - case notSet = 0 - - /// 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 .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" - } - } - } - func protoEnumValue() -> TelemetrySensorType { - - switch self { - - - case .notSet: - return TelemetrySensorType.notSet - case .bme280: - return TelemetrySensorType.bme280 - case .bme680: - return TelemetrySensorType.bme680 - case .mcp9808: - return TelemetrySensorType.mcp9808 - case .shtc3: - return TelemetrySensorType.shtc3 - case .ina260: - return TelemetrySensorType.ina260 - case .ina219: - return TelemetrySensorType.ina219 - } - } -} - // Default of 0 is off enum ErrorRecoveryIntervals: Int, CaseIterable, Identifiable { @@ -188,11 +120,8 @@ struct TelemetryConfig: View { @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 environmentRecoveryInterval = 0 - @State var environmentReadErrorCountThreshold = 0 var body: some View { @@ -222,6 +151,9 @@ struct TelemetryConfig: View { } Section(header: Text("Sensor Options")) { + + Text("") + .font(.caption) Toggle(isOn: $environmentMeasurementEnabled) { @@ -229,13 +161,6 @@ struct TelemetryConfig: View { } .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") @@ -247,37 +172,6 @@ struct TelemetryConfig: View { Label("Display Fahrenheit", systemImage: "thermometer") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - - } - - 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) } } .disabled(bleManager.connectedPeripheral == nil) @@ -306,11 +200,8 @@ struct TelemetryConfig: View { tc.deviceUpdateInterval = UInt32(deviceUpdateInterval) tc.environmentUpdateInterval = UInt32(environmentUpdateInterval) tc.environmentMeasurementEnabled = environmentMeasurementEnabled - tc.environmentSensorType = SensorTypes(rawValue: environmentSensorType)!.protoEnumValue() tc.environmentScreenEnabled = environmentScreenEnabled tc.environmentDisplayFahrenheit = environmentDisplayFahrenheit - tc.environmentRecoveryInterval = UInt32(environmentRecoveryInterval) - tc.environmentReadErrorCountThreshold = UInt32(environmentReadErrorCountThreshold) let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) @@ -338,16 +229,11 @@ struct TelemetryConfig: View { if self.initialLoad{ self.bleManager.context = context - self.deviceUpdateInterval = Int(node!.telemetryConfig?.deviceUpdateInterval ?? 0) self.environmentUpdateInterval = Int(node!.telemetryConfig?.environmentUpdateInterval ?? 0) self.environmentMeasurementEnabled = node!.telemetryConfig?.environmentMeasurementEnabled ?? false - self.environmentSensorType = Int(node!.telemetryConfig?.environmentSensorType ?? 0) self.environmentScreenEnabled = node!.telemetryConfig?.environmentScreenEnabled ?? false self.environmentDisplayFahrenheit = node!.telemetryConfig?.environmentDisplayFahrenheit ?? false - self.environmentRecoveryInterval = Int(node!.telemetryConfig?.environmentRecoveryInterval ?? 0) - self.environmentReadErrorCountThreshold = Int(node!.telemetryConfig?.environmentReadErrorCountThreshold ?? 0) - self.hasChanges = false self.initialLoad = false } @@ -373,13 +259,6 @@ struct TelemetryConfig: View { if newEnvEnabled != node!.telemetryConfig!.environmentMeasurementEnabled { hasChanges = true } } } - .onChange(of: environmentSensorType) { newEnvSensorType in - - if node != nil && node!.telemetryConfig != nil { - - if newEnvSensorType != node!.telemetryConfig!.environmentSensorType { hasChanges = true } - } - } .onChange(of: environmentScreenEnabled) { newEnvScreenEnabled in if node!.telemetryConfig != nil { @@ -394,20 +273,6 @@ struct TelemetryConfig: View { if newEnvDisplayF != node!.telemetryConfig!.environmentDisplayFahrenheit { hasChanges = true } } } - .onChange(of: environmentRecoveryInterval) { newEnvRecoveryInterval in - - if node != nil && node!.telemetryConfig != nil { - - if newEnvRecoveryInterval != node!.telemetryConfig!.environmentRecoveryInterval { hasChanges = true } - } - } - .onChange(of: environmentReadErrorCountThreshold) { newEnvReadErrorCountThreshold in - - if node != nil && node!.telemetryConfig != nil { - - if newEnvReadErrorCountThreshold != node!.telemetryConfig!.environmentReadErrorCountThreshold { hasChanges = true } - } - } .navigationViewStyle(StackNavigationViewStyle()) } } From 4759da2c37dbab91d3ce530d2ba877b601d3fd78 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 1 Aug 2022 07:25:39 -0700 Subject: [PATCH 02/36] Add some text explaining sensor I2C detection --- Meshtastic/Export/WriteCsvFile.swift | 1 + Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Meshtastic/Export/WriteCsvFile.swift b/Meshtastic/Export/WriteCsvFile.swift index 4e1f2b49..accb70a3 100644 --- a/Meshtastic/Export/WriteCsvFile.swift +++ b/Meshtastic/Export/WriteCsvFile.swift @@ -35,6 +35,7 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin } } else { + // Create Environment Telemetry Header csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, Voltage, Current" diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 32896e4c..14123fac 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -152,7 +152,7 @@ struct TelemetryConfig: View { Section(header: Text("Sensor Options")) { - Text("") + Text("I2C Connected sensors will be detected automatically. Supported sensors are BMP280, BME280, BME680, MCP9808, INA219 and INA260.") .font(.caption) Toggle(isOn: $environmentMeasurementEnabled) { From 096930efd79e780e04e135f8ad8bb90a6319cdff Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 1 Aug 2022 21:57:03 -0700 Subject: [PATCH 03/36] Start of wifi config --- Meshtastic.xcodeproj/project.pbxproj | 4 + .../contents | 12 +- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- .../Views/Settings/Config/WiFiConfig.swift | 147 ++++++++++++++++++ Meshtastic/Views/Settings/Settings.swift | 14 +- Meshtastic/Views/Settings/ShareChannel.swift | 1 + 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 Meshtastic/Views/Settings/Config/WiFiConfig.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f8a5d04a..205b9eb3 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */; }; DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; }; DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; }; + DD8ED9C52898D51F00B3B0AB /* WiFiConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C42898D51F00B3B0AB /* WiFiConfig.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 */; }; @@ -146,6 +147,7 @@ DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = ""; }; DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = ""; }; DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 5.xcdatamodel"; sourceTree = ""; }; + DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiConfig.swift; sourceTree = ""; }; DD90860A26F645B700DC5189 /* Meshtastic.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meshtastic.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -275,6 +277,7 @@ DD8EBF42285058FA00426DCA /* DisplayConfig.swift */, DD2553562855B02500E55709 /* LoRaConfig.swift */, DD2553582855B52700E55709 /* PositionConfig.swift */, + DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */, DD61937B2863877A00E59241 /* Module */, ); path = Config; @@ -680,6 +683,7 @@ C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, + DD8ED9C52898D51F00B3B0AB /* WiFiConfig.swift in Sources */, DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */, DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */, diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents index cc2670e5..7acb195b 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents @@ -106,6 +106,7 @@ + @@ -190,6 +191,14 @@ + + + + + + + + @@ -198,7 +207,7 @@ - + @@ -206,5 +215,6 @@ + \ No newline at end of file diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index b8e59b63..8672b5d7 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -22,7 +22,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.27" + @State var minimumVersion = "1.3.28" @State var invalidVersion = false diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift new file mode 100644 index 00000000..92aa068d --- /dev/null +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -0,0 +1,147 @@ +// +// WiFiConfig.swift +// Meshtastic +// +// Copyright (c) Garth Vander Houwen 8/1/2022 +// + +import SwiftUI + +struct WiFiConfig: 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: Bool = false + + @State var ssid = "" + @State var password = "" + + @State var apMode = false + @State var apHidden = false + + var body: some View { + + VStack { + + Text("Enabling WiFi will disable bluetooth, only one connection method works at a time. Saving these settings will disconnect your device from the app.") + .font(.title3) + .padding() + + Form { + + Section(header: Text("SSID & Password")) { + + + } + Section(header: Text("AP Settings")) { + + Toggle(isOn: $apMode) { + + Label("Soft AP Mode", systemImage: "wifi") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("If set the software access point mode will be activated.") + .font(.caption) + + Toggle(isOn: $apHidden) { + + Label("Hidden AP", systemImage: "eye.slash") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("If set the SSID for the AP will be hidden.") + .font(.caption) + + } + } + .disabled(bleManager.connectedPeripheral == nil) + + 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 WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var wifi = Config.WiFiConfig() + wifi.ssid = ssid + wifi.psk = password + wifi.apMode = apMode + wifi.apHidden = apHidden + + //let adminMessageId = bleManager.saveWiFiConfig(config: wiFi, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = 0 + if adminMessageId > 0 { + + // 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("WiFi Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + }) + .onAppear { + + if self.initialLoad{ + + self.bleManager.context = context + + self.ssid = node!.wiFiConfig?.ssid ?? "" + self.password = node!.wiFiConfig?.password ?? "" + self.apMode = (node!.wiFiConfig?.apMode ?? false) + self.apHidden = (node!.wiFiConfig?.apHidden ?? false) + + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: ssid) { newSsid in + + hasChanges = true + //if node != nil && node!.wiFiConfig != nil { newSsid != node!.wiFiConfig.ssid { hasChanges = true }} + } + .onChange(of: password) { newPassword in + + hasChanges = true + //if node != nil && node!.wiFiConfig != nil { newPassword != node!.wiFiConfig!.password { hasChanges = true }} + } + .onChange(of: apMode) { newAPMode in + + hasChanges = true + //if node != nil && node!.wiFiConfig != nil { newAPMode != node!.wiFiConfig!.apMode { self.hasChanges = true }} + } + .onChange(of: apHidden) { newAPHidden in + + hasChanges = true + //if node != nil && node!.wiFiConfig != nil { newAPHidden != node!.wiFiConfig!.apHidden { hasChanges = true }} + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 8eb6180c..d656b809 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -100,6 +100,17 @@ struct Settings: View { } .disabled(bleManager.connectedPeripheral == nil) + NavigationLink { + WiFiConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + } label: { + + Image(systemName: "wifi") + .symbolRenderingMode(.hierarchical) + + Text("WiFi") + } + .disabled(bleManager.connectedPeripheral == nil) + Text("Default settings values are prefered as they consume no bandwidth when sent over the mesh.") .font(.caption2) .fixedSize(horizontal: false, vertical: true) @@ -193,8 +204,7 @@ struct Settings: View { // Not Implemented: // Store Forward Config - Not Working, TBEAM Only - // WiFi Config - Would break connection to device - // MQTT Config - Part of WiFi + // MQTT Config - Can do from WebUI once WiFi is enabled } .onAppear { diff --git a/Meshtastic/Views/Settings/ShareChannel.swift b/Meshtastic/Views/Settings/ShareChannel.swift index 0615b803..d2eb08e0 100644 --- a/Meshtastic/Views/Settings/ShareChannel.swift +++ b/Meshtastic/Views/Settings/ShareChannel.swift @@ -70,6 +70,7 @@ struct ShareChannel: View { ) Spacer() Text("Channel Name (Long/Slow)").font(.title) + Text(String(node!.myInfo!.maxChannels)) Spacer() } .frame(width: bounds.size.width, height: bounds.size.height) From 045fb598d532a349f78a3a1e66a138c18cdccdb0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 2 Aug 2022 09:44:59 -0700 Subject: [PATCH 04/36] Finish wifi config --- Meshtastic/Helpers/BLEManager.swift | 32 ++++++- Meshtastic/Helpers/MeshPackets.swift | 88 +++++++++++++++++- .../Views/Settings/Config/WiFiConfig.swift | 91 +++++++++++++++---- Meshtastic/Views/Settings/UserConfig.swift | 21 +++-- 4 files changed, 202 insertions(+), 30 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 8bc54686..0e093920 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1112,6 +1112,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return 0 } + public func saveWiFiConfig(config: Config.WiFiConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 { + + var adminPacket = AdminMessage() + adminPacket.setConfig.wifi = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() @@ -1174,14 +1203,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph var adminPacket = AdminMessage() adminPacket.getCannedMessageModulePart1Request = true - //adminPacket.getOwnerRequest = true - var meshPacket: MeshPacket = MeshPacket() meshPacket.to = UInt32(connectedPeripheral.num) meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -334,6 +339,87 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont } } + + if config.payloadVariant == Config.OneOf_PayloadVariant.wifi(config.wifi) { + + var isDefault = false + + if (try! config.wifi.jsonString()) == "{}" { + + isDefault = true + if meshlogging { MeshLogger.log("📶 Default WiFi config received \(String(nodeNum))") } + + } else { + + if meshlogging { MeshLogger.log("📶 Custom WiFi config received \(String(nodeNum))") } + } + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) + + do { + + let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] + // Found a node, save WiFi Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].wiFiConfig == nil { + + let newWiFiConfig = WiFiConfigEntity(context: context) + + if isDefault { + + newWiFiConfig.ssid = "" + newWiFiConfig.password = "" + newWiFiConfig.apMode = false + newWiFiConfig.apHidden = false + + } else { + + newWiFiConfig.ssid = config.wifi.ssid + newWiFiConfig.password = config.wifi.psk + newWiFiConfig.apMode = config.wifi.apMode + newWiFiConfig.apHidden = config.wifi.apHidden + } + newWiFiConfig.num = fetchedNode[0].num + fetchedNode[0].wiFiConfig = newWiFiConfig + + } else { + + if isDefault { + + fetchedNode[0].wiFiConfig?.ssid = "" + fetchedNode[0].wiFiConfig?.password = "" + fetchedNode[0].wiFiConfig?.apMode = false + fetchedNode[0].wiFiConfig?.apHidden = false + + } else { + + fetchedNode[0].wiFiConfig?.ssid = config.wifi.ssid + fetchedNode[0].wiFiConfig?.password = config.wifi.psk + fetchedNode[0].wiFiConfig?.apMode = config.wifi.apMode + fetchedNode[0].wiFiConfig?.apHidden = config.wifi.apHidden + } + } + + do { + + try context.save() + if meshlogging { MeshLogger.log("💾 Updated WiFi Config for node number: \(String(nodeNum))") } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Updating Core Data WifionfigEntity: \(nsError)") + } + } + + } catch { + + } + } } func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) { @@ -425,7 +511,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj try context.save() if meshlogging { MeshLogger.log("💾 Updated Canned Message Module Config for node number: \(String(nodeNum))") } - print(try config.cannedMessage.jsonString()) + } catch { diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index 92aa068d..f4c1d7d7 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -36,6 +36,55 @@ struct WiFiConfig: View { Section(header: Text("SSID & Password")) { + HStack { + Label("SSID", systemImage: "wifi") + TextField("SSID", text: $ssid) + .foregroundColor(.gray) + .onChange(of: ssid, perform: { value in + + let totalBytes = ssid.utf8.count + + // Only mess with the value if it is too big + if totalBytes > 30 { + + let firstNBytes = Data(ssid.utf8.prefix(30)) + + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + + // Set the shortName back to the last place where it was the right size + ssid = maxBytesString + } + } + }) + .foregroundColor(.gray) + } + .keyboardType(.default) + .disableAutocorrection(true) + + HStack { + Label("Password", systemImage: "wallet.pass") + TextField("Password", text: $password) + .foregroundColor(.gray) + .onChange(of: password, perform: { value in + + let totalBytes = password.utf8.count + + // Only mess with the value if it is too big + if totalBytes > 60 { + + let firstNBytes = Data(ssid.utf8.prefix(60)) + + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + + // Set the shortName back to the last place where it was the right size + ssid = maxBytesString + } + } + }) + .foregroundColor(.gray) + } + .keyboardType(.default) + .disableAutocorrection(true) } Section(header: Text("AP Settings")) { @@ -81,18 +130,18 @@ struct WiFiConfig: View { Button("Save WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { var wifi = Config.WiFiConfig() - wifi.ssid = ssid - wifi.psk = password - wifi.apMode = apMode - wifi.apHidden = apHidden + wifi.ssid = self.ssid + wifi.psk = self.password + wifi.apMode = self.apMode + wifi.apHidden = self.apHidden + + let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) - //let adminMessageId = bleManager.saveWiFiConfig(config: wiFi, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) - let adminMessageId = 0 if adminMessageId > 0 { // 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 + self.hasChanges = false } else { @@ -124,23 +173,31 @@ struct WiFiConfig: View { } .onChange(of: ssid) { newSsid in - hasChanges = true - //if node != nil && node!.wiFiConfig != nil { newSsid != node!.wiFiConfig.ssid { hasChanges = true }} + if node != nil && node!.wiFiConfig != nil { + + if newSsid != node!.wiFiConfig!.ssid { hasChanges = true } + } } .onChange(of: password) { newPassword in - hasChanges = true - //if node != nil && node!.wiFiConfig != nil { newPassword != node!.wiFiConfig!.password { hasChanges = true }} + if node != nil && node!.wiFiConfig != nil { + + if newPassword != node!.wiFiConfig!.password { hasChanges = true } + } } - .onChange(of: apMode) { newAPMode in + .onChange(of: apMode) { newApMode in - hasChanges = true - //if node != nil && node!.wiFiConfig != nil { newAPMode != node!.wiFiConfig!.apMode { self.hasChanges = true }} + if node != nil && node!.wiFiConfig != nil { + + if newApMode != node!.wiFiConfig!.apMode { hasChanges = true } + } } - .onChange(of: apHidden) { newAPHidden in + .onChange(of: apHidden) { newApHidden in - hasChanges = true - //if node != nil && node!.wiFiConfig != nil { newAPHidden != node!.wiFiConfig!.apHidden { hasChanges = true }} + if node != nil && node!.wiFiConfig != nil { + + if newApHidden != node!.wiFiConfig!.apHidden { hasChanges = true } + } } .navigationViewStyle(StackNavigationViewStyle()) } diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index eedb3504..6d9d304d 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -48,6 +48,17 @@ struct UserConfig: View { } } }) + + } + .keyboardType(.default) + .disableAutocorrection(true) + Text("Long name can be up to 36 bytes long.") + .font(.caption) + + HStack { + Label("Short Name", systemImage: "circlebadge.fill") + TextField("Long Name", text: $shortName) + .foregroundColor(.gray) .onChange(of: shortName, perform: { value in let totalBytes = shortName.utf8.count @@ -66,16 +77,6 @@ struct UserConfig: View { }) .foregroundColor(.gray) } - .keyboardType(.default) - .disableAutocorrection(true) - Text("Long name can be up to 36 bytes long.") - .font(.caption) - - HStack { - Label("Short Name", systemImage: "circlebadge.fill") - TextField("Long Name", text: $shortName) - .foregroundColor(.gray) - } .keyboardType(.asciiCapable) .disableAutocorrection(true) 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 4 bytes long.") From 2756db10ec3affca58372caaa95275add9ce28ca Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 2 Aug 2022 09:49:11 -0700 Subject: [PATCH 05/36] Restrict wifi to devices with wifi --- Meshtastic/Views/Settings/Config/WiFiConfig.swift | 2 +- Meshtastic/Views/Settings/Settings.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index f4c1d7d7..246c2e0b 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -107,7 +107,7 @@ struct WiFiConfig: View { } } - .disabled(bleManager.connectedPeripheral == nil) + .disabled(!(node != nil && node!.myInfo?.hasWifi ?? false)) Button { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index d656b809..e1887074 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -107,7 +107,7 @@ struct Settings: View { Image(systemName: "wifi") .symbolRenderingMode(.hierarchical) - Text("WiFi") + Text("WiFi (ESP32 Only)") } .disabled(bleManager.connectedPeripheral == nil) From 8aeb204f17134d1f943246e2b25d42e80e59fd89 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 2 Aug 2022 09:52:32 -0700 Subject: [PATCH 06/36] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 205b9eb3..0bfd178e 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -880,7 +880,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.27; + MARKETING_VERSION = 1.3.28; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -912,7 +912,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.27; + MARKETING_VERSION = 1.3.28; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From af13a1733fd8e8ad3e0e58476f31864d59d6e931 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 2 Aug 2022 16:25:45 -0700 Subject: [PATCH 07/36] Fix wifi data validation so config will save --- .../MeshtasticDataModel v 5.xcdatamodel/contents | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents index 7acb195b..84e9cfee 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents @@ -195,8 +195,8 @@ - - + + From a0265a43114b254bbb627eed8156c9c7ecdf177b Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 3 Aug 2022 07:28:11 -0700 Subject: [PATCH 08/36] Fix telemetry title, remove messages from logging --- Meshtastic/Helpers/BLEManager.swift | 1 + Meshtastic/Helpers/MeshPackets.swift | 4 ++-- Meshtastic/Views/Nodes/TelemetryLog.swift | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 0e093920..636b0f35 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1214,6 +1214,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph var dataMessage = DataMessage() dataMessage.payload = try! adminPacket.serializedData() dataMessage.portnum = PortNum.adminApp + dataMessage.wantResponse = wantResponse meshPacket.decoded = dataMessage diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 5d1febfc..d03afff3 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1422,7 +1422,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging: if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) { - if meshLogging { MeshLogger.log("💬 Message received for text message app \(messageText)") } + if meshLogging { MeshLogger.log("💬 Message received for text message app") } let messageUsers: NSFetchRequest = NSFetchRequest.init(entityName: "UserEntity") messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from]) @@ -1496,7 +1496,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging: ] manager.schedule() - if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") } + if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown")") } } } catch { diff --git a/Meshtastic/Views/Nodes/TelemetryLog.swift b/Meshtastic/Views/Nodes/TelemetryLog.swift index b6e5fd05..d60f72c5 100644 --- a/Meshtastic/Views/Nodes/TelemetryLog.swift +++ b/Meshtastic/Views/Nodes/TelemetryLog.swift @@ -177,7 +177,7 @@ struct TelemetryLog: View { Spacer() Text("Device Metrics") - .font(.title) + .font(.title3) Spacer() } @@ -234,7 +234,6 @@ struct TelemetryLog: View { } else if tel.metricsType == 1 { // Environment Metrics - let tempReadingType = (!(node.telemetryConfig?.environmentDisplayFahrenheit ?? true)) ? "°C" : "°F" From f513049366f11e13c257093a1d4ac1099f274734 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 3 Aug 2022 15:48:54 -0700 Subject: [PATCH 09/36] Trap config race condition. --- Meshtastic/Helpers/MeshPackets.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index d03afff3..c83504f3 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -243,10 +243,16 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont let nsError = error as NSError print("💥 Error Updating Core Data LoRaConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in core data matching connected node number \(nodeNum)") } + } catch { + let nsError = error as NSError + print("💥 Fetching node for core data LoRaConfigEntity failed: \(nsError)") } } From 1f0735749aa4c028d5ad2addf64c7d4cc16f55ad Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 4 Aug 2022 19:43:03 -0700 Subject: [PATCH 10/36] Upgrade DM asks, update protobufs, streamline wifi config, add enabled to wificonfig --- Meshtastic/Helpers/MeshPackets.swift | 54 ++++++++++--------- .../contents | 6 ++- Meshtastic/Protobufs/config.pb.swift | 10 ++++ Meshtastic/Protobufs/mesh.pb.swift | 8 +++ .../Views/Messages/UserMessageList.swift | 3 ++ Meshtastic/Views/Nodes/NodeList.swift | 18 +++---- .../Config/Module/CannedMessagesConfig.swift | 9 ++-- .../Module/ExternalNotificationConfig.swift | 2 +- .../Views/Settings/Config/WiFiConfig.swift | 48 ++++++++++++----- 9 files changed, 105 insertions(+), 53 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index c83504f3..6d5bb1f1 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1318,43 +1318,45 @@ func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObj if meshLogging { MeshLogger.log("🕸️ ROUTING PACKET received for RequestID: \(packet.decoded.requestID) Error: \(errorExplanation)") } - if routingMessage.errorReason == Routing.Error.none { - let fetchMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") - fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID)) + let fetchMessageRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MessageEntity") + fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID)) - do { + do { - let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity] + let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity] + + if fetchedMessage?.count ?? 0 > 0 { - if fetchedMessage?.count ?? 0 > 0 { + fetchedMessage![0].ackError = Int32(routingMessage.errorReason.rawValue) + + if routingMessage.errorReason == Routing.Error.none { fetchedMessage![0].receivedACK = true - fetchedMessage![0].ackSNR = packet.rxSnr - fetchedMessage![0].ackTimestamp = Int32(packet.rxTime) - fetchedMessage![0].objectWillChange.send() - fetchedMessage![0].fromUser?.objectWillChange.send() - fetchedMessage![0].toUser?.objectWillChange.send() - - } else { - - return } + fetchedMessage![0].ackSNR = packet.rxSnr + fetchedMessage![0].ackTimestamp = Int32(packet.rxTime) + fetchedMessage![0].objectWillChange.send() - try context.save() - - if meshLogging { - MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)") - } + } else { - } catch { - - context.rollback() - - let nsError = error as NSError - print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)") + return } + + try context.save() + + if meshLogging { + MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)") + } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)") } + } } diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents index 84e9cfee..54fd678a 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 5.xcdatamodel/contents @@ -47,6 +47,7 @@ + @@ -194,6 +195,7 @@ + @@ -205,7 +207,7 @@ - + @@ -215,6 +217,6 @@ - + \ No newline at end of file diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index d37e9615..ad46c586 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -527,6 +527,10 @@ struct Config { /// If set, the node AP will broadcast as a hidden SSID var apHidden: Bool = false + /// + /// If set, wifi is enabled. Previously done through setting ssid and psk + var enabled: Bool = false + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1381,6 +1385,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem 2: .same(proto: "psk"), 3: .standard(proto: "ap_mode"), 4: .standard(proto: "ap_hidden"), + 5: .same(proto: "enabled"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1393,6 +1398,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem case 2: try { try decoder.decodeSingularStringField(value: &self.psk) }() case 3: try { try decoder.decodeSingularBoolField(value: &self.apMode) }() case 4: try { try decoder.decodeSingularBoolField(value: &self.apHidden) }() + case 5: try { try decoder.decodeSingularBoolField(value: &self.enabled) }() default: break } } @@ -1411,6 +1417,9 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if self.apHidden != false { try visitor.visitSingularBoolField(value: self.apHidden, fieldNumber: 4) } + if self.enabled != false { + try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 5) + } try unknownFields.traverse(visitor: &visitor) } @@ -1419,6 +1428,7 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem if lhs.psk != rhs.psk {return false} if lhs.apMode != rhs.apMode {return false} if lhs.apHidden != rhs.apHidden {return false} + if lhs.enabled != rhs.enabled {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/mesh.pb.swift b/Meshtastic/Protobufs/mesh.pb.swift index a09549de..d528fb7e 100644 --- a/Meshtastic/Protobufs/mesh.pb.swift +++ b/Meshtastic/Protobufs/mesh.pb.swift @@ -130,6 +130,10 @@ enum HardwareModel: SwiftProtobuf.Enum { /// M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ case m5Stack // = 44 + /// + /// B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station + case stationG1 // = 45 + /// /// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. case privateHw // = 255 @@ -166,6 +170,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case 42: self = .nrf52840Pca10059 case 43: self = .drDev case 44: self = .m5Stack + case 45: self = .stationG1 case 255: self = .privateHw default: self = .UNRECOGNIZED(rawValue) } @@ -198,6 +203,7 @@ enum HardwareModel: SwiftProtobuf.Enum { case .nrf52840Pca10059: return 42 case .drDev: return 43 case .m5Stack: return 44 + case .stationG1: return 45 case .privateHw: return 255 case .UNRECOGNIZED(let i): return i } @@ -235,6 +241,7 @@ extension HardwareModel: CaseIterable { .nrf52840Pca10059, .drDev, .m5Stack, + .stationG1, .privateHw, ] } @@ -2176,6 +2183,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding { 42: .same(proto: "NRF52840_PCA10059"), 43: .same(proto: "DR_DEV"), 44: .same(proto: "M5STACK"), + 45: .same(proto: "STATION_G1"), 255: .same(proto: "PRIVATE_HW"), ] } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index e4941797..ed4366ef 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -236,6 +236,9 @@ struct UserMessageList: View { Text("Unknown Age").font(.caption2).foregroundColor(.gray) } } + } else if message.ackError > 0 { + + Text("Ack Failure") } if message.ackSNR != 0 { VStack { diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 94c6bd06..95755bd4 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -19,7 +19,7 @@ struct NodeList: View { @State var initialLoad: Bool = true @FetchRequest( - sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], + sortDescriptors: [NSSortDescriptor(key: "user.shortName", ascending: true)], animation: .default) private var nodes: FetchedResults @@ -61,7 +61,7 @@ struct NodeList: View { if UIDevice.current.userInterfaceIdiom == .pad { Text(node.user?.longName ?? "Unknown").font(.headline) .offset(x: -15) } else { - Text(node.user?.longName ?? "Unknown").font(.title).offset(x: -15) + Text(node.user?.longName ?? "Unknown").font(.title2).offset(x: -15) } } .padding(.bottom, 10) @@ -106,16 +106,16 @@ struct NodeList: View { if initialLoad { - self.bleManager.context = context self.bleManager.userSettings = userSettings + self.bleManager.context = context self.initialLoad = false - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - - if nodes.count > 0 { - selection = "0" - } - } +// if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { +// +// if nodes.count > 0 { +// selection = "0" +// } +// } } } } diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index fe3ec406..3fe04782 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -376,9 +376,12 @@ struct CannedMessagesConfig: View { // RAK Rotary Encoder updown1Enabled = true rotary1Enabled = false - inputbrokerEventCw = InputEventChars.keyUp.rawValue - inputbrokerEventCcw = InputEventChars.keyDown.rawValue - inputbrokerEventPress = InputEventChars.keySelect.rawValue + inputbrokerPinA = 4 + inputbrokerPinB = 10 + inputbrokerPinPress = 3 + inputbrokerEventCw = InputEventChars.keyNone.rawValue + inputbrokerEventCcw = InputEventChars.keyNone.rawValue + inputbrokerEventPress = InputEventChars.keyNone.rawValue } else if newPreset == 2 { diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index e54f3617..3769d4f6 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -139,7 +139,7 @@ struct ExternalNotificationConfig: View { Label("Save", systemImage: "square.and.arrow.down") } - .disabled(bleManager.connectedPeripheral == nil || !hasChanges || !(node!.myInfo?.hasWifi ?? false)) + .disabled(bleManager.connectedPeripheral == nil || !hasChanges) .buttonStyle(.bordered) .buttonBorderShape(.capsule) .controlSize(.large) diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index 246c2e0b..a569f9d3 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -17,6 +17,8 @@ struct WiFiConfig: View { @State private var isPresentingSaveConfirm: Bool = false @State var initialLoad: Bool = true @State var hasChanges: Bool = false + + @State var enabled = false @State var ssid = "" @State var password = "" @@ -28,16 +30,29 @@ struct WiFiConfig: View { VStack { - Text("Enabling WiFi will disable bluetooth, only one connection method works at a time. Saving these settings will disconnect your device from the app.") - .font(.title3) + Text("Enabling WiFi will disable the bluetooth connection to the app.") + .font(.callout) .padding() Form { - Section(header: Text("SSID & Password")) { + + Section(header: Text("Options")) { + + Text("WiFi client mode is enabled by default, if Soft AP is enabled the SSID and psk will be used as the default credentials for the access point.") + .font(.caption) + + //HStack { + + Toggle(isOn: $enabled) { + + Label("Enable", systemImage: "wifi") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + // } HStack { - Label("SSID", systemImage: "wifi") + Label("SSID", systemImage: "network") TextField("SSID", text: $ssid) .foregroundColor(.gray) .onChange(of: ssid, perform: { value in @@ -91,19 +106,21 @@ struct WiFiConfig: View { Toggle(isOn: $apMode) { - Label("Soft AP Mode", systemImage: "wifi") + Label("Soft AP Mode", systemImage: "externaldrive.fill.badge.wifi") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("If set the software access point mode will be activated.") - .font(.caption) - Toggle(isOn: $apHidden) { + if apMode { + + Toggle(isOn: $apHidden) { - Label("Hidden AP", systemImage: "eye.slash") + Label("Hidden AP", systemImage: "eye.slash") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("If set the SSID for the AP will be hidden.") + .font(.caption) } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("If set the SSID for the AP will be hidden.") - .font(.caption) + } } @@ -171,6 +188,13 @@ struct WiFiConfig: View { self.initialLoad = false } } + .onChange(of: enabled) { newEnabled in + + if node != nil && node!.wiFiConfig != nil { + + if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true } + } + } .onChange(of: ssid) { newSsid in if node != nil && node!.wiFiConfig != nil { From f92738611f03cc4f8713ec21e8b839b2cb90754f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 4 Aug 2022 20:23:40 -0700 Subject: [PATCH 11/36] Firmware version requirement update --- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- .../Views/Settings/Config/WiFiConfig.swift | 33 ++++++++----------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 8672b5d7..1e69925e 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -22,7 +22,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.28" + @State var minimumVersion = "1.3.30" @State var invalidVersion = false diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index a569f9d3..0e44b935 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -30,26 +30,18 @@ struct WiFiConfig: View { VStack { - Text("Enabling WiFi will disable the bluetooth connection to the app.") - .font(.callout) - .padding() - Form { + Text("Enabling WiFi will disable the bluetooth connection to the app.") + .font(.title3) Section(header: Text("Options")) { - - Text("WiFi client mode is enabled by default, if Soft AP is enabled the SSID and psk will be used as the default credentials for the access point.") - .font(.caption) - - //HStack { - Toggle(isOn: $enabled) { + Toggle(isOn: $enabled) { - Label("Enable", systemImage: "wifi") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - // } + Label("Enable", systemImage: "wifi") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) HStack { Label("SSID", systemImage: "network") @@ -102,7 +94,10 @@ struct WiFiConfig: View { .disableAutocorrection(true) } - Section(header: Text("AP Settings")) { + Section(header: Text("Sofware Access Point")) { + + Text("WiFi uses client mode by default, if Software Access Point(AP) is on the SSID and password will be used to access the AP at meshtastic.local.") + .font(.caption) Toggle(isOn: $apMode) { @@ -114,14 +109,10 @@ struct WiFiConfig: View { Toggle(isOn: $apHidden) { - Label("Hidden AP", systemImage: "eye.slash") + Label("Hidden SSID", systemImage: "eye.slash") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("If set the SSID for the AP will be hidden.") - .font(.caption) } - - } } .disabled(!(node != nil && node!.myInfo?.hasWifi ?? false)) @@ -147,6 +138,7 @@ struct WiFiConfig: View { Button("Save WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { var wifi = Config.WiFiConfig() + wifi.enabled = self.enabled wifi.ssid = self.ssid wifi.psk = self.password wifi.apMode = self.apMode @@ -179,6 +171,7 @@ struct WiFiConfig: View { self.bleManager.context = context + self.enabled = (node!.wiFiConfig?.enabled ?? false) self.ssid = node!.wiFiConfig?.ssid ?? "" self.password = node!.wiFiConfig?.password ?? "" self.apMode = (node!.wiFiConfig?.apMode ?? false) From 510d731fa7265514a540f51888e6787f5aafb6c7 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 4 Aug 2022 20:41:27 -0700 Subject: [PATCH 12/36] Bump Version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 0bfd178e..279a0636 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -880,7 +880,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.28; + MARKETING_VERSION = 1.3.30; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -912,7 +912,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.28; + MARKETING_VERSION = 1.3.30; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From dccefee3383c553aabbb90e00b6c7e7d701380b9 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 5 Aug 2022 15:58:12 -0700 Subject: [PATCH 13/36] ACK errors --- Meshtastic.xcodeproj/project.pbxproj | 12 ++ Meshtastic/Enums/RoutingError.swift | 109 ++++++++++++++++++ Meshtastic/Helpers/MeshPackets.swift | 2 + .../Views/Messages/UserMessageList.swift | 57 +++++---- 4 files changed, 157 insertions(+), 23 deletions(-) create mode 100644 Meshtastic/Enums/RoutingError.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 279a0636..d1afffe5 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; }; DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; }; DD8ED9C52898D51F00B3B0AB /* WiFiConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */; }; + DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C7289CE4B900B3B0AB /* RoutingError.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 */; }; @@ -148,6 +149,7 @@ DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = ""; }; DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 5.xcdatamodel"; sourceTree = ""; }; DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiConfig.swift; sourceTree = ""; }; + DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingError.swift; sourceTree = ""; }; DD90860A26F645B700DC5189 /* Meshtastic.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meshtastic.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -305,6 +307,14 @@ path = Export; sourceTree = ""; }; + DD8ED9C6289CE4A100B3B0AB /* Enums */ = { + isa = PBXGroup; + children = ( + DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */, + ); + path = Enums; + sourceTree = ""; + }; DD8EDE9226F97A2B00A5A10B /* Frameworks */ = { isa = PBXGroup; children = ( @@ -359,6 +369,7 @@ DDC2E15626CE248E0042C5E4 /* Meshtastic */ = { isa = PBXGroup; children = ( + DD8ED9C6289CE4A100B3B0AB /* Enums */, DD90860A26F645B700DC5189 /* Meshtastic.entitlements */, DDC4D5662754996200A4208E /* Persistence */, DDAF8C5626ED07740058C060 /* Protobufs */, @@ -680,6 +691,7 @@ DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */, + DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, diff --git a/Meshtastic/Enums/RoutingError.swift b/Meshtastic/Enums/RoutingError.swift new file mode 100644 index 00000000..b242f45f --- /dev/null +++ b/Meshtastic/Enums/RoutingError.swift @@ -0,0 +1,109 @@ +// +// RoutingError.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/4/22. +// + +enum RoutingError: Int, CaseIterable, Identifiable { + + case none = 0 + case noRoute = 1 + case gotNak = 2 + case timeout = 3 + case noInterface = 4 + case maxRetransmit = 5 + case noChannel = 6 + case tooLarge = 7 + case noResponse = 8 + case badRequest = 32 + case notAuthorized = 33 + + var id: Int { self.rawValue } + var display: String { + get { + switch self { + + case .none: + return "No Error." + case .noRoute: + return "No Route" + case .gotNak: + return "Received a nak" + case .timeout: + return "Timeout" + case .noInterface: + return "No Interface" + case .maxRetransmit: + return "Max Retransmission Reached" + case .noChannel: + return "No Channel" + case .tooLarge: + return "The packet is too large" + case .noResponse: + return "No Response" + case .badRequest: + return "Bad Request" + case .notAuthorized: + return "Not Authorized" + } + } + } + var description: String { + get { + switch self { + + case .none: + return "This message is not a failure." + case .noRoute: + return "Our node doesn't have a route to the requested destination anymore." + case .gotNak: + return "We received a nak while trying to forward on your behalf." + case .timeout: + return "We timed out while attempting to route this packet." + case .noInterface: + return "No suitable interface could be found for delivering this packet." + case .maxRetransmit: + return "We reached the max retransmission count (Hop Limit) and have received no responses." + case .noChannel: + return "No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)." + case .tooLarge: + return "The packet was too big for sending (exceeds interface MTU after encoding)." + case .noResponse: + return "The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions)." + case .badRequest: + return "The application layer service on the remote node received your request, but considered your request somehow invalid." + case .notAuthorized: + return "The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel)." + } + } + } + func protoEnumValue() -> Routing.Error { + + switch self { + + case .none: + return Routing.Error.none + case .noRoute: + return Routing.Error.noRoute + case .gotNak: + return Routing.Error.gotNak + case .timeout: + return Routing.Error.timeout + case .noInterface: + return Routing.Error.noInterface + case .maxRetransmit: + return Routing.Error.maxRetransmit + case .noChannel: + return Routing.Error.noChannel + case .tooLarge: + return Routing.Error.tooLarge + case .noResponse: + return Routing.Error.noResponse + case .badRequest: + return Routing.Error.badRequest + case .notAuthorized: + return Routing.Error.notAuthorized + } + } +} diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 6d5bb1f1..8157d990 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1337,6 +1337,8 @@ func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObj fetchedMessage![0].ackSNR = packet.rxSnr fetchedMessage![0].ackTimestamp = Int32(packet.rxTime) fetchedMessage![0].objectWillChange.send() + fetchedMessage![0].fromUser?.objectWillChange.send() + fetchedMessage![0].toUser?.objectWillChange.send() } else { diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index ed4366ef..846a05ca 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -213,37 +213,41 @@ struct UserMessageList: View { let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) - Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray) + Text("Sent \(messageDate, style: .date) \(messageDate.formattedDate(format: "h:mm:ss a"))").font(.caption2).foregroundColor(.gray) } - VStack { - - Text("Received ACK: \(message.receivedACK ? "✔️" : "")") - - } + if message.receivedACK { - VStack { - - let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp)) - - let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) - if ackDate >= sixMonthsAgo! { - - Text("ACK \(ackDate, style: .date) \(ackDate, style: .time)").font(.caption2).foregroundColor(.gray) - - } else { - - Text("Unknown Age").font(.caption2).foregroundColor(.gray) - } - } - } else if message.ackError > 0 { - Text("Ack Failure") + VStack { + + Text("Received Ack \(message.receivedACK ? "✔️" : "")") + } } + if message.ackError > 0 { + + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) + Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) + } + VStack { + + let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp)) + + let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) + if ackDate >= sixMonthsAgo! { + + Text((ackDate.formattedDate(format: "h:mm:ss a"))).font(.caption2).foregroundColor(.gray) + + } else { + + Text("Unknown Age").font(.caption2).foregroundColor(.gray) + } + } + if message.ackSNR != 0 { VStack { - Text("ACK SNR \(String(message.ackSNR))") + Text("Ack SNR \(String(message.ackSNR))") .font(.caption2) .foregroundColor(.gray) } @@ -296,6 +300,13 @@ struct UserMessageList: View { Text("Acknowledged").font(.caption2).foregroundColor(.gray) } + + if message.ackError > 0 { + + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) + Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) + .font(.caption2).foregroundColor(.red) + } } } .padding(.bottom) From af4ef58e1431060c831724067740a93790ad1aa3 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 6 Aug 2022 08:07:16 -0700 Subject: [PATCH 14/36] Support new wifi config, update firmware version modal, prevent connection of unsupported devices for real this time. --- Meshtastic.xcodeproj/project.pbxproj | 4 +- Meshtastic/Helpers/BLEManager.swift | 15 +- Meshtastic/Helpers/MeshPackets.swift | 12 +- Meshtastic/Meshtastic.entitlements | 2 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 221 ++++++++++++++++++ Meshtastic/Protobufs/config.pb.swift | 133 ++++++++--- Meshtastic/Views/Bluetooth/Connect.swift | 18 +- .../Views/Settings/Config/WiFiConfig.swift | 107 +++++---- 9 files changed, 403 insertions(+), 111 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index d1afffe5..40df6cb7 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -150,6 +150,7 @@ DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 5.xcdatamodel"; sourceTree = ""; }; DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiConfig.swift; sourceTree = ""; }; DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingError.swift; sourceTree = ""; }; + DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 6.xcdatamodel"; sourceTree = ""; }; DD90860A26F645B700DC5189 /* Meshtastic.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meshtastic.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -1097,13 +1098,14 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */, DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */, DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */, DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */, DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */; + currentVersion = DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 636b0f35..35b8dd92 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -29,7 +29,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph @Published var connectedPeripheral: Peripheral! @Published var lastConnectionError: String - @Published var lastConnnectionVersion: String + @Published var connectedVersion: String @Published var isSwitchedOn: Bool = false @Published var isScanning: Bool = false @@ -77,7 +77,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph //self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false self.lastConnectionError = "" - self.lastConnnectionVersion = "0.0.0" + self.connectedVersion = "0.0.0" super.init() // let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager") centralManager = CBCentralManager(delegate: self, queue: nil) @@ -159,13 +159,15 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") } stopScanning() + if self.connectedPeripheral != nil { if meshLoggingEnabled { MeshLogger.log("ℹ️ BLE Disconnecting from: \(self.connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")") } self.disconnectPeripheral() } - + + self.connectedVersion = "0.0.0" self.centralManager?.connect(peripheral) // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent @@ -460,15 +462,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // MyInfo if decodedInfo.myInfo.isInitialized && decodedInfo.myInfo.myNodeNum > 0 { + + let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".") + var version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))] nowKnown = true + connectedVersion = String(version) + let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!) if myInfo != nil { self.connectedPeripheral.bitrate = myInfo!.bitrate self.connectedPeripheral.num = myInfo!.myNodeNum - lastConnnectionVersion = myInfo?.firmwareVersion ?? myInfo!.firmwareVersion ?? "Unknown" + self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown" self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown" self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown" diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 8157d990..38a3b7a6 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -377,15 +377,13 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont newWiFiConfig.ssid = "" newWiFiConfig.password = "" - newWiFiConfig.apMode = false - newWiFiConfig.apHidden = false + newWiFiConfig.mode = 0 } else { newWiFiConfig.ssid = config.wifi.ssid newWiFiConfig.password = config.wifi.psk - newWiFiConfig.apMode = config.wifi.apMode - newWiFiConfig.apHidden = config.wifi.apHidden + newWiFiConfig.mode = Int32(config.wifi.mode.rawValue) } newWiFiConfig.num = fetchedNode[0].num fetchedNode[0].wiFiConfig = newWiFiConfig @@ -396,15 +394,13 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont fetchedNode[0].wiFiConfig?.ssid = "" fetchedNode[0].wiFiConfig?.password = "" - fetchedNode[0].wiFiConfig?.apMode = false - fetchedNode[0].wiFiConfig?.apHidden = false + fetchedNode[0].wiFiConfig?.mode = 0 } else { fetchedNode[0].wiFiConfig?.ssid = config.wifi.ssid fetchedNode[0].wiFiConfig?.password = config.wifi.psk - fetchedNode[0].wiFiConfig?.apMode = config.wifi.apMode - fetchedNode[0].wiFiConfig?.apHidden = config.wifi.apHidden + fetchedNode[0].wiFiConfig?.mode = Int32(config.wifi.mode.rawValue) } } diff --git a/Meshtastic/Meshtastic.entitlements b/Meshtastic/Meshtastic.entitlements index 68669b4c..5c8b6ee4 100644 --- a/Meshtastic/Meshtastic.entitlements +++ b/Meshtastic/Meshtastic.entitlements @@ -4,7 +4,7 @@ com.apple.developer.associated-domains - applinks:meshtastic.org/E/* + applinks:meshtastic.org/e/* com.apple.security.app-sandbox diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 5664b468..319888a5 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 5.xcdatamodel + MeshtasticDataModel v 6.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents new file mode 100644 index 00000000..eca24535 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index ad46c586..7d003b01 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -509,6 +509,15 @@ struct Config { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. + /// + /// Enable WiFi (disables Bluetooth) + var enabled: Bool = false + + /// + /// If set, this node will try to join the specified wifi network and + /// acquire an address via DHCP + var mode: Config.WiFiConfig.WiFiMode = .client + /// /// If set, this node will try to join the specified wifi network and /// acquire an address via DHCP @@ -518,21 +527,48 @@ struct Config { /// If set, will be use to authenticate to the named wifi var psk: String = String() - /// - /// If set, the node will operate as an AP (and DHCP server), otherwise it - /// will be a station - var apMode: Bool = false - - /// - /// If set, the node AP will broadcast as a hidden SSID - var apHidden: Bool = false - - /// - /// If set, wifi is enabled. Previously done through setting ssid and psk - var enabled: Bool = false - var unknownFields = SwiftProtobuf.UnknownStorage() + enum WiFiMode: SwiftProtobuf.Enum { + typealias RawValue = Int + + /// + /// This mode is used to connect to an external WiFi network + case client // = 0 + + /// + /// In this mode the node will operate as an AP (and DHCP server) + case accessPoint // = 1 + + /// + /// If set, the node AP will broadcast as a hidden SSID + case accessPointHidden // = 2 + case UNRECOGNIZED(Int) + + init() { + self = .client + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .client + case 1: self = .accessPoint + case 2: self = .accessPointHidden + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .client: return 0 + case .accessPoint: return 1 + case .accessPointHidden: return 2 + case .UNRECOGNIZED(let i): return i + } + } + + } + init() {} } @@ -557,6 +593,11 @@ struct Config { /// Potentially useful for devices without user buttons. var autoScreenCarouselSecs: UInt32 = 0 + /// + /// If this is set, the displayed compass will always point north. if unset, the old behaviour + /// (top of display is heading direction) is used. + var compassNorthTop: Bool = false + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -920,6 +961,15 @@ extension Config.PowerConfig.ChargeCurrent: CaseIterable { ] } +extension Config.WiFiConfig.WiFiMode: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [Config.WiFiConfig.WiFiMode] = [ + .client, + .accessPoint, + .accessPointHidden, + ] +} + extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable { // The compiler won't synthesize support with the UNRECOGNIZED case. static var allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [ @@ -976,6 +1026,7 @@ extension Config.PositionConfig.PositionFlags: @unchecked Sendable {} extension Config.PowerConfig: @unchecked Sendable {} extension Config.PowerConfig.ChargeCurrent: @unchecked Sendable {} extension Config.WiFiConfig: @unchecked Sendable {} +extension Config.WiFiConfig.WiFiMode: @unchecked Sendable {} extension Config.DisplayConfig: @unchecked Sendable {} extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {} extension Config.LoRaConfig: @unchecked Sendable {} @@ -1381,11 +1432,10 @@ extension Config.PowerConfig.ChargeCurrent: SwiftProtobuf._ProtoNameProviding { extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = Config.protoMessageName + ".WiFiConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "ssid"), - 2: .same(proto: "psk"), - 3: .standard(proto: "ap_mode"), - 4: .standard(proto: "ap_hidden"), - 5: .same(proto: "enabled"), + 1: .same(proto: "enabled"), + 2: .same(proto: "mode"), + 3: .same(proto: "ssid"), + 4: .same(proto: "psk"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1394,52 +1444,56 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.ssid) }() - case 2: try { try decoder.decodeSingularStringField(value: &self.psk) }() - case 3: try { try decoder.decodeSingularBoolField(value: &self.apMode) }() - case 4: try { try decoder.decodeSingularBoolField(value: &self.apHidden) }() - case 5: try { try decoder.decodeSingularBoolField(value: &self.enabled) }() + case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }() + case 2: try { try decoder.decodeSingularEnumField(value: &self.mode) }() + case 3: try { try decoder.decodeSingularStringField(value: &self.ssid) }() + case 4: try { try decoder.decodeSingularStringField(value: &self.psk) }() default: break } } } func traverse(visitor: inout V) throws { + if self.enabled != false { + try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1) + } + if self.mode != .client { + try visitor.visitSingularEnumField(value: self.mode, fieldNumber: 2) + } if !self.ssid.isEmpty { - try visitor.visitSingularStringField(value: self.ssid, fieldNumber: 1) + try visitor.visitSingularStringField(value: self.ssid, fieldNumber: 3) } if !self.psk.isEmpty { - try visitor.visitSingularStringField(value: self.psk, fieldNumber: 2) - } - if self.apMode != false { - try visitor.visitSingularBoolField(value: self.apMode, fieldNumber: 3) - } - if self.apHidden != false { - try visitor.visitSingularBoolField(value: self.apHidden, fieldNumber: 4) - } - if self.enabled != false { - try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 5) + try visitor.visitSingularStringField(value: self.psk, fieldNumber: 4) } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: Config.WiFiConfig, rhs: Config.WiFiConfig) -> Bool { + if lhs.enabled != rhs.enabled {return false} + if lhs.mode != rhs.mode {return false} if lhs.ssid != rhs.ssid {return false} if lhs.psk != rhs.psk {return false} - if lhs.apMode != rhs.apMode {return false} - if lhs.apHidden != rhs.apHidden {return false} - if lhs.enabled != rhs.enabled {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } +extension Config.WiFiConfig.WiFiMode: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "Client"), + 1: .same(proto: "AccessPoint"), + 2: .same(proto: "AccessPointHidden"), + ] +} + extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = Config.protoMessageName + ".DisplayConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .standard(proto: "screen_on_secs"), 2: .standard(proto: "gps_format"), 3: .standard(proto: "auto_screen_carousel_secs"), + 4: .standard(proto: "compass_north_top"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1451,6 +1505,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp case 1: try { try decoder.decodeSingularUInt32Field(value: &self.screenOnSecs) }() case 2: try { try decoder.decodeSingularEnumField(value: &self.gpsFormat) }() case 3: try { try decoder.decodeSingularUInt32Field(value: &self.autoScreenCarouselSecs) }() + case 4: try { try decoder.decodeSingularBoolField(value: &self.compassNorthTop) }() default: break } } @@ -1466,6 +1521,9 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp if self.autoScreenCarouselSecs != 0 { try visitor.visitSingularUInt32Field(value: self.autoScreenCarouselSecs, fieldNumber: 3) } + if self.compassNorthTop != false { + try visitor.visitSingularBoolField(value: self.compassNorthTop, fieldNumber: 4) + } try unknownFields.traverse(visitor: &visitor) } @@ -1473,6 +1531,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp if lhs.screenOnSecs != rhs.screenOnSecs {return false} if lhs.gpsFormat != rhs.gpsFormat {return false} if lhs.autoScreenCarouselSecs != rhs.autoScreenCarouselSecs {return false} + if lhs.compassNorthTop != rhs.compassNorthTop {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 1e69925e..41161dc1 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -16,15 +16,12 @@ struct Connect: View { @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings - @State private var showingVersionSheet = false - @State var initialLoad: Bool = true @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.30" + @State var minimumVersion = "1.3.36" @State var invalidVersion = false - var body: some View { @@ -267,23 +264,18 @@ struct Connect: View { InvalidVersion(errorText: "1.3 ALPHA PREVIEW this version of the app supports only version \(minimumVersion) and above. Your device has been disconnected.") } - - - .onChange(of: firmwareVersion) { iv in + + .onChange(of: (self.bleManager.connectedVersion)) { ic in - bleManager.disconnectPeripheral() - } - .onChange(of: self.bleManager.isConnected) { ic in - - firmwareVersion = bleManager.lastConnnectionVersion + firmwareVersion = self.bleManager.connectedVersion let supportedVersion = firmwareVersion == "0.0.0" || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedSame invalidVersion = !supportedVersion if invalidVersion { + bleManager.disconnectPeripheral() } - } .onAppear(perform: { diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index 0e44b935..1da29f14 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -7,6 +7,40 @@ import SwiftUI +enum WiFiModes: Int, CaseIterable, Identifiable { + + case client = 0 + case accessPoint = 1 + case accessPointHidden = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .client: + return "Client" + case .accessPoint: + return "Software Access Point" + case .accessPointHidden: + return "Software Access Point (Hidden)" + + } + } + } + func protoEnumValue() -> Config.WiFiConfig.WiFiMode { + + switch self { + + case .client: + return Config.WiFiConfig.WiFiMode.client + case .accessPoint: + return Config.WiFiConfig.WiFiMode.accessPoint + case .accessPointHidden: + return Config.WiFiConfig.WiFiMode.accessPointHidden + } + } +} + struct WiFiConfig: View { @Environment(\.managedObjectContext) var context @@ -19,12 +53,9 @@ struct WiFiConfig: View { @State var hasChanges: Bool = false @State var enabled = false - @State var ssid = "" @State var password = "" - - @State var apMode = false - @State var apHidden = false + @State var mode = 0 var body: some View { @@ -39,22 +70,35 @@ struct WiFiConfig: View { Toggle(isOn: $enabled) { - Label("Enable", systemImage: "wifi") + Label("Enabled", systemImage: "wifi") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + Picker("Mode", selection: $mode ) { + ForEach(WiFiModes.allCases) { lu in + Text(lu.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + } + Section(header: Text("SSID & Password")) { + HStack { Label("SSID", systemImage: "network") TextField("SSID", text: $ssid) .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) .onChange(of: ssid, perform: { value in let totalBytes = ssid.utf8.count // Only mess with the value if it is too big - if totalBytes > 30 { + if totalBytes > 32 { - let firstNBytes = Data(ssid.utf8.prefix(30)) + let firstNBytes = Data(ssid.utf8.prefix(32)) if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { @@ -66,20 +110,22 @@ struct WiFiConfig: View { .foregroundColor(.gray) } .keyboardType(.default) - .disableAutocorrection(true) + HStack { Label("Password", systemImage: "wallet.pass") TextField("Password", text: $password) .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) .onChange(of: password, perform: { value in let totalBytes = password.utf8.count // Only mess with the value if it is too big - if totalBytes > 60 { + if totalBytes > 63 { - let firstNBytes = Data(ssid.utf8.prefix(60)) + let firstNBytes = Data(ssid.utf8.prefix(63)) if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { @@ -91,28 +137,6 @@ struct WiFiConfig: View { .foregroundColor(.gray) } .keyboardType(.default) - .disableAutocorrection(true) - - } - Section(header: Text("Sofware Access Point")) { - - Text("WiFi uses client mode by default, if Software Access Point(AP) is on the SSID and password will be used to access the AP at meshtastic.local.") - .font(.caption) - - Toggle(isOn: $apMode) { - - Label("Soft AP Mode", systemImage: "externaldrive.fill.badge.wifi") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - - if apMode { - - Toggle(isOn: $apHidden) { - - Label("Hidden SSID", systemImage: "eye.slash") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - } } } .disabled(!(node != nil && node!.myInfo?.hasWifi ?? false)) @@ -141,8 +165,7 @@ struct WiFiConfig: View { wifi.enabled = self.enabled wifi.ssid = self.ssid wifi.psk = self.password - wifi.apMode = self.apMode - wifi.apHidden = self.apHidden + wifi.mode = WiFiModes(rawValue: self.mode)?.protoEnumValue() ?? WiFiModes.client.protoEnumValue() let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) @@ -174,9 +197,8 @@ struct WiFiConfig: View { self.enabled = (node!.wiFiConfig?.enabled ?? false) self.ssid = node!.wiFiConfig?.ssid ?? "" self.password = node!.wiFiConfig?.password ?? "" - self.apMode = (node!.wiFiConfig?.apMode ?? false) - self.apHidden = (node!.wiFiConfig?.apHidden ?? false) - + self.mode = Int(node!.wiFiConfig?.mode ?? 0) + self.hasChanges = false self.initialLoad = false } @@ -202,18 +224,11 @@ struct WiFiConfig: View { if newPassword != node!.wiFiConfig!.password { hasChanges = true } } } - .onChange(of: apMode) { newApMode in + .onChange(of: mode) { newMode in if node != nil && node!.wiFiConfig != nil { - if newApMode != node!.wiFiConfig!.apMode { hasChanges = true } - } - } - .onChange(of: apHidden) { newApHidden in - - if node != nil && node!.wiFiConfig != nil { - - if newApHidden != node!.wiFiConfig!.apHidden { hasChanges = true } + if newMode != node!.wiFiConfig!.mode { hasChanges = true } } } .navigationViewStyle(StackNavigationViewStyle()) From 19b646dd73fa7df120f8a89cff16dcd968f38150 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 6 Aug 2022 12:57:05 -0700 Subject: [PATCH 15/36] Bump version required --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 40df6cb7..0b7571d5 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -893,7 +893,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.30; + MARKETING_VERSION = 1.3.32; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -925,7 +925,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.30; + MARKETING_VERSION = 1.3.32; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From e44c3739deeb972f98049eec333d4503e9a4bbf5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 6 Aug 2022 12:57:36 -0700 Subject: [PATCH 16/36] Fix required version on bluetooth connect view --- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 41161dc1..5db1234b 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -20,7 +20,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.36" + @State var minimumVersion = "1.3.32" @State var invalidVersion = false var body: some View { From c8dc97acb60188a61149ed5cd273c37a87033bf4 Mon Sep 17 00:00:00 2001 From: Casey Date: Thu, 4 Aug 2022 23:03:46 -0600 Subject: [PATCH 17/36] invalidate existing timer before creating a new one --- Meshtastic/Helpers/BLEManager.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 636b0f35..2fd21994 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -168,6 +168,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.centralManager?.connect(peripheral) + // Invalidate any existing timer + self.timeoutTimer?.invalidate() + // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread let context = ["name": "@\(peripheral.name ?? "Unknown")"] From 602ff58db8ff4e819c3657288c4ba8b2d653a4d7 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 7 Aug 2022 08:31:39 -0700 Subject: [PATCH 18/36] Extra logging for config packets --- Meshtastic/Helpers/MeshPackets.swift | 67 ++++++++++++++++++- .../contents | 3 +- .../Views/Settings/Config/DisplayConfig.swift | 21 +++++- .../Config/Module/CannedMessagesConfig.swift | 16 ++--- 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 38a3b7a6..147308bf 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -21,6 +21,10 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont isDefault = true print("📟 Default Device config") + + } else { + + print("📟 Custom Device config") } let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -96,6 +100,10 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont isDefault = true print("🖥️ Default Display config") + + } else { + + print("🖥️ Custom Display config") } let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -116,12 +124,14 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont newDisplayConfig.screenOnSeconds = 0 newDisplayConfig.screenCarouselInterval = 0 newDisplayConfig.gpsFormat = 0 + newDisplayConfig.compassNorthTop = false } else { newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue) newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs) newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) + newDisplayConfig.compassNorthTop = config.display.compassNorthTop } fetchedNode[0].displayConfig = newDisplayConfig @@ -132,12 +142,14 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont fetchedNode[0].displayConfig?.screenOnSeconds = 0 fetchedNode[0].displayConfig?.screenCarouselInterval = 0 fetchedNode[0].displayConfig?.gpsFormat = 0 + fetchedNode[0].displayConfig?.compassNorthTop = false } else { fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue) fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs) fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) + fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop } } @@ -153,10 +165,15 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont let nsError = error as NSError print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Display Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data DisplayConfigEntity failed: \(nsError)") } } @@ -245,7 +262,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont } } else { - print("💥 No Nodes found in core data matching connected node number \(nodeNum)") + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Lora Config") } @@ -339,10 +356,15 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont let nsError = error as NSError print("💥 Error Updating Core Data PositionConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Position Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data PositionConfigEntity failed: \(nsError)") } } @@ -414,12 +436,17 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont context.rollback() let nsError = error as NSError - print("💥 Error Updating Core Data WifionfigEntity: \(nsError)") + print("💥 Error Updating Core Data WiFiConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save WiFi Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data WiFiConfigEntity failed: \(nsError)") } } } @@ -435,6 +462,9 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj isDefault = true print("🥫 Default Canned Message Module config") + } else { + + print("🥫 Custom Canned Message Module config") } let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -522,10 +552,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj let nsError = error as NSError print("💥 Error Updating Core Data CannedMessageConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Canned Message Module Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data CannedMessageConfigEntity failed: \(nsError)") } } @@ -537,6 +572,10 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj isDefault = true print("🚨 Default External Notifiation Module config") + + } else { + + print("🚨 Custom External Notifiation Module config") } let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -609,10 +648,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj let nsError = error as NSError print("💥 Error Updating Core Data ExternalNotificationConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save External Notifiation Module Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data ExternalNotificationConfigEntity failed: \(nsError)") } } @@ -682,10 +726,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj let nsError = error as NSError print("💥 Error Updating Core Data RangeTestConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Range Test Module Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data RangeTestConfigEntity failed: \(nsError)") } } @@ -776,10 +825,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj let nsError = error as NSError print("💥 Error Updating Core Data SerialConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Serial Module Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data SerialConfigEntity failed: \(nsError)") } } @@ -856,10 +910,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj let nsError = error as NSError print("💥 Error Updating Core Data TelemetryConfigEntity: \(nsError)") } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Telemetry Module Config") } } catch { + let nsError = error as NSError + print("💥 Fetching node for core data TelemetryConfigEntity failed: \(nsError)") } } } @@ -1208,6 +1267,7 @@ func adminAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb } + if meshLogging { MeshLogger.log("ℹ️ MESH PACKET received for Admin App UNHANDLED \(try! packet.jsonString())") } //PowerConfig @@ -1265,6 +1325,7 @@ func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)") } } + } else { print("💥 Empty POSITION_APP Packet") @@ -1487,7 +1548,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging: print("💥 Failed to save new MessageEntity \(nsError)") } do { - + print(newMessage) if messageSaved && (newMessage.toUser != nil && newMessage.toUser!.num == broadcastNodeNum || connectedNode == newMessage.toUser!.num) { // Create an iOS Notification for the received message and schedule it immediately diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents index eca24535..6f9b7000 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents @@ -22,6 +22,7 @@ + @@ -203,7 +204,7 @@ - + diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 6d4da661..f08c7dbf 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -134,13 +134,14 @@ struct DisplayConfig: View { @State var screenOnSeconds = 0 @State var screenCarouselInterval = 0 @State var gpsFormat = 0 + @State var compassNorthTop = false var body: some View { VStack { Form { - Section(header: Text("Timing")) { + Section(header: Text("Device Screen")) { Picker("Screen on for", selection: $screenOnSeconds ) { ForEach(ScreenOnIntervals.allCases) { soi in @@ -161,8 +162,15 @@ struct DisplayConfig: View { Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.") .font(.caption) - .listRowSeparator(.visible) + + Toggle(isOn: $compassNorthTop) { + Label("Always point north", systemImage: "location.north.circle") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("The compass heading on the screen outside of the circle will always point north.") + .font(.caption) + } Section(header: Text("Format")) { Picker("GPS Format", selection: $gpsFormat ) { @@ -203,6 +211,7 @@ struct DisplayConfig: View { dc.gpsFormat = GpsFormats(rawValue: gpsFormat)!.protoEnumValue() dc.screenOnSecs = UInt32(screenOnSeconds) dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval) + dc.compassNorthTop = compassNorthTop let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) @@ -234,6 +243,7 @@ struct DisplayConfig: View { self.gpsFormat = Int(node!.displayConfig?.gpsFormat ?? 0) self.screenOnSeconds = Int(node!.displayConfig?.screenOnSeconds ?? 0) self.screenCarouselInterval = Int(node!.displayConfig?.screenCarouselInterval ?? 0) + self.compassNorthTop = node!.displayConfig?.compassNorthTop ?? false self.hasChanges = false self.initialLoad = false } @@ -252,6 +262,13 @@ struct DisplayConfig: View { if newCarouselSecs != node!.displayConfig!.screenCarouselInterval { hasChanges = true } } } + .onChange(of: compassNorthTop) { newCompassNorthTop in + + if node != nil && node!.displayConfig != nil { + + if newCompassNorthTop != node!.displayConfig!.compassNorthTop { hasChanges = true } + } + } .onChange(of: gpsFormat) { newGpsFormat in if node != nil && node!.displayConfig != nil { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 3fe04782..950c77de 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -241,7 +241,7 @@ struct CannedMessagesConfig: View { Section(header: Text("Key Mapping")) { - Picker("inputbrokerEventCw", selection: $inputbrokerEventCw ) { + Picker("Clockwise Rotary Event", selection: $inputbrokerEventCw ) { ForEach(InputEventChars.allCases) { iec in Text(iec.description) } @@ -250,7 +250,7 @@ struct CannedMessagesConfig: View { .padding(.top, 10) .padding(.bottom, 10) - Picker("inputbrokerEventCcw", selection: $inputbrokerEventCcw ) { + Picker("Counter Clockwise Rotary Event", selection: $inputbrokerEventCcw ) { ForEach(InputEventChars.allCases) { iec in Text(iec.description) } @@ -259,7 +259,7 @@ struct CannedMessagesConfig: View { .padding(.top, 10) .padding(.bottom, 10) - Picker("inputBrokerEventPress", selection: $inputbrokerEventPress ) { + Picker("Encoder Press Event", selection: $inputbrokerEventPress ) { ForEach(InputEventChars.allCases) { iec in Text(iec.description) } @@ -379,9 +379,9 @@ struct CannedMessagesConfig: View { inputbrokerPinA = 4 inputbrokerPinB = 10 inputbrokerPinPress = 3 - inputbrokerEventCw = InputEventChars.keyNone.rawValue - inputbrokerEventCcw = InputEventChars.keyNone.rawValue - inputbrokerEventPress = InputEventChars.keyNone.rawValue + inputbrokerEventCw = InputEventChars.keyUp.rawValue + inputbrokerEventCcw = InputEventChars.keyDown.rawValue + inputbrokerEventPress = InputEventChars.keySelect.rawValue } else if newPreset == 2 { @@ -391,8 +391,8 @@ struct CannedMessagesConfig: View { inputbrokerPinA = 25 inputbrokerPinB = 39 inputbrokerPinPress = 36 - inputbrokerEventCw = InputEventChars.keyUp.rawValue - inputbrokerEventCcw = InputEventChars.keyDown.rawValue + inputbrokerEventCw = InputEventChars.keyLeft.rawValue + inputbrokerEventCcw = InputEventChars.keyRight.rawValue inputbrokerEventPress = InputEventChars.keySelect.rawValue } From 2e36ac355f280d0f43c18517457c1dc2ad2494d1 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 7 Aug 2022 14:18:17 -0700 Subject: [PATCH 19/36] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 0b7571d5..8844f769 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -893,7 +893,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.32; + MARKETING_VERSION = 1.3.33; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -925,7 +925,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.32; + MARKETING_VERSION = 1.3.33; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From 56da04827af16fca343665cf205ac483931d34b2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 11 Aug 2022 23:34:09 -0700 Subject: [PATCH 20/36] New protos and assorted bug fixes --- Meshtastic.xcodeproj/project.pbxproj | 4 + Meshtastic/Helpers/BLEManager.swift | 130 ++++++++++++++---- Meshtastic/Helpers/MeshPackets.swift | 40 +++--- Meshtastic/Protobufs/admin.pb.swift | 95 +++++++++++++ Meshtastic/Protobufs/config.pb.swift | 4 +- Meshtastic/Protobufs/mesh.pb.swift | 20 +++ Meshtastic/Views/Nodes/LocationHistory.swift | 2 +- Meshtastic/Views/Nodes/TelemetryLog.swift | 2 +- .../Views/Settings/Config/DeviceConfig.swift | 2 +- .../Views/Settings/Config/DisplayConfig.swift | 2 +- .../Views/Settings/Config/LoRaConfig.swift | 2 +- .../Config/Module/CannedMessagesConfig.swift | 6 +- .../Module/ExternalNotificationConfig.swift | 2 +- .../Config/Module/RangeTestConfig.swift | 2 +- .../Settings/Config/Module/SerialConfig.swift | 2 +- .../Config/Module/TelemetryConfig.swift | 2 +- .../Settings/Config/PositionConfig.swift | 2 +- .../Views/Settings/Config/WiFiConfig.swift | 2 +- Meshtastic/Views/Settings/UserConfig.swift | 2 +- gen_protos.sh | 2 +- 20 files changed, 256 insertions(+), 69 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 8844f769..33b13253 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -69,6 +69,7 @@ DDAF8C6926ED0D070058C060 /* deviceonly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6826ED0D070058C060 /* deviceonly.pb.swift */; }; DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6D26ED19040058C060 /* Extensions.swift */; }; DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */; }; + DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; }; DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; }; DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; }; @@ -169,6 +170,7 @@ DDAF8C6D26ED19040058C060 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = telemetry.pb.swift; sourceTree = ""; }; DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 3.xcdatamodel"; sourceTree = ""; }; + DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; @@ -326,6 +328,7 @@ DDAF8C5626ED07740058C060 /* Protobufs */ = { isa = PBXGroup; children = ( + DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */, DDCFF600285453A7005FA625 /* localonly.pb.swift */, DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */, DDAF8C6126ED0A230058C060 /* admin.pb.swift */, @@ -690,6 +693,7 @@ DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */, C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */, DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, + DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */, DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */, DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 0962dc11..7564cb3f 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -145,7 +145,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if meshLoggingEnabled { MeshLogger.log(self.lastConnectionError + " This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer.") } self.timeoutTimerCount = 0 - self.timeoutTimer?.invalidate() + if self.timeoutTimer != nil { + + self.timeoutTimer!.invalidate() + } } else { @@ -171,7 +174,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.centralManager?.connect(peripheral) // Invalidate any existing timer - self.timeoutTimer?.invalidate() + if self.timeoutTimer != nil { + + self.timeoutTimer!.invalidate() + } // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread @@ -228,8 +234,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Invalidate and reset connection timer count, remove any connection errors self.lastConnectionError = "" - self.timeoutTimer!.invalidate() self.timeoutTimerCount = 0 + if self.timeoutTimer != nil { + + self.timeoutTimer!.invalidate() + } // Map the peripheral to the connectedNode and connectedPeripheral ObservedObjects connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first @@ -467,7 +476,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if decodedInfo.myInfo.isInitialized && decodedInfo.myInfo.myNodeNum > 0 { let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".") - var version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))] + let version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))] nowKnown = true connectedVersion = String(version) @@ -594,6 +603,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if userSettings?.provideLocation ?? false { if self.positionTimer != nil { + self.positionTimer!.invalidate() } let context = ["name": "@\(peripheral.name ?? "Unknown")"] @@ -744,6 +754,67 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return success } + public func sendLocation(destNum: Int64) -> Bool { + + var success = false + + let fromNodeNum = connectedPeripheral.num + + if fromNodeNum <= 0 || (LocationHelper.currentLocation.latitude == LocationHelper.DefaultLocation.latitude && LocationHelper.currentLocation.longitude == LocationHelper.DefaultLocation.longitude) { + + return false + } + + //let fetchNode: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + //fetchNode.predicate = NSPredicate(format: "num == %lld", fromNodeNum) + + //do { + + // let fetchedNode = try context?.fetch(fetchNode) as! [NodeInfoEntity] + + + // if fetchedNode.count == 1 { + + var waypointPacket = Location() + waypointPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) + waypointPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) + waypointPacket.expire = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970) + waypointPacket.name = "Test Waypoint" + + + var meshPacket = MeshPacket() + meshPacket.to = UInt32(destNum) + meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num + meshPacket.wantAck = true + + var dataMessage = DataMessage() + dataMessage.payload = try! waypointPacket.serializedData() + dataMessage.portnum = PortNum.waypointApp + + meshPacket.decoded = dataMessage + + var toRadio: ToRadio! + toRadio = ToRadio() + toRadio.packet = meshPacket + let binaryData: Data = try! toRadio.serializedData() + + if meshLoggingEnabled { MeshLogger.log("📍 Sent a Location Packet from the Apple device GPS to node: \(fromNodeNum)") } + + if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + + connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) + success = true + + } + // } + + //} catch { + // success = false + //} + + return success + } + public func sendPosition(destNum: Int64, wantResponse: Bool) -> Bool { var success = false @@ -976,7 +1047,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } - public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 { + public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setOwner = config @@ -986,16 +1057,16 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveDeviceConfig(config: Config.DeviceConfig, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.device = config @@ -1015,7 +1086,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveDisplayConfig(config: Config.DisplayConfig, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.display = config @@ -1044,7 +1115,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.lora = config @@ -1074,7 +1145,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func savePositionConfig(config: Config.PositionConfig, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.position = config @@ -1103,7 +1174,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveWiFiConfig(config: Config.WiFiConfig, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.wifi = config @@ -1132,7 +1203,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity, wantAck: Bool) -> Int64 { var adminPacket = AdminMessage() adminPacket.setModuleConfig.cannedMessage = config @@ -1161,7 +1232,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveExternalNotificationModuleConfig(config: ModuleConfig.ExternalNotificationConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setModuleConfig.externalNotification = config @@ -1267,7 +1339,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setModuleConfig.rangeTest = config @@ -1295,7 +1367,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveSerialModuleConfig(config: ModuleConfig.SerialConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setModuleConfig.serial = config @@ -1323,7 +1395,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveTelemetryModuleConfig(config: ModuleConfig.TelemetryConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setModuleConfig.telemetry = config @@ -1352,7 +1424,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..=4.1) static func ==(lhs: AdminMessage.OneOf_Variant, rhs: AdminMessage.OneOf_Variant) -> Bool { @@ -522,6 +561,10 @@ struct AdminMessage { guard case .confirmSetModuleConfig(let l) = lhs, case .confirmSetModuleConfig(let r) = rhs else { preconditionFailure() } return l == r }() + case (.getAllChannelRequest, .getAllChannelRequest): return { + guard case .getAllChannelRequest(let l) = lhs, case .getAllChannelRequest(let r) = rhs else { preconditionFailure() } + return l == r + }() case (.confirmSetChannel, .confirmSetChannel): return { guard case .confirmSetChannel(let l) = lhs, case .confirmSetChannel(let r) = rhs else { preconditionFailure() } return l == r @@ -590,6 +633,14 @@ struct AdminMessage { guard case .shutdownSeconds(let l) = lhs, case .shutdownSeconds(let r) = rhs else { preconditionFailure() } return l == r }() + case (.getDeviceMetadataRequest, .getDeviceMetadataRequest): return { + guard case .getDeviceMetadataRequest(let l) = lhs, case .getDeviceMetadataRequest(let r) = rhs else { preconditionFailure() } + return l == r + }() + case (.getDeviceMetadataResponse, .getDeviceMetadataResponse): return { + guard case .getDeviceMetadataResponse(let l) = lhs, case .getDeviceMetadataResponse(let r) = rhs else { preconditionFailure() } + return l == r + }() default: return false } } @@ -780,6 +831,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 15: .standard(proto: "get_module_config_response"), 16: .standard(proto: "set_module_config"), 17: .standard(proto: "confirm_set_module_config"), + 18: .standard(proto: "get_all_channel_request"), 32: .standard(proto: "confirm_set_channel"), 33: .standard(proto: "confirm_set_radio"), 34: .standard(proto: "exit_simulator"), @@ -797,6 +849,8 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 46: .standard(proto: "set_canned_message_module_part3"), 47: .standard(proto: "set_canned_message_module_part4"), 51: .standard(proto: "shutdown_seconds"), + 52: .standard(proto: "get_device_metadata_request"), + 53: .standard(proto: "get_device_metadata_response"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -957,6 +1011,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat self.variant = .confirmSetModuleConfig(v) } }() + case 18: try { + var v: Bool? + try decoder.decodeSingularBoolField(value: &v) + if let v = v { + if self.variant != nil {try decoder.handleConflictingOneOf()} + self.variant = .getAllChannelRequest(v) + } + }() case 32: try { var v: Bool? try decoder.decodeSingularBoolField(value: &v) @@ -1093,6 +1155,27 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat self.variant = .shutdownSeconds(v) } }() + case 52: try { + var v: UInt32? + try decoder.decodeSingularUInt32Field(value: &v) + if let v = v { + if self.variant != nil {try decoder.handleConflictingOneOf()} + self.variant = .getDeviceMetadataRequest(v) + } + }() + case 53: try { + var v: DeviceMetadata? + var hadOneofValue = false + if let current = self.variant { + hadOneofValue = true + if case .getDeviceMetadataResponse(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.variant = .getDeviceMetadataResponse(v) + } + }() default: break } } @@ -1160,6 +1243,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .confirmSetModuleConfig(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularBoolField(value: v, fieldNumber: 17) }() + case .getAllChannelRequest?: try { + guard case .getAllChannelRequest(let v)? = self.variant else { preconditionFailure() } + try visitor.visitSingularBoolField(value: v, fieldNumber: 18) + }() case .confirmSetChannel?: try { guard case .confirmSetChannel(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularBoolField(value: v, fieldNumber: 32) @@ -1228,6 +1315,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .shutdownSeconds(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularInt32Field(value: v, fieldNumber: 51) }() + case .getDeviceMetadataRequest?: try { + guard case .getDeviceMetadataRequest(let v)? = self.variant else { preconditionFailure() } + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 52) + }() + case .getDeviceMetadataResponse?: try { + guard case .getDeviceMetadataResponse(let v)? = self.variant else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 53) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index 7d003b01..806ee401 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -359,7 +359,7 @@ struct Config { /// /// Power Config\ - /// See [power management](/docs/software/other/power) for additional power management state machine option details. + /// See [Power Config](/docs/settings/config/power) for additional power config details. struct PowerConfig { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -573,7 +573,7 @@ struct Config { } /// - /// TODO: REPLACE + /// Display Config struct DisplayConfig { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for diff --git a/Meshtastic/Protobufs/mesh.pb.swift b/Meshtastic/Protobufs/mesh.pb.swift index d528fb7e..ebb8d747 100644 --- a/Meshtastic/Protobufs/mesh.pb.swift +++ b/Meshtastic/Protobufs/mesh.pb.swift @@ -1127,6 +1127,14 @@ struct Location { /// If true, only allow the original sender to update the location. var locked: Bool = false + /// + /// Name of the location - max 30 chars + var name: String = String() + + ///* + /// Description of the location - max 100 chars + var description_p: String = String() + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -2766,6 +2774,8 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 3: .standard(proto: "longitude_i"), 4: .same(proto: "expire"), 5: .same(proto: "locked"), + 6: .same(proto: "name"), + 7: .same(proto: "description"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2779,6 +2789,8 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB case 3: try { try decoder.decodeSingularSFixed32Field(value: &self.longitudeI) }() case 4: try { try decoder.decodeSingularUInt32Field(value: &self.expire) }() case 5: try { try decoder.decodeSingularBoolField(value: &self.locked) }() + case 6: try { try decoder.decodeSingularStringField(value: &self.name) }() + case 7: try { try decoder.decodeSingularStringField(value: &self.description_p) }() default: break } } @@ -2800,6 +2812,12 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if self.locked != false { try visitor.visitSingularBoolField(value: self.locked, fieldNumber: 5) } + if !self.name.isEmpty { + try visitor.visitSingularStringField(value: self.name, fieldNumber: 6) + } + if !self.description_p.isEmpty { + try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 7) + } try unknownFields.traverse(visitor: &visitor) } @@ -2809,6 +2827,8 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB if lhs.longitudeI != rhs.longitudeI {return false} if lhs.expire != rhs.expire {return false} if lhs.locked != rhs.locked {return false} + if lhs.name != rhs.name {return false} + if lhs.description_p != rhs.description_p {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Views/Nodes/LocationHistory.swift b/Meshtastic/Views/Nodes/LocationHistory.swift index 692d2827..0d132998 100644 --- a/Meshtastic/Views/Nodes/LocationHistory.swift +++ b/Meshtastic/Views/Nodes/LocationHistory.swift @@ -22,7 +22,7 @@ struct LocationHistory: View { List { - ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in + ForEach(node.positions!.reversed() as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in VStack { diff --git a/Meshtastic/Views/Nodes/TelemetryLog.swift b/Meshtastic/Views/Nodes/TelemetryLog.swift index d60f72c5..43969616 100644 --- a/Meshtastic/Views/Nodes/TelemetryLog.swift +++ b/Meshtastic/Views/Nodes/TelemetryLog.swift @@ -20,7 +20,7 @@ struct TelemetryLog: View { List { - ForEach(node.telemetries!.array as! [TelemetryEntity], id: \.self) { (tel: TelemetryEntity) in + ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (tel: TelemetryEntity) in VStack (alignment: .leading) { diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 32d2cff2..b0b137a6 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -124,7 +124,7 @@ struct DeviceConfig: View { dc.serialDisabled = !serialEnabled dc.debugLogEnabled = debugLogEnabled - let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index f08c7dbf..6fcb7c35 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -213,7 +213,7 @@ struct DisplayConfig: View { dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval) dc.compassNorthTop = compassNorthTop - let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index d3a708e5..d89a0542 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -261,7 +261,7 @@ struct LoRaConfig: View { lc.region = RegionCodes(rawValue: region)!.protoEnumValue() lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue() - let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 950c77de..9534b7dd 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -320,7 +320,7 @@ struct CannedMessagesConfig: View { cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue() cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue() - let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true @@ -391,8 +391,8 @@ struct CannedMessagesConfig: View { inputbrokerPinA = 25 inputbrokerPinB = 39 inputbrokerPinPress = 36 - inputbrokerEventCw = InputEventChars.keyLeft.rawValue - inputbrokerEventCcw = InputEventChars.keyRight.rawValue + inputbrokerEventCw = InputEventChars.keyUp.rawValue + inputbrokerEventCcw = InputEventChars.keyDown.rawValue inputbrokerEventPress = InputEventChars.keySelect.rawValue } diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 3769d4f6..7ef699ed 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -159,7 +159,7 @@ struct ExternalNotificationConfig: View { enc.output = UInt32(output) enc.outputMs = UInt32(outputMilliseconds) - let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0{ diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 077b4d14..642a12ec 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -113,7 +113,7 @@ struct RangeTestConfig: View { rtc.save = save rtc.sender = UInt32(sender) - let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index b4432798..99f183cf 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -303,7 +303,7 @@ struct SerialConfig: View { sc.timeout = UInt32(timeout) sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue() - let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 14123fac..275a70b0 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -203,7 +203,7 @@ struct TelemetryConfig: View { tc.environmentScreenEnabled = environmentScreenEnabled tc.environmentDisplayFahrenheit = environmentDisplayFahrenheit - let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index ff5ece92..42ca784d 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -323,7 +323,7 @@ struct PositionConfig: View { pc.gpsAttemptTime = UInt32(gpsAttemptTime) pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds) - let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0{ diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index 1da29f14..e7cfb5eb 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -167,7 +167,7 @@ struct WiFiConfig: View { wifi.psk = self.password wifi.mode = WiFiModes(rawValue: self.mode)?.protoEnumValue() ?? WiFiModes.client.protoEnumValue() - let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index 6d9d304d..accc224b 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -113,7 +113,7 @@ struct UserConfig: View { u.shortName = shortName u.longName = longName - let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!, wantResponse: true) + let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!, wantAck: true) if adminMessageId > 0 { diff --git a/gen_protos.sh b/gen_protos.sh index 968e1e39..94c685c2 100755 --- a/gen_protos.sh +++ b/gen_protos.sh @@ -15,7 +15,7 @@ fi pdir=$(realpath "../Meshtastic-protobufs") sdir=$(realpath "./Meshtastic/Protobufs") echo "pdir:$pdir sdir:$sdir" -pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto config.proto deviceonly.proto localonly.proto mesh.proto module_config.proto mqtt.proto portnums.proto remote_hardware.proto +pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto config.proto device_metadata.proto deviceonly.proto localonly.proto mesh.proto module_config.proto mqtt.proto portnums.proto remote_hardware.proto storeforward.proto telemetry.proto" for pf in $pfiles do From d7819b74040198bc10b0bd0182064750edb2af3c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 12 Aug 2022 08:58:10 -0700 Subject: [PATCH 21/36] Set wantAck to true for all admin messages, hook up waypoint to position in messages --- Meshtastic/Helpers/BLEManager.swift | 203 ++++++++---------- Meshtastic/Helpers/MeshPackets.swift | 2 +- .../Views/Messages/UserMessageList.swift | 4 +- Meshtastic/Views/Nodes/NodeDetail.swift | 4 +- .../Views/Settings/Config/DeviceConfig.swift | 4 +- .../Views/Settings/Config/DisplayConfig.swift | 2 +- .../Views/Settings/Config/LoRaConfig.swift | 2 +- .../Config/Module/CannedMessagesConfig.swift | 2 +- .../Settings/Config/PositionConfig.swift | 2 +- .../Views/Settings/Config/WiFiConfig.swift | 2 +- Meshtastic/Views/Settings/UserConfig.swift | 2 +- 11 files changed, 100 insertions(+), 129 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 7564cb3f..14a63a6b 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -754,7 +754,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return success } - public func sendLocation(destNum: Int64) -> Bool { + public func sendLocation(destNum: Int64, wantAck: Bool) -> Bool { var success = false @@ -764,58 +764,45 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } + + var waypointPacket = Location() + waypointPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) + waypointPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) - //let fetchNode: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") - //fetchNode.predicate = NSPredicate(format: "num == %lld", fromNodeNum) + let oneWeekFromNow = Calendar.current.date(byAdding: .day, value: 7, to: Date()) + waypointPacket.expire = UInt32(oneWeekFromNow!.timeIntervalSince1970) + waypointPacket.name = "Test Waypoint" + + + var meshPacket = MeshPacket() + meshPacket.to = UInt32(destNum) + meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num + meshPacket.wantAck = true//wantAck + + var dataMessage = DataMessage() + dataMessage.payload = try! waypointPacket.serializedData() + dataMessage.portnum = PortNum.waypointApp + + meshPacket.decoded = dataMessage - //do { - - // let fetchedNode = try context?.fetch(fetchNode) as! [NodeInfoEntity] + var toRadio: ToRadio! + toRadio = ToRadio() + toRadio.packet = meshPacket + let binaryData: Data = try! toRadio.serializedData() + + if meshLoggingEnabled { MeshLogger.log("📍 Sent a Location Packet from the Apple device GPS to node: \(fromNodeNum)") } + + if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { + connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) + success = true - // if fetchedNode.count == 1 { - - var waypointPacket = Location() - waypointPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) - waypointPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) - waypointPacket.expire = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970) - waypointPacket.name = "Test Waypoint" - - - var meshPacket = MeshPacket() - meshPacket.to = UInt32(destNum) - meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num - meshPacket.wantAck = true - - var dataMessage = DataMessage() - dataMessage.payload = try! waypointPacket.serializedData() - dataMessage.portnum = PortNum.waypointApp - - meshPacket.decoded = dataMessage - - var toRadio: ToRadio! - toRadio = ToRadio() - toRadio.packet = meshPacket - let binaryData: Data = try! toRadio.serializedData() - - if meshLoggingEnabled { MeshLogger.log("📍 Sent a Location Packet from the Apple device GPS to node: \(fromNodeNum)") } - - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { - - connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) - success = true - - } - // } - - //} catch { - // success = false - //} + } return success } - public func sendPosition(destNum: Int64, wantResponse: Bool) -> Bool { + public func sendPosition(destNum: Int64, wantAck: Bool) -> Bool { var success = false @@ -825,58 +812,43 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } + + var positionPacket = Position() + positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) + positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) + positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970) + positionPacket.altitude = Int32(LocationHelper.currentAltitude) - let fetchNode: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") - fetchNode.predicate = NSPredicate(format: "num == %lld", fromNodeNum) - - do { - - let fetchedNode = try context?.fetch(fetchNode) as! [NodeInfoEntity] + // Get Errors without some speed + if LocationHelper.currentSpeed >= 5 { + positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed) + positionPacket.groundTrack = UInt32(LocationHelper.currentHeading) + } + + var meshPacket = MeshPacket() + meshPacket.to = UInt32(destNum) + meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num + meshPacket.wantAck = wantAck + + var dataMessage = DataMessage() + dataMessage.payload = try! positionPacket.serializedData() + dataMessage.portnum = PortNum.positionApp + + meshPacket.decoded = dataMessage - if fetchedNode.count == 1 { - - var positionPacket = Position() - positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) - positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) - positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970) - positionPacket.altitude = Int32(LocationHelper.currentAltitude) - - // Get Errors without some speed - if LocationHelper.currentSpeed >= 5 { - - positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed) - positionPacket.groundTrack = UInt32(LocationHelper.currentHeading) - } - - var meshPacket = MeshPacket() - meshPacket.to = UInt32(destNum) - meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num - meshPacket.wantAck = wantResponse - - var dataMessage = DataMessage() - dataMessage.payload = try! positionPacket.serializedData() - dataMessage.portnum = PortNum.positionApp - - meshPacket.decoded = dataMessage - - var toRadio: ToRadio! - toRadio = ToRadio() - toRadio.packet = meshPacket - let binaryData: Data = try! toRadio.serializedData() - - if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet from the Apple device GPS to node: \(fromNodeNum)") } - - if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { - - connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) - success = true - - } - } + var toRadio: ToRadio! + toRadio = ToRadio() + toRadio.packet = meshPacket + let binaryData: Data = try! toRadio.serializedData() + + if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet from the Apple device GPS to node: \(fromNodeNum)") } + + if connectedPeripheral!.peripheral.state == CBPeripheralState.connected { - } catch { - success = false + connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) + success = true + } return success @@ -890,7 +862,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Send a position out to the mesh if "share location with the mesh" is enabled in settings if userSettings!.provideLocation { - let success = sendPosition(destNum: connectedPeripheral.num, wantResponse: false) + let success = sendPosition(destNum: connectedPeripheral.num, wantAck: false) if !success { print("Failed to send positon to device") @@ -900,7 +872,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } } - public func sendShutdown(destNum: Int64, wantResponse: Bool) -> Bool { + public func sendShutdown(destNum: Int64) -> Bool { var adminPacket = AdminMessage() adminPacket.shutdownSeconds = 10 @@ -910,7 +882,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + public func sendReboot(destNum: Int64) -> Bool { var adminPacket = AdminMessage() adminPacket.rebootSeconds = 10 @@ -958,7 +930,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Bool { + public func sendFactoryReset(destNum: Int64) -> Bool { var deviceConfig = Config.DeviceConfig() deviceConfig.factoryReset = true @@ -1010,7 +982,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setOwner = config @@ -1057,7 +1029,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveDeviceConfig(config: Config.DeviceConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.device = config @@ -1086,7 +1058,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveDisplayConfig(config: Config.DisplayConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.display = config @@ -1115,7 +1087,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.lora = config @@ -1145,7 +1117,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func savePositionConfig(config: Config.PositionConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.position = config @@ -1174,7 +1146,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveWiFiConfig(config: Config.WiFiConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setConfig.wifi = config @@ -1203,7 +1175,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(connectedPeripheral.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { + public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { var adminPacket = AdminMessage() adminPacket.setModuleConfig.cannedMessage = config @@ -1232,7 +1204,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() - adminPacket.setCannedMessageModulePart1 = messages var meshPacket: MeshPacket = MeshPacket() @@ -1261,7 +1232,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph meshPacket.from = 0 //UInt32(fromUser.num) meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. 0 { @@ -153,7 +153,7 @@ struct DeviceConfig: View { ) { Button("Erase all device settings?", role: .destructive) { - if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: true) { + if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num) { print("Factory Reset Failed") } diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 6fcb7c35..13d9ab13 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -213,7 +213,7 @@ struct DisplayConfig: View { dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval) dc.compassNorthTop = compassNorthTop - let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) + let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index d89a0542..9b30f94e 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -261,7 +261,7 @@ struct LoRaConfig: View { lc.region = RegionCodes(rawValue: region)!.protoEnumValue() lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue() - let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) + let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index 9534b7dd..eba68209 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -320,7 +320,7 @@ struct CannedMessagesConfig: View { cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue() cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue() - let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) + let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { // Should show a saved successfully alert once I know that to be true diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 42ca784d..a2348bea 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -323,7 +323,7 @@ struct PositionConfig: View { pc.gpsAttemptTime = UInt32(gpsAttemptTime) pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds) - let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!, wantAck: true) + let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0{ diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index e7cfb5eb..e322a72a 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -167,7 +167,7 @@ struct WiFiConfig: View { wifi.psk = self.password wifi.mode = WiFiModes(rawValue: self.mode)?.protoEnumValue() ?? WiFiModes.client.protoEnumValue() - let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!, wantAck: true) + let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index accc224b..3747c71c 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -113,7 +113,7 @@ struct UserConfig: View { u.shortName = shortName u.longName = longName - let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!, wantAck: true) + let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { From d3fb8eb9026ba90596927de374c1de723fdebfff Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 12 Aug 2022 09:21:18 -0700 Subject: [PATCH 22/36] Bump version, include new proto file --- Meshtastic/Protobufs/device_metadata.pb.swift | 85 +++++++++++++++++++ Meshtastic/Views/Bluetooth/Connect.swift | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 Meshtastic/Protobufs/device_metadata.pb.swift diff --git a/Meshtastic/Protobufs/device_metadata.pb.swift b/Meshtastic/Protobufs/device_metadata.pb.swift new file mode 100644 index 00000000..5c6ed312 --- /dev/null +++ b/Meshtastic/Protobufs/device_metadata.pb.swift @@ -0,0 +1,85 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: device_metadata.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// +/// Device metadata response +struct DeviceMetadata { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Device firmware version string + var firmwareVersion: String = String() + + /// + /// Device state version + var deviceStateVersion: UInt32 = 0 + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension DeviceMetadata: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "DeviceMetadata" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "firmware_version"), + 2: .standard(proto: "device_state_version"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.firmwareVersion) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.deviceStateVersion) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.firmwareVersion.isEmpty { + try visitor.visitSingularStringField(value: self.firmwareVersion, fieldNumber: 1) + } + if self.deviceStateVersion != 0 { + try visitor.visitSingularUInt32Field(value: self.deviceStateVersion, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: DeviceMetadata, rhs: DeviceMetadata) -> Bool { + if lhs.firmwareVersion != rhs.firmwareVersion {return false} + if lhs.deviceStateVersion != rhs.deviceStateVersion {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 5db1234b..41161dc1 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -20,7 +20,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.32" + @State var minimumVersion = "1.3.36" @State var invalidVersion = false var body: some View { From ef401d45005322622d71b6cde78bd9ca6beace11 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 12 Aug 2022 10:05:03 -0700 Subject: [PATCH 23/36] Bump version in project --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 33b13253..80f324a7 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -897,7 +897,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.33; + MARKETING_VERSION = 1.3.36; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -929,7 +929,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.33; + MARKETING_VERSION = 1.3.36; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From e32fbef859989af5554a48df5552f46a85dcc723 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 14 Aug 2022 22:42:26 -0700 Subject: [PATCH 24/36] Fix notification crash, update versions to require 1.3.38 --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- Meshtastic/Helpers/MeshPackets.swift | 30 ++++++++++++++---------- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 80f324a7..f8cf8487 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -897,7 +897,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.36; + MARKETING_VERSION = 1.3.38; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -929,7 +929,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.36; + MARKETING_VERSION = 1.3.38; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 56c29885..dbc7eb0c 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1542,20 +1542,24 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging: if messageSaved { //&& (newMessage.toUser != nil && newMessage.toUser!.num == broadcastNodeNum || connectedNode == newMessage.toUser!.num) { - // Create an iOS Notification for the received message and schedule it immediately - let manager = LocalNotificationManager() - - manager.notifications = [ - Notification( - id: ("notification.id.\(newMessage.messageId)"), - title: "\(newMessage.fromUser?.longName ?? "Unknown")", - subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")", - content: messageText) - ] - - manager.schedule() - if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown")") } + if newMessage.fromUser != nil { + + // Create an iOS Notification for the received message and schedule it immediately + let manager = LocalNotificationManager() + + manager.notifications = [ + Notification( + id: ("notification.id.\(newMessage.messageId)"), + title: "\(newMessage.fromUser?.longName ?? "Unknown")", + subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")", + content: messageText) + ] + + manager.schedule() + + if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown")") } + } } } catch { diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 41161dc1..07ceaffd 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -20,7 +20,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.36" + @State var minimumVersion = "1.3.38" @State var invalidVersion = false var body: some View { From ed437a450b5336e2d62509a34ae7bb293ba28de6 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 15 Aug 2022 19:40:46 -0700 Subject: [PATCH 25/36] Photo updates --- Meshtastic/Helpers/BLEManager.swift | 4 +- Meshtastic/Protobufs/admin.pb.swift | 350 +++---------------- Meshtastic/Protobufs/cannedmessages.pb.swift | 42 +-- Meshtastic/Protobufs/config.pb.swift | 156 +++++++++ Meshtastic/Protobufs/localonly.pb.swift | 25 +- Meshtastic/Protobufs/mesh.pb.swift | 198 +++-------- 6 files changed, 287 insertions(+), 488 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 14a63a6b..a6a78c44 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1225,7 +1225,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph public func saveCannedMessageModuleMessages(messages: String, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 { var adminPacket = AdminMessage() - adminPacket.setCannedMessageModulePart1 = messages + adminPacket.setCannedMessageModuleMessages = messages var meshPacket: MeshPacket = MeshPacket() meshPacket.to = UInt32(toUser.num) @@ -1254,7 +1254,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph public func getCannedMessageModuleMessages(destNum: Int64, wantResponse: Bool) -> Bool { var adminPacket = AdminMessage() - adminPacket.getCannedMessageModulePart1Request = true + adminPacket.getCannedMessageModuleMessagesRequest = true var meshPacket: MeshPacket = MeshPacket() meshPacket.to = UInt32(connectedPeripheral.num) diff --git a/Meshtastic/Protobufs/admin.pb.swift b/Meshtastic/Protobufs/admin.pb.swift index c09a3a20..636d5908 100644 --- a/Meshtastic/Protobufs/admin.pb.swift +++ b/Meshtastic/Protobufs/admin.pb.swift @@ -233,123 +233,33 @@ struct AdminMessage { } /// - /// Get the Canned Message Module message part1 in the response to this message. - var getCannedMessageModulePart1Request: Bool { + /// Get the Canned Message Module messages in the response to this message. + var getCannedMessageModuleMessagesRequest: Bool { get { - if case .getCannedMessageModulePart1Request(let v)? = variant {return v} + if case .getCannedMessageModuleMessagesRequest(let v)? = variant {return v} return false } - set {variant = .getCannedMessageModulePart1Request(newValue)} + set {variant = .getCannedMessageModuleMessagesRequest(newValue)} } /// - /// TODO: REPLACE - var getCannedMessageModulePart1Response: String { + /// Get the Canned Message Module messages in the response to this message. + var getCannedMessageModuleMessagesResponse: String { get { - if case .getCannedMessageModulePart1Response(let v)? = variant {return v} + if case .getCannedMessageModuleMessagesResponse(let v)? = variant {return v} return String() } - set {variant = .getCannedMessageModulePart1Response(newValue)} + set {variant = .getCannedMessageModuleMessagesResponse(newValue)} } /// - /// Get the Canned Message Module message part2 in the response to this message. - var getCannedMessageModulePart2Request: Bool { + /// Set the Canned Message Module messages text. + var setCannedMessageModuleMessages: String { get { - if case .getCannedMessageModulePart2Request(let v)? = variant {return v} - return false - } - set {variant = .getCannedMessageModulePart2Request(newValue)} - } - - /// - /// TODO: REPLACE - var getCannedMessageModulePart2Response: String { - get { - if case .getCannedMessageModulePart2Response(let v)? = variant {return v} + if case .setCannedMessageModuleMessages(let v)? = variant {return v} return String() } - set {variant = .getCannedMessageModulePart2Response(newValue)} - } - - /// - /// Get the Canned Message Module message part3 in the response to this message. - var getCannedMessageModulePart3Request: Bool { - get { - if case .getCannedMessageModulePart3Request(let v)? = variant {return v} - return false - } - set {variant = .getCannedMessageModulePart3Request(newValue)} - } - - /// - /// TODO: REPLACE - var getCannedMessageModulePart3Response: String { - get { - if case .getCannedMessageModulePart3Response(let v)? = variant {return v} - return String() - } - set {variant = .getCannedMessageModulePart3Response(newValue)} - } - - /// - /// Get the Canned Message Module message part4 in the response to this message. - var getCannedMessageModulePart4Request: Bool { - get { - if case .getCannedMessageModulePart4Request(let v)? = variant {return v} - return false - } - set {variant = .getCannedMessageModulePart4Request(newValue)} - } - - /// - /// TODO: REPLACE - var getCannedMessageModulePart4Response: String { - get { - if case .getCannedMessageModulePart4Response(let v)? = variant {return v} - return String() - } - set {variant = .getCannedMessageModulePart4Response(newValue)} - } - - /// - /// Set the canned message module part 1 text. - var setCannedMessageModulePart1: String { - get { - if case .setCannedMessageModulePart1(let v)? = variant {return v} - return String() - } - set {variant = .setCannedMessageModulePart1(newValue)} - } - - /// - /// Set the canned message module part 2 text. - var setCannedMessageModulePart2: String { - get { - if case .setCannedMessageModulePart2(let v)? = variant {return v} - return String() - } - set {variant = .setCannedMessageModulePart2(newValue)} - } - - /// - /// Set the canned message module part 3 text. - var setCannedMessageModulePart3: String { - get { - if case .setCannedMessageModulePart3(let v)? = variant {return v} - return String() - } - set {variant = .setCannedMessageModulePart3(newValue)} - } - - /// - /// Set the canned message module part 4 text. - var setCannedMessageModulePart4: String { - get { - if case .setCannedMessageModulePart4(let v)? = variant {return v} - return String() - } - set {variant = .setCannedMessageModulePart4(newValue)} + set {variant = .setCannedMessageModuleMessages(newValue)} } /// @@ -454,41 +364,14 @@ struct AdminMessage { /// Tell the node to reboot in this many seconds (or <0 to cancel reboot) case rebootSeconds(Int32) /// - /// Get the Canned Message Module message part1 in the response to this message. - case getCannedMessageModulePart1Request(Bool) + /// Get the Canned Message Module messages in the response to this message. + case getCannedMessageModuleMessagesRequest(Bool) /// - /// TODO: REPLACE - case getCannedMessageModulePart1Response(String) + /// Get the Canned Message Module messages in the response to this message. + case getCannedMessageModuleMessagesResponse(String) /// - /// Get the Canned Message Module message part2 in the response to this message. - case getCannedMessageModulePart2Request(Bool) - /// - /// TODO: REPLACE - case getCannedMessageModulePart2Response(String) - /// - /// Get the Canned Message Module message part3 in the response to this message. - case getCannedMessageModulePart3Request(Bool) - /// - /// TODO: REPLACE - case getCannedMessageModulePart3Response(String) - /// - /// Get the Canned Message Module message part4 in the response to this message. - case getCannedMessageModulePart4Request(Bool) - /// - /// TODO: REPLACE - case getCannedMessageModulePart4Response(String) - /// - /// Set the canned message module part 1 text. - case setCannedMessageModulePart1(String) - /// - /// Set the canned message module part 2 text. - case setCannedMessageModulePart2(String) - /// - /// Set the canned message module part 3 text. - case setCannedMessageModulePart3(String) - /// - /// Set the canned message module part 4 text. - case setCannedMessageModulePart4(String) + /// Set the Canned Message Module messages text. + case setCannedMessageModuleMessages(String) /// /// Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) case shutdownSeconds(Int32) @@ -581,52 +464,16 @@ struct AdminMessage { guard case .rebootSeconds(let l) = lhs, case .rebootSeconds(let r) = rhs else { preconditionFailure() } return l == r }() - case (.getCannedMessageModulePart1Request, .getCannedMessageModulePart1Request): return { - guard case .getCannedMessageModulePart1Request(let l) = lhs, case .getCannedMessageModulePart1Request(let r) = rhs else { preconditionFailure() } + case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return { + guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() } return l == r }() - case (.getCannedMessageModulePart1Response, .getCannedMessageModulePart1Response): return { - guard case .getCannedMessageModulePart1Response(let l) = lhs, case .getCannedMessageModulePart1Response(let r) = rhs else { preconditionFailure() } + case (.getCannedMessageModuleMessagesResponse, .getCannedMessageModuleMessagesResponse): return { + guard case .getCannedMessageModuleMessagesResponse(let l) = lhs, case .getCannedMessageModuleMessagesResponse(let r) = rhs else { preconditionFailure() } return l == r }() - case (.getCannedMessageModulePart2Request, .getCannedMessageModulePart2Request): return { - guard case .getCannedMessageModulePart2Request(let l) = lhs, case .getCannedMessageModulePart2Request(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModulePart2Response, .getCannedMessageModulePart2Response): return { - guard case .getCannedMessageModulePart2Response(let l) = lhs, case .getCannedMessageModulePart2Response(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModulePart3Request, .getCannedMessageModulePart3Request): return { - guard case .getCannedMessageModulePart3Request(let l) = lhs, case .getCannedMessageModulePart3Request(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModulePart3Response, .getCannedMessageModulePart3Response): return { - guard case .getCannedMessageModulePart3Response(let l) = lhs, case .getCannedMessageModulePart3Response(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModulePart4Request, .getCannedMessageModulePart4Request): return { - guard case .getCannedMessageModulePart4Request(let l) = lhs, case .getCannedMessageModulePart4Request(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.getCannedMessageModulePart4Response, .getCannedMessageModulePart4Response): return { - guard case .getCannedMessageModulePart4Response(let l) = lhs, case .getCannedMessageModulePart4Response(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setCannedMessageModulePart1, .setCannedMessageModulePart1): return { - guard case .setCannedMessageModulePart1(let l) = lhs, case .setCannedMessageModulePart1(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setCannedMessageModulePart2, .setCannedMessageModulePart2): return { - guard case .setCannedMessageModulePart2(let l) = lhs, case .setCannedMessageModulePart2(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setCannedMessageModulePart3, .setCannedMessageModulePart3): return { - guard case .setCannedMessageModulePart3(let l) = lhs, case .setCannedMessageModulePart3(let r) = rhs else { preconditionFailure() } - return l == r - }() - case (.setCannedMessageModulePart4, .setCannedMessageModulePart4): return { - guard case .setCannedMessageModulePart4(let l) = lhs, case .setCannedMessageModulePart4(let r) = rhs else { preconditionFailure() } + case (.setCannedMessageModuleMessages, .setCannedMessageModuleMessages): return { + guard case .setCannedMessageModuleMessages(let l) = lhs, case .setCannedMessageModuleMessages(let r) = rhs else { preconditionFailure() } return l == r }() case (.shutdownSeconds, .shutdownSeconds): return { @@ -675,6 +522,10 @@ struct AdminMessage { /// /// TODO: REPLACE case loraConfig // = 5 + + /// + /// TODO: REPLACE + case bluetoothConfig // = 6 case UNRECOGNIZED(Int) init() { @@ -689,6 +540,7 @@ struct AdminMessage { case 3: self = .wifiConfig case 4: self = .displayConfig case 5: self = .loraConfig + case 6: self = .bluetoothConfig default: self = .UNRECOGNIZED(rawValue) } } @@ -701,6 +553,7 @@ struct AdminMessage { case .wifiConfig: return 3 case .displayConfig: return 4 case .loraConfig: return 5 + case .bluetoothConfig: return 6 case .UNRECOGNIZED(let i): return i } } @@ -787,6 +640,7 @@ extension AdminMessage.ConfigType: CaseIterable { .wifiConfig, .displayConfig, .loraConfig, + .bluetoothConfig, ] } @@ -836,18 +690,9 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat 33: .standard(proto: "confirm_set_radio"), 34: .standard(proto: "exit_simulator"), 35: .standard(proto: "reboot_seconds"), - 36: .standard(proto: "get_canned_message_module_part1_request"), - 37: .standard(proto: "get_canned_message_module_part1_response"), - 38: .standard(proto: "get_canned_message_module_part2_request"), - 39: .standard(proto: "get_canned_message_module_part2_response"), - 40: .standard(proto: "get_canned_message_module_part3_request"), - 41: .standard(proto: "get_canned_message_module_part3_response"), - 42: .standard(proto: "get_canned_message_module_part4_request"), - 43: .standard(proto: "get_canned_message_module_part4_response"), - 44: .standard(proto: "set_canned_message_module_part1"), - 45: .standard(proto: "set_canned_message_module_part2"), - 46: .standard(proto: "set_canned_message_module_part3"), - 47: .standard(proto: "set_canned_message_module_part4"), + 36: .standard(proto: "get_canned_message_module_messages_request"), + 37: .standard(proto: "get_canned_message_module_messages_response"), + 44: .standard(proto: "set_canned_message_module_messages"), 51: .standard(proto: "shutdown_seconds"), 52: .standard(proto: "get_device_metadata_request"), 53: .standard(proto: "get_device_metadata_response"), @@ -1056,7 +901,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat try decoder.decodeSingularBoolField(value: &v) if let v = v { if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart1Request(v) + self.variant = .getCannedMessageModuleMessagesRequest(v) } }() case 37: try { @@ -1064,55 +909,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat try decoder.decodeSingularStringField(value: &v) if let v = v { if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart1Response(v) - } - }() - case 38: try { - var v: Bool? - try decoder.decodeSingularBoolField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart2Request(v) - } - }() - case 39: try { - var v: String? - try decoder.decodeSingularStringField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart2Response(v) - } - }() - case 40: try { - var v: Bool? - try decoder.decodeSingularBoolField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart3Request(v) - } - }() - case 41: try { - var v: String? - try decoder.decodeSingularStringField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart3Response(v) - } - }() - case 42: try { - var v: Bool? - try decoder.decodeSingularBoolField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart4Request(v) - } - }() - case 43: try { - var v: String? - try decoder.decodeSingularStringField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .getCannedMessageModulePart4Response(v) + self.variant = .getCannedMessageModuleMessagesResponse(v) } }() case 44: try { @@ -1120,31 +917,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat try decoder.decodeSingularStringField(value: &v) if let v = v { if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .setCannedMessageModulePart1(v) - } - }() - case 45: try { - var v: String? - try decoder.decodeSingularStringField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .setCannedMessageModulePart2(v) - } - }() - case 46: try { - var v: String? - try decoder.decodeSingularStringField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .setCannedMessageModulePart3(v) - } - }() - case 47: try { - var v: String? - try decoder.decodeSingularStringField(value: &v) - if let v = v { - if self.variant != nil {try decoder.handleConflictingOneOf()} - self.variant = .setCannedMessageModulePart4(v) + self.variant = .setCannedMessageModuleMessages(v) } }() case 51: try { @@ -1263,54 +1036,18 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat guard case .rebootSeconds(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularInt32Field(value: v, fieldNumber: 35) }() - case .getCannedMessageModulePart1Request?: try { - guard case .getCannedMessageModulePart1Request(let v)? = self.variant else { preconditionFailure() } + case .getCannedMessageModuleMessagesRequest?: try { + guard case .getCannedMessageModuleMessagesRequest(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularBoolField(value: v, fieldNumber: 36) }() - case .getCannedMessageModulePart1Response?: try { - guard case .getCannedMessageModulePart1Response(let v)? = self.variant else { preconditionFailure() } + case .getCannedMessageModuleMessagesResponse?: try { + guard case .getCannedMessageModuleMessagesResponse(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularStringField(value: v, fieldNumber: 37) }() - case .getCannedMessageModulePart2Request?: try { - guard case .getCannedMessageModulePart2Request(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularBoolField(value: v, fieldNumber: 38) - }() - case .getCannedMessageModulePart2Response?: try { - guard case .getCannedMessageModulePart2Response(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularStringField(value: v, fieldNumber: 39) - }() - case .getCannedMessageModulePart3Request?: try { - guard case .getCannedMessageModulePart3Request(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularBoolField(value: v, fieldNumber: 40) - }() - case .getCannedMessageModulePart3Response?: try { - guard case .getCannedMessageModulePart3Response(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularStringField(value: v, fieldNumber: 41) - }() - case .getCannedMessageModulePart4Request?: try { - guard case .getCannedMessageModulePart4Request(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularBoolField(value: v, fieldNumber: 42) - }() - case .getCannedMessageModulePart4Response?: try { - guard case .getCannedMessageModulePart4Response(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularStringField(value: v, fieldNumber: 43) - }() - case .setCannedMessageModulePart1?: try { - guard case .setCannedMessageModulePart1(let v)? = self.variant else { preconditionFailure() } + case .setCannedMessageModuleMessages?: try { + guard case .setCannedMessageModuleMessages(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularStringField(value: v, fieldNumber: 44) }() - case .setCannedMessageModulePart2?: try { - guard case .setCannedMessageModulePart2(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularStringField(value: v, fieldNumber: 45) - }() - case .setCannedMessageModulePart3?: try { - guard case .setCannedMessageModulePart3(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularStringField(value: v, fieldNumber: 46) - }() - case .setCannedMessageModulePart4?: try { - guard case .setCannedMessageModulePart4(let v)? = self.variant else { preconditionFailure() } - try visitor.visitSingularStringField(value: v, fieldNumber: 47) - }() case .shutdownSeconds?: try { guard case .shutdownSeconds(let v)? = self.variant else { preconditionFailure() } try visitor.visitSingularInt32Field(value: v, fieldNumber: 51) @@ -1343,6 +1080,7 @@ extension AdminMessage.ConfigType: SwiftProtobuf._ProtoNameProviding { 3: .same(proto: "WIFI_CONFIG"), 4: .same(proto: "DISPLAY_CONFIG"), 5: .same(proto: "LORA_CONFIG"), + 6: .same(proto: "BLUETOOTH_CONFIG"), ] } diff --git a/Meshtastic/Protobufs/cannedmessages.pb.swift b/Meshtastic/Protobufs/cannedmessages.pb.swift index 6d544684..3d4c6596 100644 --- a/Meshtastic/Protobufs/cannedmessages.pb.swift +++ b/Meshtastic/Protobufs/cannedmessages.pb.swift @@ -29,19 +29,7 @@ struct CannedMessageModuleConfig { /// /// Predefined messages for canned message module separated by '|' characters. - var messagesPart1: String = String() - - /// - /// TODO: REPLACE - var messagesPart2: String = String() - - /// - /// TODO: REPLACE - var messagesPart3: String = String() - - /// - /// TODO: REPLACE - var messagesPart4: String = String() + var messages: String = String() var unknownFields = SwiftProtobuf.UnknownStorage() @@ -57,10 +45,7 @@ extension CannedMessageModuleConfig: @unchecked Sendable {} extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "CannedMessageModuleConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 11: .same(proto: "messagesPart1"), - 12: .same(proto: "messagesPart2"), - 13: .same(proto: "messagesPart3"), - 14: .same(proto: "messagesPart4"), + 1: .same(proto: "messages"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -69,36 +54,21 @@ extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 11: try { try decoder.decodeSingularStringField(value: &self.messagesPart1) }() - case 12: try { try decoder.decodeSingularStringField(value: &self.messagesPart2) }() - case 13: try { try decoder.decodeSingularStringField(value: &self.messagesPart3) }() - case 14: try { try decoder.decodeSingularStringField(value: &self.messagesPart4) }() + case 1: try { try decoder.decodeSingularStringField(value: &self.messages) }() default: break } } } func traverse(visitor: inout V) throws { - if !self.messagesPart1.isEmpty { - try visitor.visitSingularStringField(value: self.messagesPart1, fieldNumber: 11) - } - if !self.messagesPart2.isEmpty { - try visitor.visitSingularStringField(value: self.messagesPart2, fieldNumber: 12) - } - if !self.messagesPart3.isEmpty { - try visitor.visitSingularStringField(value: self.messagesPart3, fieldNumber: 13) - } - if !self.messagesPart4.isEmpty { - try visitor.visitSingularStringField(value: self.messagesPart4, fieldNumber: 14) + if !self.messages.isEmpty { + try visitor.visitSingularStringField(value: self.messages, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: CannedMessageModuleConfig, rhs: CannedMessageModuleConfig) -> Bool { - if lhs.messagesPart1 != rhs.messagesPart1 {return false} - if lhs.messagesPart2 != rhs.messagesPart2 {return false} - if lhs.messagesPart3 != rhs.messagesPart3 {return false} - if lhs.messagesPart4 != rhs.messagesPart4 {return false} + if lhs.messages != rhs.messages {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/config.pb.swift b/Meshtastic/Protobufs/config.pb.swift index 806ee401..8ee51d30 100644 --- a/Meshtastic/Protobufs/config.pb.swift +++ b/Meshtastic/Protobufs/config.pb.swift @@ -77,6 +77,14 @@ struct Config { set {payloadVariant = .lora(newValue)} } + var bluetooth: Config.BluetoothConfig { + get { + if case .bluetooth(let v)? = payloadVariant {return v} + return Config.BluetoothConfig() + } + set {payloadVariant = .bluetooth(newValue)} + } + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -88,6 +96,7 @@ struct Config { case wifi(Config.WiFiConfig) case display(Config.DisplayConfig) case lora(Config.LoRaConfig) + case bluetooth(Config.BluetoothConfig) #if !swift(>=4.1) static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool { @@ -119,6 +128,10 @@ struct Config { guard case .lora(let l) = lhs, case .lora(let r) = rhs else { preconditionFailure() } return l == r }() + case (.bluetooth, .bluetooth): return { + guard case .bluetooth(let l) = lhs, case .bluetooth(let r) = rhs else { preconditionFailure() } + return l == r + }() default: return false } } @@ -906,6 +919,68 @@ struct Config { init() {} } + struct BluetoothConfig { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Enable Bluetooth on the device + var enabled: Bool = false + + /// + /// Determines the pairing strategy for the device + var mode: Config.BluetoothConfig.PairingMode = .randomPin + + /// + /// Specified pin for PairingMode.FixedPin + var fixedPin: UInt32 = 0 + + var unknownFields = SwiftProtobuf.UnknownStorage() + + enum PairingMode: SwiftProtobuf.Enum { + typealias RawValue = Int + + /// + /// Device generates a random pin that will be shown on the screen of the device for pairing + case randomPin // = 0 + + /// + /// Device requires a specified fixed pin for pairing + case fixedPin // = 1 + + /// + /// Device requires no pin for pairing + case noPin // = 2 + case UNRECOGNIZED(Int) + + init() { + self = .randomPin + } + + init?(rawValue: Int) { + switch rawValue { + case 0: self = .randomPin + case 1: self = .fixedPin + case 2: self = .noPin + default: self = .UNRECOGNIZED(rawValue) + } + } + + var rawValue: Int { + switch self { + case .randomPin: return 0 + case .fixedPin: return 1 + case .noPin: return 2 + case .UNRECOGNIZED(let i): return i + } + } + + } + + init() {} + } + init() {} } @@ -1014,6 +1089,15 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable { ] } +extension Config.BluetoothConfig.PairingMode: CaseIterable { + // The compiler won't synthesize support with the UNRECOGNIZED case. + static var allCases: [Config.BluetoothConfig.PairingMode] = [ + .randomPin, + .fixedPin, + .noPin, + ] +} + #endif // swift(>=4.2) #if swift(>=5.5) && canImport(_Concurrency) @@ -1032,6 +1116,8 @@ extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {} extension Config.LoRaConfig: @unchecked Sendable {} extension Config.LoRaConfig.RegionCode: @unchecked Sendable {} extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {} +extension Config.BluetoothConfig: @unchecked Sendable {} +extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {} #endif // swift(>=5.5) && canImport(_Concurrency) // MARK: - Code below here is support for the SwiftProtobuf runtime. @@ -1045,6 +1131,7 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas 4: .same(proto: "wifi"), 5: .same(proto: "display"), 6: .same(proto: "lora"), + 7: .same(proto: "bluetooth"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1131,6 +1218,19 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas self.payloadVariant = .lora(v) } }() + case 7: try { + var v: Config.BluetoothConfig? + var hadOneofValue = false + if let current = self.payloadVariant { + hadOneofValue = true + if case .bluetooth(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.payloadVariant = .bluetooth(v) + } + }() default: break } } @@ -1166,6 +1266,10 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas guard case .lora(let v)? = self.payloadVariant else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 6) }() + case .bluetooth?: try { + guard case .bluetooth(let v)? = self.payloadVariant else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 7) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) @@ -1663,3 +1767,55 @@ extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding { 6: .same(proto: "ShortFast"), ] } + +extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = Config.protoMessageName + ".BluetoothConfig" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "enabled"), + 2: .same(proto: "mode"), + 3: .standard(proto: "fixed_pin"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }() + case 2: try { try decoder.decodeSingularEnumField(value: &self.mode) }() + case 3: try { try decoder.decodeSingularUInt32Field(value: &self.fixedPin) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.enabled != false { + try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1) + } + if self.mode != .randomPin { + try visitor.visitSingularEnumField(value: self.mode, fieldNumber: 2) + } + if self.fixedPin != 0 { + try visitor.visitSingularUInt32Field(value: self.fixedPin, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Config.BluetoothConfig, rhs: Config.BluetoothConfig) -> Bool { + if lhs.enabled != rhs.enabled {return false} + if lhs.mode != rhs.mode {return false} + if lhs.fixedPin != rhs.fixedPin {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Config.BluetoothConfig.PairingMode: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "RandomPin"), + 1: .same(proto: "FixedPin"), + 2: .same(proto: "NoPin"), + ] +} diff --git a/Meshtastic/Protobufs/localonly.pb.swift b/Meshtastic/Protobufs/localonly.pb.swift index 61136c16..f4b51fba 100644 --- a/Meshtastic/Protobufs/localonly.pb.swift +++ b/Meshtastic/Protobufs/localonly.pb.swift @@ -91,6 +91,17 @@ struct LocalConfig { /// Clears the value of `lora`. Subsequent reads from it will return its default value. mutating func clearLora() {_uniqueStorage()._lora = nil} + /// + /// The part of the config that is specific to the Bluetooth settings + var bluetooth: Config.BluetoothConfig { + get {return _storage._bluetooth ?? Config.BluetoothConfig()} + set {_uniqueStorage()._bluetooth = newValue} + } + /// Returns true if `bluetooth` has been explicitly set. + var hasBluetooth: Bool {return _storage._bluetooth != nil} + /// Clears the value of `bluetooth`. Subsequent reads from it will return its default value. + mutating func clearBluetooth() {_uniqueStorage()._bluetooth = nil} + /// /// A version integer used to invalidate old save files when we make /// incompatible changes This integer is set at build time and is private to @@ -221,7 +232,8 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 4: .same(proto: "wifi"), 5: .same(proto: "display"), 6: .same(proto: "lora"), - 7: .same(proto: "version"), + 7: .same(proto: "bluetooth"), + 8: .same(proto: "version"), ] fileprivate class _StorageClass { @@ -231,6 +243,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _wifi: Config.WiFiConfig? = nil var _display: Config.DisplayConfig? = nil var _lora: Config.LoRaConfig? = nil + var _bluetooth: Config.BluetoothConfig? = nil var _version: UInt32 = 0 static let defaultInstance = _StorageClass() @@ -244,6 +257,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati _wifi = source._wifi _display = source._display _lora = source._lora + _bluetooth = source._bluetooth _version = source._version } } @@ -269,7 +283,8 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati case 4: try { try decoder.decodeSingularMessageField(value: &_storage._wifi) }() case 5: try { try decoder.decodeSingularMessageField(value: &_storage._display) }() case 6: try { try decoder.decodeSingularMessageField(value: &_storage._lora) }() - case 7: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }() + case 7: try { try decoder.decodeSingularMessageField(value: &_storage._bluetooth) }() + case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }() default: break } } @@ -300,8 +315,11 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati try { if let v = _storage._lora { try visitor.visitSingularMessageField(value: v, fieldNumber: 6) } }() + try { if let v = _storage._bluetooth { + try visitor.visitSingularMessageField(value: v, fieldNumber: 7) + } }() if _storage._version != 0 { - try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 7) + try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 8) } } try unknownFields.traverse(visitor: &visitor) @@ -318,6 +336,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if _storage._wifi != rhs_storage._wifi {return false} if _storage._display != rhs_storage._display {return false} if _storage._lora != rhs_storage._lora {return false} + if _storage._bluetooth != rhs_storage._bluetooth {return false} if _storage._version != rhs_storage._version {return false} return true } diff --git a/Meshtastic/Protobufs/mesh.pb.swift b/Meshtastic/Protobufs/mesh.pb.swift index ebb8d747..46dbf236 100644 --- a/Meshtastic/Protobufs/mesh.pb.swift +++ b/Meshtastic/Protobufs/mesh.pb.swift @@ -798,23 +798,6 @@ struct User { /// Also, "long_name" should be their licence number. var isLicensed: Bool = false - /// - /// Transmit power at antenna connector, in decibel-milliwatt - /// An optional self-reported value useful in network planning, discovery - /// and positioning - along with ant_gain_dbi and ant_azimuth below - var txPowerDbm: UInt32 = 0 - - /// - /// Antenna gain (applicable to both Tx and Rx), in decibel-isotropic - var antGainDbi: UInt32 = 0 - - /// - /// Directional antenna true azimuth *if applicable*, in degrees (0-360) - /// Only applicable in case of stationary nodes with a directional antenna - /// Zero = not applicable (mobile or omni) or not specified - /// (use a value of 360 to indicate an antenna azimuth of zero degrees) - var antAzimuth: UInt32 = 0 - var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1500,65 +1483,58 @@ struct NodeInfo { /// /// The node number - var num: UInt32 { - get {return _storage._num} - set {_uniqueStorage()._num = newValue} - } + var num: UInt32 = 0 /// /// The user info for this node var user: User { - get {return _storage._user ?? User()} - set {_uniqueStorage()._user = newValue} + get {return _user ?? User()} + set {_user = newValue} } /// Returns true if `user` has been explicitly set. - var hasUser: Bool {return _storage._user != nil} + var hasUser: Bool {return self._user != nil} /// Clears the value of `user`. Subsequent reads from it will return its default value. - mutating func clearUser() {_uniqueStorage()._user = nil} + mutating func clearUser() {self._user = nil} /// /// This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. /// Position.time now indicates the last time we received a POSITION from that node. var position: Position { - get {return _storage._position ?? Position()} - set {_uniqueStorage()._position = newValue} + get {return _position ?? Position()} + set {_position = newValue} } /// Returns true if `position` has been explicitly set. - var hasPosition: Bool {return _storage._position != nil} + var hasPosition: Bool {return self._position != nil} /// Clears the value of `position`. Subsequent reads from it will return its default value. - mutating func clearPosition() {_uniqueStorage()._position = nil} + mutating func clearPosition() {self._position = nil} /// /// Returns the Signal-to-noise ratio (SNR) of the last received message, /// as measured by the receiver. Return SNR of the last received message in dB - var snr: Float { - get {return _storage._snr} - set {_uniqueStorage()._snr = newValue} - } + var snr: Float = 0 /// /// Set to indicate the last time we received a packet from this node - var lastHeard: UInt32 { - get {return _storage._lastHeard} - set {_uniqueStorage()._lastHeard = newValue} - } + var lastHeard: UInt32 = 0 /// /// The latest device metrics for the node. var deviceMetrics: DeviceMetrics { - get {return _storage._deviceMetrics ?? DeviceMetrics()} - set {_uniqueStorage()._deviceMetrics = newValue} + get {return _deviceMetrics ?? DeviceMetrics()} + set {_deviceMetrics = newValue} } /// Returns true if `deviceMetrics` has been explicitly set. - var hasDeviceMetrics: Bool {return _storage._deviceMetrics != nil} + var hasDeviceMetrics: Bool {return self._deviceMetrics != nil} /// Clears the value of `deviceMetrics`. Subsequent reads from it will return its default value. - mutating func clearDeviceMetrics() {_uniqueStorage()._deviceMetrics = nil} + mutating func clearDeviceMetrics() {self._deviceMetrics = nil} var unknownFields = SwiftProtobuf.UnknownStorage() init() {} - fileprivate var _storage = _StorageClass.defaultInstance + fileprivate var _user: User? = nil + fileprivate var _position: Position? = nil + fileprivate var _deviceMetrics: DeviceMetrics? = nil } /// @@ -2480,9 +2456,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, 4: .same(proto: "macaddr"), 6: .standard(proto: "hw_model"), 7: .standard(proto: "is_licensed"), - 10: .standard(proto: "tx_power_dbm"), - 11: .standard(proto: "ant_gain_dbi"), - 12: .standard(proto: "ant_azimuth"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2497,9 +2470,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, case 4: try { try decoder.decodeSingularBytesField(value: &self.macaddr) }() case 6: try { try decoder.decodeSingularEnumField(value: &self.hwModel) }() case 7: try { try decoder.decodeSingularBoolField(value: &self.isLicensed) }() - case 10: try { try decoder.decodeSingularUInt32Field(value: &self.txPowerDbm) }() - case 11: try { try decoder.decodeSingularUInt32Field(value: &self.antGainDbi) }() - case 12: try { try decoder.decodeSingularUInt32Field(value: &self.antAzimuth) }() default: break } } @@ -2524,15 +2494,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, if self.isLicensed != false { try visitor.visitSingularBoolField(value: self.isLicensed, fieldNumber: 7) } - if self.txPowerDbm != 0 { - try visitor.visitSingularUInt32Field(value: self.txPowerDbm, fieldNumber: 10) - } - if self.antGainDbi != 0 { - try visitor.visitSingularUInt32Field(value: self.antGainDbi, fieldNumber: 11) - } - if self.antAzimuth != 0 { - try visitor.visitSingularUInt32Field(value: self.antAzimuth, fieldNumber: 12) - } try unknownFields.traverse(visitor: &visitor) } @@ -2543,9 +2504,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, if lhs.macaddr != rhs.macaddr {return false} if lhs.hwModel != rhs.hwModel {return false} if lhs.isLicensed != rhs.isLicensed {return false} - if lhs.txPowerDbm != rhs.txPowerDbm {return false} - if lhs.antGainDbi != rhs.antGainDbi {return false} - if lhs.antAzimuth != rhs.antAzimuth {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -3050,98 +3008,56 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB 6: .standard(proto: "device_metrics"), ] - fileprivate class _StorageClass { - var _num: UInt32 = 0 - var _user: User? = nil - var _position: Position? = nil - var _snr: Float = 0 - var _lastHeard: UInt32 = 0 - var _deviceMetrics: DeviceMetrics? = nil - - static let defaultInstance = _StorageClass() - - private init() {} - - init(copying source: _StorageClass) { - _num = source._num - _user = source._user - _position = source._position - _snr = source._snr - _lastHeard = source._lastHeard - _deviceMetrics = source._deviceMetrics - } - } - - fileprivate mutating func _uniqueStorage() -> _StorageClass { - if !isKnownUniquelyReferenced(&_storage) { - _storage = _StorageClass(copying: _storage) - } - return _storage - } - mutating func decodeMessage(decoder: inout D) throws { - _ = _uniqueStorage() - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularUInt32Field(value: &_storage._num) }() - case 2: try { try decoder.decodeSingularMessageField(value: &_storage._user) }() - case 3: try { try decoder.decodeSingularMessageField(value: &_storage._position) }() - case 4: try { try decoder.decodeSingularFloatField(value: &_storage._snr) }() - case 5: try { try decoder.decodeSingularFixed32Field(value: &_storage._lastHeard) }() - case 6: try { try decoder.decodeSingularMessageField(value: &_storage._deviceMetrics) }() - default: break - } + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.num) }() + case 2: try { try decoder.decodeSingularMessageField(value: &self._user) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._position) }() + case 4: try { try decoder.decodeSingularFloatField(value: &self.snr) }() + case 5: try { try decoder.decodeSingularFixed32Field(value: &self.lastHeard) }() + case 6: try { try decoder.decodeSingularMessageField(value: &self._deviceMetrics) }() + default: break } } } func traverse(visitor: inout V) throws { - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every if/case branch local when no optimizations - // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and - // https://github.com/apple/swift-protobuf/issues/1182 - if _storage._num != 0 { - try visitor.visitSingularUInt32Field(value: _storage._num, fieldNumber: 1) - } - try { if let v = _storage._user { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } }() - try { if let v = _storage._position { - try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } }() - if _storage._snr != 0 { - try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4) - } - if _storage._lastHeard != 0 { - try visitor.visitSingularFixed32Field(value: _storage._lastHeard, fieldNumber: 5) - } - try { if let v = _storage._deviceMetrics { - try visitor.visitSingularMessageField(value: v, fieldNumber: 6) - } }() + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + if self.num != 0 { + try visitor.visitSingularUInt32Field(value: self.num, fieldNumber: 1) } + try { if let v = self._user { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + try { if let v = self._position { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() + if self.snr != 0 { + try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 4) + } + if self.lastHeard != 0 { + try visitor.visitSingularFixed32Field(value: self.lastHeard, fieldNumber: 5) + } + try { if let v = self._deviceMetrics { + try visitor.visitSingularMessageField(value: v, fieldNumber: 6) + } }() try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: NodeInfo, rhs: NodeInfo) -> Bool { - if lhs._storage !== rhs._storage { - let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in - let _storage = _args.0 - let rhs_storage = _args.1 - if _storage._num != rhs_storage._num {return false} - if _storage._user != rhs_storage._user {return false} - if _storage._position != rhs_storage._position {return false} - if _storage._snr != rhs_storage._snr {return false} - if _storage._lastHeard != rhs_storage._lastHeard {return false} - if _storage._deviceMetrics != rhs_storage._deviceMetrics {return false} - return true - } - if !storagesAreEqual {return false} - } + if lhs.num != rhs.num {return false} + if lhs._user != rhs._user {return false} + if lhs._position != rhs._position {return false} + if lhs.snr != rhs.snr {return false} + if lhs.lastHeard != rhs.lastHeard {return false} + if lhs._deviceMetrics != rhs._deviceMetrics {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } From 4000054c561b8f48c3335b6c8aa3ff2d627f6820 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 17 Aug 2022 07:25:51 -0700 Subject: [PATCH 26/36] Update project.pbxproj Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f8cf8487..62d23873 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -897,7 +897,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.38; + MARKETING_VERSION = 1.3.39; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -929,7 +929,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.38; + MARKETING_VERSION = 1.3.39; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; From 6a4df7872dab6c896e6f3b498343d582dca516e0 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 17 Aug 2022 07:28:46 -0700 Subject: [PATCH 27/36] Update Connect.swift --- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 07ceaffd..21a46f62 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -20,7 +20,7 @@ struct Connect: View { @State var isPreferredRadio: Bool = false @State var firmwareVersion = "0.0.0" - @State var minimumVersion = "1.3.38" + @State var minimumVersion = "1.3.39" @State var invalidVersion = false var body: some View { From ebf9e8feec01daa7a3d21f497f843b91505607d4 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 18 Aug 2022 08:46:10 -0700 Subject: [PATCH 28/36] Initial BLE config view --- Meshtastic.xcodeproj/project.pbxproj | 8 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 2 +- .../contents | 231 ++++++++++++++++++ .../Settings/Config/BluetoothConfig.swift | 174 +++++++++++++ Meshtastic/Views/Settings/Settings.swift | 12 + 6 files changed, 426 insertions(+), 3 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents create mode 100644 Meshtastic/Views/Settings/Config/BluetoothConfig.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f8cf8487..03924cf8 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6D26ED19040058C060 /* Extensions.swift */; }; DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */; }; DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */; }; + DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; }; DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; }; DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; }; @@ -171,6 +172,8 @@ DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = telemetry.pb.swift; sourceTree = ""; }; DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 3.xcdatamodel"; sourceTree = ""; }; DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = ""; }; + DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = ""; }; + DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 7.xcdatamodel"; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; @@ -278,6 +281,7 @@ DD61937A2863876A00E59241 /* Config */ = { isa = PBXGroup; children = ( + DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */, DD41582528582E9B009B0E59 /* DeviceConfig.swift */, DD8EBF42285058FA00426DCA /* DisplayConfig.swift */, DD2553562855B02500E55709 /* LoRaConfig.swift */, @@ -664,6 +668,7 @@ DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */, DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */, DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */, + DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, DDC4D568275499A500A4208E /* Persistence.swift in Sources */, DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */, @@ -1102,6 +1107,7 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */, DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */, DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */, DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */, @@ -1109,7 +1115,7 @@ DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */; + currentVersion = DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 319888a5..bf45c105 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 6.xcdatamodel + MeshtasticDataModel v 7.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents index 6f9b7000..22353512 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 6.xcdatamodel/contents @@ -1,5 +1,5 @@ - + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents new file mode 100644 index 00000000..d58af5fd --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 7.xcdatamodel/contents @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift new file mode 100644 index 00000000..1057f1eb --- /dev/null +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -0,0 +1,174 @@ +// +// BluetoothConfig.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 8/18/22. +// + +import SwiftUI + +enum BluetoothModes: Int, CaseIterable, Identifiable { + + case randomPin = 0 + case fixedPin = 1 + case noPin = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .randomPin: + return "Random" + case .fixedPin: + return "Fixed" + case .noPin: + return "None" + } + } + } + func protoEnumValue() -> Config.BluetoothConfig.PairingMode { + + switch self { + + case .randomPin: + return Config.BluetoothConfig.PairingMode.randomPin + case .fixedPin: + return Config.BluetoothConfig.PairingMode.fixedPin + case .noPin: + return Config.BluetoothConfig.PairingMode.noPin + } + } +} + +struct BluetoothConfig: 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 = true + /// Determines the pairing strategy for the device + @State var mode = 0 + + /// Specified pin for PairingMode.FixedPin + @State var fixedPin = 123456 + + var body: some View { + + VStack { + + Form { + + Section(header: Text("Options")) { + + Toggle(isOn: $enabled) { + + Label("Enabled", systemImage: "antenna.radiowaves.left.and.right") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + + Picker("Pairing PIN", selection: $mode ) { + ForEach(BluetoothModes.allCases) { bm in + Text(bm.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + if mode == 2 { + + + } + } + } + .disabled(bleManager.connectedPeripheral == nil) + + 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 Bluetooth Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") { + + var bc = Config.BluetoothConfig() + bc.enabled = enabled + bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin + bc.fixedPin = UInt32(fixedPin) + + let adminMessageId = 0//bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!) + + if adminMessageId > 0 { + + // Should show a saved successfully alert once I know that to be true + // for now just disable the button after a successful save + hasChanges = false + + } else { + + } + } + } + } + .navigationTitle("Display Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + }) + .onAppear { + + if self.initialLoad{ + + self.bleManager.context = context + + self.enabled = node!.bluetoothConfig?.enabled ?? true + self.mode = Int(node!.bluetoothConfig?.mode ?? 0) + self.fixedPin = Int(node!.bluetoothConfig?.fixedPin ?? 123456) + self.hasChanges = false + self.initialLoad = false + } + } + .onChange(of: enabled) { newEnabled in + + if node != nil && node!.bluetoothConfig != nil { + + if newEnabled != node!.bluetoothConfig!.enabled { hasChanges = true } + } + } + .onChange(of: mode) { newMode in + + if node != nil && node!.bluetoothConfig != nil { + + if newMode != node!.bluetoothConfig!.mode { hasChanges = true } + } + } + .onChange(of: fixedPin) { newFixedPin in + + if node != nil && node!.bluetoothConfig != nil { + + if newFixedPin != node!.bluetoothConfig!.fixedPin { hasChanges = true } + } + } + + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index e1887074..db8d7702 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -69,6 +69,18 @@ struct Settings: View { } .disabled(bleManager.connectedPeripheral == nil) + NavigationLink() { + + BluetoothConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) + } label: { + + Image(systemName: "antenna.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) + + Text("Bluetooth (BLE)") + } + .disabled(bleManager.connectedPeripheral == nil) + NavigationLink { DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum })) } label: { From 91ad2585907f7e702f5b0b094808563de50ff463 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 19 Aug 2022 23:26:02 -0700 Subject: [PATCH 29/36] Add Distance to node list --- Meshtastic.xcodeproj/project.pbxproj | 8 ++++ Meshtastic/Enums/BluetoothModes.swift | 39 ++++++++++++++++++ Meshtastic/Views/Helpers/DistanceText.swift | 22 ++++++++++ Meshtastic/Views/Nodes/NodeList.swift | 41 +++++++++++++++---- .../Settings/Config/BluetoothConfig.swift | 33 --------------- .../Settings/Config/PositionConfig.swift | 7 ++++ 6 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 Meshtastic/Enums/BluetoothModes.swift create mode 100644 Meshtastic/Views/Helpers/DistanceText.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 8cd75056..c22230c2 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -71,6 +71,8 @@ DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */; }; DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */; }; DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */; }; + DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */; }; + DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; }; DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; }; DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; }; @@ -174,6 +176,8 @@ DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = ""; }; DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = ""; }; DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 7.xcdatamodel"; sourceTree = ""; }; + DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothModes.swift; sourceTree = ""; }; + DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; @@ -318,6 +322,7 @@ isa = PBXGroup; children = ( DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */, + DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */, ); path = Enums; sourceTree = ""; @@ -470,6 +475,7 @@ DDC3B273283F411B00AC321C /* LastHeardText.swift */, DDA6B2EA28420A7B003E8C16 /* NodeAnnotation.swift */, DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */, + DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */, ); path = Helpers; sourceTree = ""; @@ -687,6 +693,7 @@ DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */, DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */, DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, + DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */, C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */, DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, @@ -695,6 +702,7 @@ DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */, DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */, + DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */, DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */, C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */, DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, diff --git a/Meshtastic/Enums/BluetoothModes.swift b/Meshtastic/Enums/BluetoothModes.swift new file mode 100644 index 00000000..003b0a3f --- /dev/null +++ b/Meshtastic/Enums/BluetoothModes.swift @@ -0,0 +1,39 @@ +// +// BluetoothModes.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/19/22. +// + +enum BluetoothModes: Int, CaseIterable, Identifiable { + + case randomPin = 0 + case fixedPin = 1 + case noPin = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .randomPin: + return "Random" + case .fixedPin: + return "Fixed" + case .noPin: + return "None" + } + } + } + func protoEnumValue() -> Config.BluetoothConfig.PairingMode { + + switch self { + + case .randomPin: + return Config.BluetoothConfig.PairingMode.randomPin + case .fixedPin: + return Config.BluetoothConfig.PairingMode.fixedPin + case .noPin: + return Config.BluetoothConfig.PairingMode.noPin + } + } +} diff --git a/Meshtastic/Views/Helpers/DistanceText.swift b/Meshtastic/Views/Helpers/DistanceText.swift new file mode 100644 index 00000000..1709b5f4 --- /dev/null +++ b/Meshtastic/Views/Helpers/DistanceText.swift @@ -0,0 +1,22 @@ +// +// DistanceText.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/19/22. +// + +import SwiftUI +import CoreLocation +import MapKit + +struct DistanceText: View { + + var meters: CLLocationDistance + + var body: some View { + + let distanceFormatter = MKDistanceFormatter() + + Text("Distance: \(distanceFormatter.string(fromDistance: Double(meters)))") + } +} diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 95755bd4..16009570 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -9,6 +9,7 @@ // A view showing a list of devices that have been seen on the mesh network from the perspective of the connected device. import SwiftUI +import CoreLocation struct NodeList: View { @@ -35,8 +36,8 @@ struct NodeList: View { if nodes.count == 0 { Text("Scan for Radios").font(.largeTitle) - Text("No LoRa Mesh Nodes Found").font(.title2) - Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.") + Text("No Meshtastic Nodes Found").font(.title2) + Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your iPhone, iPad or Mac.") .font(.body) Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.") Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.") @@ -87,14 +88,43 @@ struct NodeList: View { Image(systemName: "clock.badge.checkmark.fill").font(.title3) .foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { LastHeardText(lastHeard: node.lastHeard).font(.subheadline).foregroundColor(.gray) + } else { LastHeardText(lastHeard: node.lastHeard).font(.title3).foregroundColor(.gray) } } + + if node.positions?.count ?? 0 > 0 { + + Spacer() + HStack(alignment: .bottom) { + + let lastPostion = node.positions!.reversed()[0] as! PositionEntity + + let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) + + let nodeCoord = CLLocation(latitude: lastPostion.coordinate!.latitude, longitude: lastPostion.coordinate!.longitude) + + let metersAway = nodeCoord.distance(from: myCoord) + + Image(systemName: "lines.measurement.horizontal").font(.title3) + .foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) + + if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { + + DistanceText(meters: metersAway).font(.subheadline).foregroundColor(.gray) + + } else { + + DistanceText(meters: metersAway).font(.title3).foregroundColor(.gray) + } + } + } } .padding([.leading, .top, .bottom]) } @@ -109,13 +139,6 @@ struct NodeList: View { self.bleManager.userSettings = userSettings self.bleManager.context = context self.initialLoad = false - -// if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { -// -// if nodes.count > 0 { -// selection = "0" -// } -// } } } } diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 1057f1eb..5f5c7e8f 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -7,39 +7,6 @@ import SwiftUI -enum BluetoothModes: Int, CaseIterable, Identifiable { - - case randomPin = 0 - case fixedPin = 1 - case noPin = 2 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - case .randomPin: - return "Random" - case .fixedPin: - return "Fixed" - case .noPin: - return "None" - } - } - } - func protoEnumValue() -> Config.BluetoothConfig.PairingMode { - - switch self { - - case .randomPin: - return Config.BluetoothConfig.PairingMode.randomPin - case .fixedPin: - return Config.BluetoothConfig.PairingMode.fixedPin - case .noPin: - return Config.BluetoothConfig.PairingMode.noPin - } - } -} - struct BluetoothConfig: View { @Environment(\.managedObjectContext) var context diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index a2348bea..19201fda 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -370,6 +370,13 @@ struct PositionConfig: View { if newSmartPosition != node!.positionConfig!.smartPositionEnabled { hasChanges = true } } } + .onChange(of: positionBroadcastSeconds) { newPositionBroadcastSeconds in + + if node != nil && node!.positionConfig != nil { + + if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true } + } + } .onChange(of: deviceGpsEnabled) { newDeviceGps in if node != nil && node!.positionConfig != nil { From 07f28252fd7c50b2415b608ca92b8fdee3026060 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 20 Aug 2022 12:15:14 -0700 Subject: [PATCH 30/36] Move enums, add distance to node list work on BLE settings --- Meshtastic.xcodeproj/project.pbxproj | 22 ++- Meshtastic/Enums/BluetoothModes.swift | 6 +- Meshtastic/Enums/DeviceRoles.swift | 48 +++++ Meshtastic/Enums/GpsFormats.swift | 56 ++++++ Meshtastic/Enums/LoraConfigEnums.swift | 181 ++++++++++++++++++ Meshtastic/Enums/ScreenIntervals.swift | 73 +++++++ Meshtastic/Enums/WiFiModes.swift | 42 ++++ Meshtastic/Helpers/Extensions.swift | 11 ++ Meshtastic/Helpers/MeshPackets.swift | 91 ++++++++- .../Settings/Config/BluetoothConfig.swift | 40 +++- .../Views/Settings/Config/DeviceConfig.swift | 40 ---- .../Views/Settings/Config/DisplayConfig.swift | 113 ----------- .../Views/Settings/Config/LoRaConfig.swift | 173 ----------------- .../Views/Settings/Config/WiFiConfig.swift | 38 +--- 14 files changed, 559 insertions(+), 375 deletions(-) create mode 100644 Meshtastic/Enums/DeviceRoles.swift create mode 100644 Meshtastic/Enums/GpsFormats.swift create mode 100644 Meshtastic/Enums/LoraConfigEnums.swift create mode 100644 Meshtastic/Enums/ScreenIntervals.swift create mode 100644 Meshtastic/Enums/WiFiModes.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index c22230c2..e21580b3 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -73,6 +73,11 @@ DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */; }; DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */; }; DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */; }; + DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */; }; + DDB6ABE228B13FB500384BA1 /* GpsFormats.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */; }; + DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */; }; + DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */; }; + DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; }; DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; }; DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; }; @@ -178,6 +183,11 @@ DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 7.xcdatamodel"; sourceTree = ""; }; DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothModes.swift; sourceTree = ""; }; DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = ""; }; + DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRoles.swift; sourceTree = ""; }; + DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GpsFormats.swift; sourceTree = ""; }; + DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenIntervals.swift; sourceTree = ""; }; + DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = ""; }; + DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiModes.swift; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; @@ -321,8 +331,13 @@ DD8ED9C6289CE4A100B3B0AB /* Enums */ = { isa = PBXGroup; children = ( - DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */, DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */, + DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */, + DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */, + DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */, + DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */, + DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */, + DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */, ); path = Enums; sourceTree = ""; @@ -672,6 +687,7 @@ DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */, DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */, DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */, + DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */, DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */, DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */, DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */, @@ -691,6 +707,7 @@ DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */, DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */, DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */, + DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */, DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */, DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */, @@ -708,6 +725,7 @@ DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */, DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, + DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */, DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */, DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, C9483F6D2773017500998F6B /* MapView.swift in Sources */, @@ -721,11 +739,13 @@ DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */, DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */, DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */, + DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */, DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */, DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */, DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */, + DDB6ABE228B13FB500384BA1 /* GpsFormats.swift in Sources */, DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */, DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */, C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */, diff --git a/Meshtastic/Enums/BluetoothModes.swift b/Meshtastic/Enums/BluetoothModes.swift index 003b0a3f..c631defa 100644 --- a/Meshtastic/Enums/BluetoothModes.swift +++ b/Meshtastic/Enums/BluetoothModes.swift @@ -16,11 +16,11 @@ enum BluetoothModes: Int, CaseIterable, Identifiable { get { switch self { case .randomPin: - return "Random" + return "Random PIN" case .fixedPin: - return "Fixed" + return "Fixed PIN" case .noPin: - return "None" + return "No PIN (Just Works)" } } } diff --git a/Meshtastic/Enums/DeviceRoles.swift b/Meshtastic/Enums/DeviceRoles.swift new file mode 100644 index 00000000..ba793bd3 --- /dev/null +++ b/Meshtastic/Enums/DeviceRoles.swift @@ -0,0 +1,48 @@ +// +// DeviceRoles.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/20/22. +// + +import Foundation + +// Default of 0 is Client +enum DeviceRoles: Int, CaseIterable, Identifiable { + + case client = 0 + case clientMute = 1 + case router = 2 + case routerClient = 3 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + case .client: + return "Client (default) - App connected client." + case .clientMute: + return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh." + case .router: + return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep." + case .routerClient: + return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client." + } + } + } + func protoEnumValue() -> Config.DeviceConfig.Role { + + switch self { + + case .client: + return Config.DeviceConfig.Role.client + case .clientMute: + return Config.DeviceConfig.Role.clientMute + case .router: + return Config.DeviceConfig.Role.router + case .routerClient: + return Config.DeviceConfig.Role.routerClient + } + } +} diff --git a/Meshtastic/Enums/GpsFormats.swift b/Meshtastic/Enums/GpsFormats.swift new file mode 100644 index 00000000..5f3dd4f6 --- /dev/null +++ b/Meshtastic/Enums/GpsFormats.swift @@ -0,0 +1,56 @@ +// +// GpsFormats.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/20/22. +// + +import Foundation + +enum GpsFormats: 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" + } + } + } + func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat { + + switch self { + + case .gpsFormatDec: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec + case .gpsFormatDms: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms + case .gpsFormatUtm: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm + case .gpsFormatMgrs: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs + case .gpsFormatOlc: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc + case .gpsFormatOsgr: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr + } + } +} diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift new file mode 100644 index 00000000..15615541 --- /dev/null +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -0,0 +1,181 @@ +// +// LoraConfig.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/20/22. +// + +import Foundation + +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 = 11 + case th = 12 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .unset: + return "Please set a region" + case .us: + return "United States" + case .eu433: + return "European Union 433mhz" + case .eu868: + return "European Union 868mhz" + case .cn: + return "China" + case .jp: + return "Japan" + case .anz: + return "Australia / New Zealand" + case .kr: + return "Korea" + case .tw: + return "Taiwan" + case .ru: + return "Russia" + case .in: + return "India" + case .nz865: + return "New Zealand 865mhz" + case .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 { + + case LongFast = 0 + case LongSlow = 1 + case VLongSlow = 2 + case MedSlow = 3 + case MedFast = 4 + case ShortSlow = 5 + case ShortFast = 6 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + case .LongFast: + return "Long Range - Fast" + case .LongSlow: + return "Long Range - Slow" + case .VLongSlow: + return "Very Long Range - Slow" + case .MedSlow: + return "Medium Range - Slow" + case .MedFast: + return "Medium Range - Fast" + case .ShortSlow: + return "Short Range - Slow" + case .ShortFast: + 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 .MedSlow: + return Config.LoRaConfig.ModemPreset.medSlow + case .MedFast: + return Config.LoRaConfig.ModemPreset.medFast + 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" + } + } + } +} diff --git a/Meshtastic/Enums/ScreenIntervals.swift b/Meshtastic/Enums/ScreenIntervals.swift new file mode 100644 index 00000000..74731e11 --- /dev/null +++ b/Meshtastic/Enums/ScreenIntervals.swift @@ -0,0 +1,73 @@ +// +// ScreenIntervals.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/20/22. +// + +import Foundation + +// Default of 0 is One Minute +enum ScreenOnIntervals: Int, CaseIterable, Identifiable { + + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 0 + case fifteenMinutes = 900 + case thirtyMinutes = 1800 + case oneHour = 3600 + case max = 31536000 // One Year + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + 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 .max: + return "Always On" + } + } + } +} + +// Default of 0 is off +enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable { + + case off = 0 + 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 .thirtySeconds: + return "Thirty Seconds" + case .oneMinute: + return "One Minute" + case .fiveMinutes: + return "Five Minutes" + case .tenMinutes: + return "Ten Minutes" + case .fifteenMinutes: + return "Fifteen Minutes" + } + } + } +} diff --git a/Meshtastic/Enums/WiFiModes.swift b/Meshtastic/Enums/WiFiModes.swift new file mode 100644 index 00000000..cfb33099 --- /dev/null +++ b/Meshtastic/Enums/WiFiModes.swift @@ -0,0 +1,42 @@ +// +// WiFiModes.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/20/22. +// + +import Foundation + +enum WiFiModes: Int, CaseIterable, Identifiable { + + case client = 0 + case accessPoint = 1 + case accessPointHidden = 2 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .client: + return "Client" + case .accessPoint: + return "Software Access Point" + case .accessPointHidden: + return "Software Access Point (Hidden)" + + } + } + } + func protoEnumValue() -> Config.WiFiConfig.WiFiMode { + + switch self { + + case .client: + return Config.WiFiConfig.WiFiMode.client + case .accessPoint: + return Config.WiFiConfig.WiFiMode.accessPoint + case .accessPointHidden: + return Config.WiFiConfig.WiFiMode.accessPointHidden + } + } +} diff --git a/Meshtastic/Helpers/Extensions.swift b/Meshtastic/Helpers/Extensions.swift index 42b350e0..8f6abe9e 100644 --- a/Meshtastic/Helpers/Extensions.swift +++ b/Meshtastic/Helpers/Extensions.swift @@ -23,6 +23,17 @@ extension Date { } } +extension Int { + + func numberOfDigits() -> Int { + if abs(self) < 10 { + return 1 + } else { + return 1 + (self/10).numberOfDigits() + } + } +} + extension String { /// Create `Data` from hexadecimal string representation diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index dbc7eb0c..1f8fb415 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -92,6 +92,92 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont } } + if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) { + + var isDefault = false + + if (try! config.bluetooth.jsonString()) == "{}" { + + isDefault = true + print("📶 Default Bluetooth config") + if meshlogging { MeshLogger.log("🖥️ Default Bluetooth config \(String(nodeNum))") } + + } else { + + if meshlogging { MeshLogger.log("🖥️ Custom Bluetooth config \(String(nodeNum))") } + print("📶 Custom Bluetooth config") + } + + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) + + do { + + let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity] + // Found a node, save Device Config + if !fetchedNode.isEmpty { + + if fetchedNode[0].bluetoothConfig == nil { + + let newBluetoothConfig = BluetoothConfigEntity(context: context) + + if isDefault { + + newBluetoothConfig.enabled = true + newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue) + newBluetoothConfig.fixedPin = Int32("123456") ?? 123456 + + } else { + + newBluetoothConfig.enabled = config.bluetooth.enabled + newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue) + newBluetoothConfig.fixedPin = Int32(config.display.autoScreenCarouselSecs) + + } + fetchedNode[0].bluetoothConfig = newBluetoothConfig + + } else { + + if isDefault { + + fetchedNode[0].displayConfig?.screenOnSeconds = 0 + fetchedNode[0].displayConfig?.screenCarouselInterval = 0 + fetchedNode[0].displayConfig?.gpsFormat = 0 + fetchedNode[0].displayConfig?.compassNorthTop = false + + } else { + + fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue) + fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs) + fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) + fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop + } + } + + do { + + try context.save() + if meshlogging { MeshLogger.log("💾 Updated Display Config for node number: \(String(nodeNum))") } + + } catch { + + context.rollback() + + let nsError = error as NSError + print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)") + } + } else { + + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Display Config") + } + + } catch { + + let nsError = error as NSError + print("💥 Fetching node for core data DisplayConfigEntity failed: \(nsError)") + } + } + if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) { var isDefault = false @@ -99,11 +185,12 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont if (try! config.display.jsonString()) == "{}" { isDefault = true - print("🖥️ Default Display config") + + if meshlogging { MeshLogger.log("🖥️ Default Display config \(String(nodeNum))") } } else { - print("🖥️ Custom Display config") + if meshlogging { MeshLogger.log("🖥️ Custom Display config \(String(nodeNum))") } } let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 5f5c7e8f..7b01ea15 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -23,7 +23,13 @@ struct BluetoothConfig: View { @State var mode = 0 /// Specified pin for PairingMode.FixedPin - @State var fixedPin = 123456 + @State var fixedPin = "123456" + + let numberFormatter: NumberFormatter = { + let formatter = NumberFormatter() + formatter.numberStyle = .none + return formatter + }() var body: some View { @@ -40,16 +46,36 @@ struct BluetoothConfig: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Picker("Pairing PIN", selection: $mode ) { + Picker("Pairing Mode", selection: $mode ) { ForEach(BluetoothModes.allCases) { bm in Text(bm.description) } } .pickerStyle(DefaultPickerStyle()) - if mode == 2 { - + if mode == 1 { + HStack { + Label("Fixed PIN", systemImage: "wallet.pass") + TextField("Fixed PIN", text: $fixedPin) + .foregroundColor(.gray) + .onChange(of: fixedPin, perform: { value in + + let digitCount = fixedPin.utf8.count + // Only mess with the value if it is too big + if digitCount > 6 || digitCount < 6 { + + fixedPin = "123456" + } + + if digitCount < 6 { + + fixedPin = "123456" + } + }) + .foregroundColor(.gray) + } + .keyboardType(.decimalPad) } } } @@ -78,7 +104,7 @@ struct BluetoothConfig: View { var bc = Config.BluetoothConfig() bc.enabled = enabled bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin - bc.fixedPin = UInt32(fixedPin) + bc.fixedPin = UInt32(fixedPin) ?? 123456 let adminMessageId = 0//bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!) @@ -109,7 +135,7 @@ struct BluetoothConfig: View { self.enabled = node!.bluetoothConfig?.enabled ?? true self.mode = Int(node!.bluetoothConfig?.mode ?? 0) - self.fixedPin = Int(node!.bluetoothConfig?.fixedPin ?? 123456) + //self.fixedPin = (String(node!.bluetoothConfig?.fixedPin) ?? "123456") self.hasChanges = false self.initialLoad = false } @@ -132,7 +158,7 @@ struct BluetoothConfig: View { if node != nil && node!.bluetoothConfig != nil { - if newFixedPin != node!.bluetoothConfig!.fixedPin { hasChanges = true } + if newFixedPin != String(node!.bluetoothConfig!.fixedPin) { hasChanges = true } } } diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 0d17f68f..809b25f8 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -6,46 +6,6 @@ // import SwiftUI -// Default of 0 is Client -enum DeviceRoles: Int, CaseIterable, Identifiable { - - case client = 0 - case clientMute = 1 - case router = 2 - case routerClient = 3 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - - case .client: - return "Client (default) - App connected client." - case .clientMute: - return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh." - case .router: - return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep." - case .routerClient: - return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client." - } - } - } - func protoEnumValue() -> Config.DeviceConfig.Role { - - switch self { - - case .client: - return Config.DeviceConfig.Role.client - case .clientMute: - return Config.DeviceConfig.Role.clientMute - case .router: - return Config.DeviceConfig.Role.router - case .routerClient: - return Config.DeviceConfig.Role.routerClient - } - } -} - struct DeviceConfig: View { @Environment(\.managedObjectContext) var context diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 13d9ab13..6a1e6e25 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -7,119 +7,6 @@ import SwiftUI -enum GpsFormats: 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" - } - } - } - func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat { - - switch self { - - case .gpsFormatDec: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec - case .gpsFormatDms: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms - case .gpsFormatUtm: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm - case .gpsFormatMgrs: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs - case .gpsFormatOlc: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc - case .gpsFormatOsgr: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr - } - } -} - -// Default of 0 is One Minute -enum ScreenOnIntervals: Int, CaseIterable, Identifiable { - - case oneMinute = 60 - case fiveMinutes = 300 - case tenMinutes = 0 - case fifteenMinutes = 900 - case thirtyMinutes = 1800 - case oneHour = 3600 - case max = 31536000 // One Year - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - 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 .max: - return "Always On" - } - } - } -} - -// Default of 0 is off -enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable { - - case off = 0 - 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 .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 diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 9b30f94e..2e63c168 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -7,179 +7,6 @@ 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 = 11 - case th = 12 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - case .unset: - return "Please set a region" - case .us: - return "United States" - case .eu433: - return "European Union 433mhz" - case .eu868: - return "European Union 868mhz" - case .cn: - return "China" - case .jp: - return "Japan" - case .anz: - return "Australia / New Zealand" - case .kr: - return "Korea" - case .tw: - return "Taiwan" - case .ru: - return "Russia" - case .in: - return "India" - case .nz865: - return "New Zealand 865mhz" - case .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 { - - case LongFast = 0 - case LongSlow = 1 - case VLongSlow = 2 - case MedSlow = 3 - case MedFast = 4 - case ShortSlow = 5 - case ShortFast = 6 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - - case .LongFast: - return "Long Range - Fast" - case .LongSlow: - return "Long Range - Slow" - case .VLongSlow: - return "Very Long Range - Slow" - case .MedSlow: - return "Medium Range - Slow" - case .MedFast: - return "Medium Range - Fast" - case .ShortSlow: - return "Short Range - Slow" - case .ShortFast: - 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 .MedSlow: - return Config.LoRaConfig.ModemPreset.medSlow - case .MedFast: - return Config.LoRaConfig.ModemPreset.medFast - 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" - } - } - } -} - struct LoRaConfig: View { @Environment(\.managedObjectContext) var context diff --git a/Meshtastic/Views/Settings/Config/WiFiConfig.swift b/Meshtastic/Views/Settings/Config/WiFiConfig.swift index e322a72a..1378be55 100644 --- a/Meshtastic/Views/Settings/Config/WiFiConfig.swift +++ b/Meshtastic/Views/Settings/Config/WiFiConfig.swift @@ -7,40 +7,6 @@ import SwiftUI -enum WiFiModes: Int, CaseIterable, Identifiable { - - case client = 0 - case accessPoint = 1 - case accessPointHidden = 2 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - case .client: - return "Client" - case .accessPoint: - return "Software Access Point" - case .accessPointHidden: - return "Software Access Point (Hidden)" - - } - } - } - func protoEnumValue() -> Config.WiFiConfig.WiFiMode { - - switch self { - - case .client: - return Config.WiFiConfig.WiFiMode.client - case .accessPoint: - return Config.WiFiConfig.WiFiMode.accessPoint - case .accessPointHidden: - return Config.WiFiConfig.WiFiMode.accessPointHidden - } - } -} - struct WiFiConfig: View { @Environment(\.managedObjectContext) var context @@ -125,12 +91,12 @@ struct WiFiConfig: View { // Only mess with the value if it is too big if totalBytes > 63 { - let firstNBytes = Data(ssid.utf8.prefix(63)) + let firstNBytes = Data(password.utf8.prefix(63)) if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { // Set the shortName back to the last place where it was the right size - ssid = maxBytesString + password = maxBytesString } } }) From af8f8ff9ac0531abbb99a5767df504a54dab73c2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 20 Aug 2022 12:31:52 -0700 Subject: [PATCH 31/36] Finish hooking up the ble config --- Meshtastic/Helpers/BLEManager.swift | 29 +++++++++++++++++++ Meshtastic/Helpers/MeshPackets.swift | 25 ++++++++-------- .../Settings/Config/BluetoothConfig.swift | 2 +- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index a6a78c44..08a77c6c 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1048,6 +1048,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return 0 } + public func saveBluetoothConfig(config: Config.BluetoothConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 { + + var adminPacket = AdminMessage() + adminPacket.setConfig.bluetooth = config + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = 0 //UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 1f8fb415..79419d51 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -131,7 +131,7 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont newBluetoothConfig.enabled = config.bluetooth.enabled newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue) - newBluetoothConfig.fixedPin = Int32(config.display.autoScreenCarouselSecs) + newBluetoothConfig.fixedPin = Int32(config.bluetooth.fixedPin) } fetchedNode[0].bluetoothConfig = newBluetoothConfig @@ -140,41 +140,40 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont if isDefault { - fetchedNode[0].displayConfig?.screenOnSeconds = 0 - fetchedNode[0].displayConfig?.screenCarouselInterval = 0 - fetchedNode[0].displayConfig?.gpsFormat = 0 - fetchedNode[0].displayConfig?.compassNorthTop = false + fetchedNode[0].bluetoothConfig?.enabled = true + fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue) + fetchedNode[0].bluetoothConfig?.fixedPin = Int32("123456") ?? 123456 } else { - fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue) - fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs) - fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs) - fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop + fetchedNode[0].bluetoothConfig?.enabled = config.bluetooth.enabled + fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue) + fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.bluetooth.fixedPin) + } } do { try context.save() - if meshlogging { MeshLogger.log("💾 Updated Display Config for node number: \(String(nodeNum))") } + if meshlogging { MeshLogger.log("💾 Updated Bluetooth Config for node number: \(String(nodeNum))") } } catch { context.rollback() let nsError = error as NSError - print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)") + print("💥 Error Updating Core Data BluetoothConfigEntity: \(nsError)") } } else { - print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Display Config") + print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Bluetooth Config") } } catch { let nsError = error as NSError - print("💥 Fetching node for core data DisplayConfigEntity failed: \(nsError)") + print("💥 Fetching node for core data BluetoothConfigEntity failed: \(nsError)") } } diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 7b01ea15..e75e4f1d 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -106,7 +106,7 @@ struct BluetoothConfig: View { bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin bc.fixedPin = UInt32(fixedPin) ?? 123456 - let adminMessageId = 0//bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!) + let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!) if adminMessageId > 0 { From 8a88b50de5019373d72fc525a58d1cce606191db Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 21 Aug 2022 08:15:27 -0700 Subject: [PATCH 32/36] Remove distance from connected node, clean up spacing --- Meshtastic.xcodeproj/project.pbxproj | 8 +- Meshtastic/Enums/GpsFormats.swift | 56 -------- Meshtastic/Enums/PositionConfigEnums.swift | 132 ++++++++++++++++++ Meshtastic/Views/Nodes/NodeList.swift | 5 +- .../Settings/Config/PositionConfig.swift | 65 --------- 5 files changed, 138 insertions(+), 128 deletions(-) delete mode 100644 Meshtastic/Enums/GpsFormats.swift create mode 100644 Meshtastic/Enums/PositionConfigEnums.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index e21580b3..c4f6ff96 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -74,7 +74,7 @@ DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */; }; DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */; }; DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */; }; - DDB6ABE228B13FB500384BA1 /* GpsFormats.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */; }; + DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */; }; DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */; }; DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */; }; DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */; }; @@ -184,7 +184,7 @@ DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothModes.swift; sourceTree = ""; }; DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = ""; }; DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRoles.swift; sourceTree = ""; }; - DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GpsFormats.swift; sourceTree = ""; }; + DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfigEnums.swift; sourceTree = ""; }; DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenIntervals.swift; sourceTree = ""; }; DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = ""; }; DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiModes.swift; sourceTree = ""; }; @@ -334,7 +334,7 @@ DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */, DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */, DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */, - DDB6ABE128B13FB500384BA1 /* GpsFormats.swift */, + DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */, DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */, DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */, DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */, @@ -745,7 +745,7 @@ C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */, DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */, DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */, - DDB6ABE228B13FB500384BA1 /* GpsFormats.swift in Sources */, + DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */, DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */, DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */, C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */, diff --git a/Meshtastic/Enums/GpsFormats.swift b/Meshtastic/Enums/GpsFormats.swift deleted file mode 100644 index 5f3dd4f6..00000000 --- a/Meshtastic/Enums/GpsFormats.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// GpsFormats.swift -// Meshtastic -// -// Copyright(c) Garth Vander Houwen 8/20/22. -// - -import Foundation - -enum GpsFormats: 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" - } - } - } - func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat { - - switch self { - - case .gpsFormatDec: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec - case .gpsFormatDms: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms - case .gpsFormatUtm: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm - case .gpsFormatMgrs: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs - case .gpsFormatOlc: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc - case .gpsFormatOsgr: - return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr - } - } -} diff --git a/Meshtastic/Enums/PositionConfigEnums.swift b/Meshtastic/Enums/PositionConfigEnums.swift new file mode 100644 index 00000000..4e2f777d --- /dev/null +++ b/Meshtastic/Enums/PositionConfigEnums.swift @@ -0,0 +1,132 @@ +// +// GpsFormats.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/20/22. +// + +import Foundation + +enum GpsFormats: 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" + } + } + } + func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat { + + switch self { + + case .gpsFormatDec: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec + case .gpsFormatDms: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms + case .gpsFormatUtm: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm + case .gpsFormatMgrs: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs + case .gpsFormatOlc: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc + case .gpsFormatOsgr: + return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr + } + } +} + + +enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { + + case fiveSeconds = 5 + case tenSeconds = 10 + case fifteenSeconds = 15 + case thirtySeconds = 0 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 900 + case thirtyMinutes = 1800 + case oneHour = 3600 + case maxInt32 = 2147483647 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + + case .fiveSeconds: + return "Five Seconds" + case .tenSeconds: + return "Ten Seconds" + case .fifteenSeconds: + return "fifteenSeconds" + 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 .maxInt32: + return "On Boot Only" + } + } + } +} + +enum GpsAttemptTimes: Int, CaseIterable, Identifiable { + + case thirtySeconds = 0 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 900 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + 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" + } + } + } +} diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 16009570..0828e7e8 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -81,9 +81,8 @@ struct NodeList: View { Text("Currently Connected").font(.title3).foregroundColor(Color.accentColor) } } - Spacer() } - + Spacer() HStack(alignment: .bottom) { Image(systemName: "clock.badge.checkmark.fill").font(.title3) @@ -99,7 +98,7 @@ struct NodeList: View { } } - if node.positions?.count ?? 0 > 0 { + if node.positions?.count ?? 0 > 0 && (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num != node.num) { Spacer() HStack(alignment: .bottom) { diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 19201fda..f575f451 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -7,71 +7,6 @@ import SwiftUI -enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { - - case thirtySeconds = 0 - case oneMinute = 60 - case fiveMinutes = 300 - case tenMinutes = 600 - case fifteenMinutes = 900 - case thirtyMinutes = 1800 - case oneHour = 3600 - case maxInt32 = 2147483647 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - - 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 .maxInt32: - return "On Boot Only" - } - } - } -} - -enum GpsAttemptTimes: Int, CaseIterable, Identifiable { - - case thirtySeconds = 0 - case oneMinute = 60 - case fiveMinutes = 300 - case tenMinutes = 600 - case fifteenMinutes = 900 - - var id: Int { self.rawValue } - var description: String { - get { - switch self { - - 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" - } - } - } -} - enum PositionBroadcastIntervals: Int, CaseIterable, Identifiable { case thirtySeconds = 30 From e92fd21f01a02e7953bf3a7f7400224e37368657 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 21 Aug 2022 09:57:12 -0700 Subject: [PATCH 33/36] Update protobufs, onboarding stubs --- Meshtastic.xcodeproj/project.pbxproj | 6 ++- Meshtastic/Protobufs/mesh.pb.swift | 45 +++++--------------- Meshtastic/Protobufs/module_config.pb.swift | 12 +++--- Meshtastic/Protobufs/portnums.pb.swift | 1 + Meshtastic/Views/Messages/Contacts.swift | 2 + Meshtastic/Views/Nodes/LocationHistory.swift | 3 -- Meshtastic/Views/Onboarding.swift | 26 +++++++++++ 7 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 Meshtastic/Views/Onboarding.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index c4f6ff96..bf6abcf2 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 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 */; }; + DD4033C228B286B70096A444 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4033C128B286B70096A444 /* Onboarding.swift */; }; DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582528582E9B009B0E59 /* DeviceConfig.swift */; }; DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; }; DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582928585C32009B0E59 /* RangeTestConfig.swift */; }; @@ -126,6 +127,7 @@ DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; DD35018A2852FC79000FC853 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = ""; }; + DD4033C128B286B70096A444 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = ""; }; DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = ""; }; DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = ""; }; @@ -447,8 +449,9 @@ DD47E3D726F2F21A00029299 /* Bluetooth */, DD47E3CA26F0E50300029299 /* Nodes */, DDC2E18B26CE25A70042C5E4 /* Messages */, - DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */, DD4A911C2708C57100501B7E /* Settings */, + DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */, + DD4033C128B286B70096A444 /* Onboarding.swift */, ); path = Views; sourceTree = ""; @@ -739,6 +742,7 @@ DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */, DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */, DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */, + DD4033C228B286B70096A444 /* Onboarding.swift in Sources */, DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */, DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, diff --git a/Meshtastic/Protobufs/mesh.pb.swift b/Meshtastic/Protobufs/mesh.pb.swift index 46dbf236..b5a68d2d 100644 --- a/Meshtastic/Protobufs/mesh.pb.swift +++ b/Meshtastic/Protobufs/mesh.pb.swift @@ -1065,33 +1065,20 @@ struct DataMessage { /// a message a heart or poop emoji. var emoji: UInt32 = 0 - /// - /// Location structure - var location: Location { - get {return _location ?? Location()} - set {_location = newValue} - } - /// Returns true if `location` has been explicitly set. - var hasLocation: Bool {return self._location != nil} - /// Clears the value of `location`. Subsequent reads from it will return its default value. - mutating func clearLocation() {self._location = nil} - var unknownFields = SwiftProtobuf.UnknownStorage() init() {} - - fileprivate var _location: Location? = nil } /// -/// Location of a waypoint to associate with a message -struct Location { +/// Waypoint message, used to share arbitrary locations across the mesh +struct Waypoint { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. /// - /// Id of the location + /// Id of the waypoint var id: UInt32 = 0 /// @@ -1103,19 +1090,19 @@ struct Location { var longitudeI: Int32 = 0 /// - /// Time the location is to expire (epoch) + /// Time the waypoint is to expire (epoch) var expire: UInt32 = 0 /// - /// If true, only allow the original sender to update the location. + /// If true, only allow the original sender to update the waypoint. var locked: Bool = false /// - /// Name of the location - max 30 chars + /// Name of the waypoint - max 30 chars var name: String = String() ///* - /// Description of the location - max 100 chars + /// Description of the waypoint - max 100 chars var description_p: String = String() var unknownFields = SwiftProtobuf.UnknownStorage() @@ -2121,7 +2108,7 @@ extension Routing: @unchecked Sendable {} extension Routing.OneOf_Variant: @unchecked Sendable {} extension Routing.Error: @unchecked Sendable {} extension DataMessage: @unchecked Sendable {} -extension Location: @unchecked Sendable {} +extension Waypoint: @unchecked Sendable {} extension MeshPacket: @unchecked Sendable {} extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {} extension MeshPacket.Priority: @unchecked Sendable {} @@ -2651,7 +2638,6 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 6: .standard(proto: "request_id"), 7: .standard(proto: "reply_id"), 8: .same(proto: "emoji"), - 9: .same(proto: "location"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2668,17 +2654,12 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati case 6: try { try decoder.decodeSingularFixed32Field(value: &self.requestID) }() case 7: try { try decoder.decodeSingularFixed32Field(value: &self.replyID) }() case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }() - case 9: try { try decoder.decodeSingularMessageField(value: &self._location) }() default: break } } } func traverse(visitor: inout V) throws { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every if/case branch local when no optimizations - // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and - // https://github.com/apple/swift-protobuf/issues/1182 if self.portnum != .unknownApp { try visitor.visitSingularEnumField(value: self.portnum, fieldNumber: 1) } @@ -2703,9 +2684,6 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if self.emoji != 0 { try visitor.visitSingularFixed32Field(value: self.emoji, fieldNumber: 8) } - try { if let v = self._location { - try visitor.visitSingularMessageField(value: v, fieldNumber: 9) - } }() try unknownFields.traverse(visitor: &visitor) } @@ -2718,14 +2696,13 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if lhs.requestID != rhs.requestID {return false} if lhs.replyID != rhs.replyID {return false} if lhs.emoji != rhs.emoji {return false} - if lhs._location != rhs._location {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } } -extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = "Location" +extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = "Waypoint" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "id"), 2: .standard(proto: "latitude_i"), @@ -2779,7 +2756,7 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB try unknownFields.traverse(visitor: &visitor) } - static func ==(lhs: Location, rhs: Location) -> Bool { + static func ==(lhs: Waypoint, rhs: Waypoint) -> Bool { if lhs.id != rhs.id {return false} if lhs.latitudeI != rhs.latitudeI {return false} if lhs.longitudeI != rhs.longitudeI {return false} diff --git a/Meshtastic/Protobufs/module_config.pb.swift b/Meshtastic/Protobufs/module_config.pb.swift index 63b87be2..0d6337a8 100644 --- a/Meshtastic/Protobufs/module_config.pb.swift +++ b/Meshtastic/Protobufs/module_config.pb.swift @@ -179,7 +179,7 @@ struct ModuleConfig { /// If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as /// is_uplink_enabled or is_downlink_enabled. /// But if this flag is set, all MQTT features will be disabled and no servers will be contacted. - var disabled: Bool = false + var enabled: Bool = false /// /// The server to use for our MQTT global message gateway feature. @@ -845,7 +845,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = ModuleConfig.protoMessageName + ".MQTTConfig" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "disabled"), + 1: .same(proto: "enabled"), 2: .same(proto: "address"), 3: .same(proto: "username"), 4: .same(proto: "password"), @@ -858,7 +858,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularBoolField(value: &self.disabled) }() + case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }() case 2: try { try decoder.decodeSingularStringField(value: &self.address) }() case 3: try { try decoder.decodeSingularStringField(value: &self.username) }() case 4: try { try decoder.decodeSingularStringField(value: &self.password) }() @@ -869,8 +869,8 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message } func traverse(visitor: inout V) throws { - if self.disabled != false { - try visitor.visitSingularBoolField(value: self.disabled, fieldNumber: 1) + if self.enabled != false { + try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1) } if !self.address.isEmpty { try visitor.visitSingularStringField(value: self.address, fieldNumber: 2) @@ -888,7 +888,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message } static func ==(lhs: ModuleConfig.MQTTConfig, rhs: ModuleConfig.MQTTConfig) -> Bool { - if lhs.disabled != rhs.disabled {return false} + if lhs.enabled != rhs.enabled {return false} if lhs.address != rhs.address {return false} if lhs.username != rhs.username {return false} if lhs.password != rhs.password {return false} diff --git a/Meshtastic/Protobufs/portnums.pb.swift b/Meshtastic/Protobufs/portnums.pb.swift index 10cef65e..1f0ae3a2 100644 --- a/Meshtastic/Protobufs/portnums.pb.swift +++ b/Meshtastic/Protobufs/portnums.pb.swift @@ -79,6 +79,7 @@ enum PortNum: SwiftProtobuf.Enum { /// /// Waypoint payloads. + /// Payload is a [Waypoint](/docs/developers/protobufs/api#waypoint) message case waypointApp // = 8 /// diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift index 2b724f14..c27485a3 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -11,6 +11,8 @@ struct Contacts: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + + @State var onboarding = true @FetchRequest( sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)], diff --git a/Meshtastic/Views/Nodes/LocationHistory.swift b/Meshtastic/Views/Nodes/LocationHistory.swift index 0d132998..52165e2d 100644 --- a/Meshtastic/Views/Nodes/LocationHistory.swift +++ b/Meshtastic/Views/Nodes/LocationHistory.swift @@ -88,12 +88,9 @@ struct LocationHistory: View { .font(.subheadline) .foregroundColor(.accentColor) .symbolRenderingMode(.hierarchical) - Text("Time:") - .font(.caption) DateTimeText(dateTime: mappin.time) .foregroundColor(.gray) .font(.caption) - } } } diff --git a/Meshtastic/Views/Onboarding.swift b/Meshtastic/Views/Onboarding.swift new file mode 100644 index 00000000..89769c08 --- /dev/null +++ b/Meshtastic/Views/Onboarding.swift @@ -0,0 +1,26 @@ +// +// Onboarding.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/21/22. +// + +import SwiftUI + +struct Onboarding: View { + + var body: some View { + + VStack { + + Text("🗺️ Set Your Region to Mesh and Message") + .font(.largeTitle) + .foregroundColor(.red) + + Text("Your region is currently set to UNSET, please set your device to the appropriate region under Settings > LoRa, after you set your region your Meshtastic device will reboot.") + .font(.callout) + .padding() + + } + } +} From 165b0cc4c6ef1e14c009287116b93854ca8787d5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 21 Aug 2022 19:06:10 -0700 Subject: [PATCH 34/36] Update Location to Waypoint per new photos --- Meshtastic/Helpers/BLEManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 08a77c6c..a5922abc 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -765,7 +765,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } - var waypointPacket = Location() + var waypointPacket = Waypoint() waypointPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) waypointPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) From 77945368ffc6f331d3205640a259b5c05064c4e2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 23 Aug 2022 07:59:15 -0700 Subject: [PATCH 35/36] Updates to data model --- Meshtastic.xcodeproj/project.pbxproj | 4 +- Meshtastic/Helpers/BLEManager.swift | 6 +- Meshtastic/Helpers/MeshPackets.swift | 10 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 231 ++++++++++++++++++ .../Views/Messages/UserMessageList.swift | 5 +- 6 files changed, 249 insertions(+), 9 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 8.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index bf6abcf2..c237cfe7 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -128,6 +128,7 @@ DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; DD35018A2852FC79000FC853 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = ""; }; DD4033C128B286B70096A444 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; + DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 8.xcdatamodel"; sourceTree = ""; }; DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = ""; }; DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = ""; }; DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = ""; }; @@ -1139,6 +1140,7 @@ DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */, DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */, DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */, DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */, @@ -1147,7 +1149,7 @@ DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */, DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */, ); - currentVersion = DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */; + currentVersion = DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index a5922abc..b04f5cb6 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -131,7 +131,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.timeoutTimerCount += 1 - if timeoutTimerCount == 5 { + if timeoutTimerCount == 10 { if connectedPeripheral != nil { @@ -182,7 +182,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread let context = ["name": "@\(peripheral.name ?? "Unknown")"] - self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) + self.timeoutTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) RunLoop.current.add(self.timeoutTimer!, forMode: .common) } @@ -273,7 +273,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Happens when device is manually reset / powered off // We will try and re-connect to this device - lastConnectionError = "🚨 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds." + lastConnectionError = "🚨 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within one minute." if peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" { if meshLoggingEnabled { MeshLogger.log("ℹ️ BLE Reconnecting: \(peripheral.name ?? "Unknown")") } self.connectTo(peripheral: peripheral) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 79419d51..332d1a1f 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -592,7 +592,8 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newCannedMessageConfig.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) newCannedMessageConfig.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) } - + + newCannedMessageConfig.num = nodeNum fetchedNode[0].cannedMessageConfig = newCannedMessageConfig } else { @@ -623,6 +624,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue) fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue) } + fetchedNode[0].cannedMessageConfig?.num = nodeNum } do { @@ -698,6 +700,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newExternalNotificationConfig.outputMilliseconds = Int32(config.externalNotification.outputMs) } + newExternalNotificationConfig.num = nodeNum fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig } else { @@ -720,6 +723,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj fetchedNode[0].externalNotificationConfig?.output = Int32(config.externalNotification.output) fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.externalNotification.outputMs) } + fetchedNode[0].externalNotificationConfig?.num = nodeNum } do { @@ -781,7 +785,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj newRangeTestConfig.enabled = config.rangeTest.enabled newRangeTestConfig.save = config.rangeTest.save } - + newRangeTestConfig.num = nodeNum fetchedNode[0].rangeTestConfig = newRangeTestConfig } else { @@ -1391,7 +1395,7 @@ func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb fetchedNode[0].id = Int64(packet.from) fetchedNode[0].num = Int64(packet.from) - fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime))) + fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time))) fetchedNode[0].snr = packet.rxSnr fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index bf45c105..4a26e238 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModel v 7.xcdatamodel + MeshtasticDataModel v 8.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 8.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 8.xcdatamodel/contents new file mode 100644 index 00000000..b6578184 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModel v 8.xcdatamodel/contents @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 3de52e83..8715a463 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -299,6 +299,9 @@ struct UserMessageList: View { if message.receivedACK { Text("Acknowledged").font(.caption2).foregroundColor(.gray) + } else { + + Text("").font(.caption2).foregroundColor(.gray) } if message.ackError > 0 { @@ -316,9 +319,9 @@ struct UserMessageList: View { Spacer(minLength:50) } } - .id(message.messageId) .padding([.leading, .trailing]) .frame(maxWidth: .infinity) + .id(message.messageId) .alert(isPresented: $showDeleteMessageAlert) { Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."), primaryButton: .destructive(Text("Delete")) { From ef9d6190257588b8918e15e255d86316b46d92a4 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 24 Aug 2022 20:21:10 -0700 Subject: [PATCH 36/36] Update ack status --- Meshtastic/Views/Messages/UserMessageList.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 3de52e83..2d07ea7f 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -296,12 +296,17 @@ struct UserMessageList: View { HStack { - if message.receivedACK { - + if currentUser && message.receivedACK { + + // Ack Received Text("Acknowledged").font(.caption2).foregroundColor(.gray) - } - - if message.ackError > 0 { + + } else if currentUser && message.ackError == 0 { + + // Empty Error + Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.yellow) + + } else if currentUser && message.ackError > 0 { let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true)