Merge branch 'main' of github.com:meshtastic/Meshtastic-Apple

This commit is contained in:
Nikola Dašić 2025-05-12 15:37:49 +02:00
commit ba84a5d566
148 changed files with 22168 additions and 18309 deletions

View file

@ -30,7 +30,7 @@ struct AmbientLightingConfig: View {
Form {
ConfigHeader(title: "Ambient Lighting", config: \.ambientLightingConfig, node: node, onAppear: setAmbientLightingConfigValue)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $ledState) {
Label("LED State", systemImage: ledState ? "lightbulb.led.fill" : "lightbulb.led")

View file

@ -42,11 +42,11 @@ struct CannedMessagesConfig: View {
Form {
ConfigHeader(title: "Canned messages", config: \.cannedMessageConfig, node: node, onAppear: setCannedMessagesValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "list.bullet.rectangle.fill")
Label("Enabled", systemImage: "list.bullet.rectangle.fill")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -103,7 +103,7 @@ struct CannedMessagesConfig: View {
Picker("Pin A", selection: $inputbrokerPinA) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -118,7 +118,7 @@ struct CannedMessagesConfig: View {
Picker("Pin B", selection: $inputbrokerPinB) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -133,7 +133,7 @@ struct CannedMessagesConfig: View {
Picker("Press Pin", selection: $inputbrokerPinPress) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}

View file

@ -47,10 +47,10 @@ struct DetectionSensorConfig: View {
Form {
ConfigHeader(title: "Detection Sensor", config: \.detectionSensorConfig, node: node, onAppear: setDetectionSensorValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "dot.radiowaves.right")
Label("Enabled", systemImage: "dot.radiowaves.right")
Text("Enables the detection sensor module, it needs to be enabled on both the node with the sensor, and any nodes that you want to receive detection sensor text messages or view the detection sensor log and chart.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -108,7 +108,7 @@ struct DetectionSensorConfig: View {
Picker("GPIO Pin to monitor", selection: $monitorPin) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -130,7 +130,7 @@ struct DetectionSensorConfig: View {
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
Section(header: Text("update.interval")) {
Section(header: Text("Update Interval")) {
Picker("Minimum time between detection broadcasts", selection: $minimumBroadcastSecs) {
ForEach(UpdateIntervals.allCases) { ui in
Text(ui.description).tag(ui.rawValue)
@ -181,7 +181,7 @@ struct DetectionSensorConfig: View {
}
}
}
.navigationTitle("detection.sensor.config")
.navigationTitle("Detection Sensor Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(

View file

@ -39,10 +39,10 @@ struct ExternalNotificationConfig: View {
Form {
ConfigHeader(title: "External notification", config: \.externalNotificationConfig, node: node, onAppear: setExternalNotificationValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "megaphone")
Label("Enabled", systemImage: "megaphone")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -82,7 +82,7 @@ struct ExternalNotificationConfig: View {
Picker("Output pin GPIO", selection: $output) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -138,7 +138,7 @@ struct ExternalNotificationConfig: View {
Picker("Output pin buzzer GPIO ", selection: $outputBuzzer) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -148,7 +148,7 @@ struct ExternalNotificationConfig: View {
Picker("Output pin vibra GPIO", selection: $outputVibra) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -189,7 +189,7 @@ struct ExternalNotificationConfig: View {
}
}
}
.navigationTitle("external.notification.config")
.navigationTitle("External Notification Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(

View file

@ -31,6 +31,7 @@ struct MQTTConfig: View {
@State var defaultTopic = "msh/US"
@State var nearbyTopics = [String]()
@State var mapReportingEnabled = false
@AppStorage("mapReportingOptIn") private var mapReportingOptIn: Bool = false
@State var mapPublishIntervalSecs = 3600
@State var mapPositionPrecision: Double = 14.0
@ -50,23 +51,23 @@ struct MQTTConfig: View {
ConfigHeader(title: "MQTT", config: \.mqttConfig, node: node, onAppear: setMqttValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "dot.radiowaves.up.forward")
Label("Enabled", systemImage: "dot.radiowaves.up.forward")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Toggle(isOn: $proxyToClientEnabled) {
Label("mqtt.clientproxy", systemImage: "iphone.radiowaves.left.and.right")
Label("MQTT Client Proxy", systemImage: "iphone.radiowaves.left.and.right")
Text("Utilizes the network connection on your phone to connect to MQTT.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if enabled && proxyToClientEnabled && node?.mqttConfig?.proxyToClientEnabled ?? false == true {
Toggle(isOn: $mqttConnected) {
Label(mqttConnected ? "mqtt.disconnect".localized : "mqtt.connect".localized, systemImage: "server.rack")
Label("Connect to MQTT via Proxy", systemImage: "server.rack")
if bleManager.mqttError.count > 0 {
Text(bleManager.mqttError)
.fixedSize(horizontal: false, vertical: true)
@ -92,12 +93,30 @@ struct MQTTConfig: View {
}
Section(header: Text("Map Report")) {
Toggle(isOn: $mapReportingEnabled) {
Label("enabled", systemImage: "map")
Label("Enabled", systemImage: "map")
Text("Your node will periodically send an unencrypted map report packet to the configured MQTT server, this includes id, short and long name, approximate location, hardware model, role, firmware version, LoRa region, modem preset and primary channel name.")
.foregroundColor(.gray)
.font(.caption)
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
if mapReportingEnabled {
Text("Consent to Share Unencrypted Node Data via MQTT")
Text("By enabling this feature, you acknowledge and expressly consent to the transmission of your devices real-time geographic location over the MQTT protocol without encryption. This location data may be used for purposes such as live map reporting, device tracking, and related telemetry functions.")
.foregroundColor(.gray)
.font(.caption)
Text("Please be advised that because the map report is not encrypted, your data may be stored and displayed permanently by third parties. Meshtastic does not assume responsibility for any such storage, display or disclosure of this data.")
.foregroundColor(.gray)
.font(.caption)
Toggle(isOn: $mapReportingOptIn) {
Label("I have read and understand the above. I voluntarily consent to the unencrypted transmission of my node data via MQTT.", systemImage: "hand.raised")
.foregroundColor(.gray)
.font(.callout)
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
if mapReportingEnabled && mapReportingOptIn {
Picker("Map Publish Interval", selection: $mapPublishIntervalSecs ) {
ForEach(UpdateIntervals.allCases) { ui in
if ui.rawValue >= 3600 {
@ -108,6 +127,9 @@ struct MQTTConfig: View {
.pickerStyle(DefaultPickerStyle())
VStack(alignment: .leading) {
Label("Approximate Location", systemImage: "location.slash.circle.fill")
Text("To comply with privacy laws like CCPA and GDPR, we avoid sharing exact location data. Instead, we use anonymized or approximate (imprecise) location information to protect your privacy.")
.foregroundColor(.gray)
.font(.callout)
Slider(value: $mapPositionPrecision, in: 11...14, step: 1) {
} minimumValueLabel: {
Image(systemName: "minus")
@ -178,8 +200,8 @@ struct MQTTConfig: View {
.autocorrectionDisabled()
if address != "mqtt.meshtastic.org" {
HStack {
Label("mqtt.username", systemImage: "person.text.rectangle")
TextField("mqtt.username", text: $username)
Label("Username", systemImage: "person.text.rectangle")
TextField("Username", text: $username)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
@ -197,8 +219,8 @@ struct MQTTConfig: View {
.keyboardType(.default)
.scrollDismissesKeyboard(.interactively)
HStack {
Label("password", systemImage: "wallet.pass")
TextField("password", text: $password)
Label("Password", systemImage: "wallet.pass")
TextField("Password", text: $password)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
@ -266,6 +288,10 @@ struct MQTTConfig: View {
if newProxyToClientEnabled != node?.mqttConfig?.proxyToClientEnabled { hasChanges = true }
}
.onChange(of: address) { _, newAddress in
if address.lowercased() == "mqtt.meshtastic.org" {
username = "meshdev"
password = "large4cats"
}
if newAddress != node?.mqttConfig?.address ?? "" { hasChanges = true }
}
.onChange(of: username) { _, newUsername in
@ -314,7 +340,7 @@ struct MQTTConfig: View {
if newMapPublishIntervalSecs != node?.mqttConfig?.mapPublishIntervalSecs ?? -1 { hasChanges = true }
}
}
.navigationTitle("mqtt.config")
.navigationTitle("MQTT Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(
@ -362,7 +388,6 @@ struct MQTTConfig: View {
}
if let placemarks = placemarks, let placemark = placemarks.first {
let cc = locale.region?.identifier ?? "UNK"
/// Country Topic unless your region is a country
if !(region?.isCountry ?? false) {
let countryTopic = defaultTopic + "/" + (placemark.isoCountryCode ?? "")

View file

@ -22,17 +22,17 @@ struct PaxCounterConfig: View {
var body: some View {
Form {
ConfigHeader(title: "config.module.paxcounter.title", config: \.powerConfig, node: node, onAppear: setPaxValues)
ConfigHeader(title: "PAX Counter Config", config: \.powerConfig, node: node, onAppear: setPaxValues)
Section {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "figure.walk.motion")
Text("config.module.paxcounter.enabled.description")
Label("Enabled", systemImage: "figure.walk.motion")
Text("When enabled the PAX Counter module counts the number of people passing by using WiFi and Bluetooth. Both WiFI and Bluetooth must be disabled for PAX counter to work.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.listRowSeparator(.visible)
if enabled {
Picker("config.module.paxcounter.updateinterval", selection: $paxcounterUpdateInterval) {
Picker("Update Interval", selection: $paxcounterUpdateInterval) {
ForEach(UpdateIntervals.allCases) { at in
if at.rawValue >= 300 {
Text(at.description)
@ -41,16 +41,16 @@ struct PaxCounterConfig: View {
}
.pickerStyle(DefaultPickerStyle())
.listRowSeparator(.hidden)
Text("config.module.paxcounter.updateinterval.description")
Text("How often we can send a message to the mesh when people are detected.")
.foregroundColor(.gray)
.font(.callout)
}
} header: {
Text("options")
Text("Options")
}
}
.disabled(self.bleManager.connectedPeripheral == nil || node?.powerConfig == nil)
.navigationTitle("config.module.paxcounter.title")
.navigationTitle("PAX Counter Config")
.navigationBarItems(trailing: ZStack {
ConnectedDevice(
bluetoothOn: bleManager.isSwitchedOn,

View file

@ -27,9 +27,9 @@ struct RangeTestConfig: View {
Form {
ConfigHeader(title: "Range", config: \.rangeTestConfig, node: node, onAppear: setRangeTestValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "figure.walk")
Label("Enabled", systemImage: "figure.walk")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.listRowSeparator(.visible)
@ -45,7 +45,7 @@ struct RangeTestConfig: View {
.font(.callout)
Toggle(isOn: $save) {
Label("save", systemImage: "square.and.arrow.down.fill")
Label("Save", systemImage: "square.and.arrow.down.fill")
Text("Saves a CSV with the range test message details, currently only available on ESP32 devices with a web server.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -71,7 +71,7 @@ struct RangeTestConfig: View {
}
}
}
.navigationTitle("range.test.config")
.navigationTitle("Range Test Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(

View file

@ -22,12 +22,12 @@ struct RtttlConfig: View {
var body: some View {
VStack {
Form {
ConfigHeader(title: "ringtone", config: \.rtttlConfig, node: node, onAppear: setRtttLConfigValue)
ConfigHeader(title: "Ringtone", config: \.rtttlConfig, node: node, onAppear: setRtttLConfigValue)
Section(header: Text("options")) {
Section(header: Text("Options")) {
HStack {
Label("ringtone", systemImage: "music.quarternote.3")
TextField("config.ringtone.label", text: $ringtone, axis: .vertical)
Label("Ringtone", systemImage: "music.quarternote.3")
TextField("Ringtone Transfer Language", text: $ringtone, axis: .vertical)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
@ -43,7 +43,7 @@ struct RtttlConfig: View {
}
.keyboardType(.default)
.listRowSeparator(.hidden)
Text("config.ringtone.description")
Text("Ringtone Transfer Language(RTTTL) Ringtone String used by supported buzzers in external notifications.")
.foregroundColor(.gray)
.font(.callout)
}
@ -62,7 +62,7 @@ struct RtttlConfig: View {
}
}
}
.navigationTitle("config.ringtone.title")
.navigationTitle("Ringtone Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(

View file

@ -33,15 +33,15 @@ struct SerialConfig: View {
Form {
ConfigHeader(title: "Serial", config: \.serialConfig, node: node, onAppear: setSerialValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "terminal")
Label("Enabled", systemImage: "terminal")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Toggle(isOn: $echo) {
Label("echo", systemImage: "repeat")
Label("Echo", systemImage: "repeat")
Text("If set, any packets you send will be echoed back to your device.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -53,7 +53,7 @@ struct SerialConfig: View {
}
.pickerStyle(DefaultPickerStyle())
.listRowSeparator(/*@START_MENU_TOKEN@*/.visible/*@END_MENU_TOKEN@*/)
Picker("timeout", selection: $timeout ) {
Picker("Timeout", selection: $timeout ) {
ForEach(SerialTimeoutIntervals.allCases) { sti in
Text(sti.description)
}
@ -64,7 +64,7 @@ struct SerialConfig: View {
.foregroundColor(.gray)
.font(.callout)
Picker("mode", selection: $mode ) {
Picker("Mode", selection: $mode ) {
ForEach(SerialModeTypes.allCases) { smt in
Text(smt.description)
}
@ -76,7 +76,7 @@ struct SerialConfig: View {
Picker("Receive data (rxd) GPIO pin", selection: $rxd) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -88,7 +88,7 @@ struct SerialConfig: View {
Picker("Transmit data (txd) GPIO pin", selection: $txd) {
ForEach(0..<49) {
if $0 == 0 {
Text("unset")
Text("Unset")
} else {
Text("Pin \($0)")
}
@ -126,7 +126,7 @@ struct SerialConfig: View {
}
}
}
.navigationTitle("serial.config")
.navigationTitle("Serial Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(

View file

@ -34,9 +34,9 @@ struct StoreForwardConfig: View {
Form {
ConfigHeader(title: "Store & Forward", config: \.storeForwardConfig, node: node, onAppear: setStoreAndForwardValues)
Section(header: Text("options")) {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("enabled", systemImage: "envelope.arrow.triangle.branch")
Label("Enabled", systemImage: "envelope.arrow.triangle.branch")
Text("Enables the store and forward module.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
@ -46,11 +46,11 @@ struct StoreForwardConfig: View {
if enabled {
Section(header: Text("settings")) {
Toggle(isOn: $heartbeat) {
Label("storeforward.heartbeat", systemImage: "waveform.path.ecg")
Label("Send Heartbeat", systemImage: "waveform.path.ecg")
Text("Send a heartbeat to advertise the server's presence.")
}
Picker("Number of records", selection: $records) {
Text("unset").tag(0)
Text("Unset").tag(0)
Text("25").tag(25)
Text("50").tag(50)
Text("75").tag(75)
@ -58,7 +58,7 @@ struct StoreForwardConfig: View {
}
.pickerStyle(DefaultPickerStyle())
Picker("History Return Max", selection: $historyReturnMax) {
Text("unset").tag(0)
Text("Unset").tag(0)
Text("25").tag(25)
Text("50").tag(50)
Text("75").tag(75)
@ -66,7 +66,7 @@ struct StoreForwardConfig: View {
}
.pickerStyle(DefaultPickerStyle())
Picker("History Return Window", selection: $historyReturnWindow) {
Text("unset").tag(0)
Text("Unset").tag(0)
Text("One Minute").tag(60)
Text("Five Minutes").tag(300)
Text("Ten Minutes").tag(600)

View file

@ -32,7 +32,7 @@ struct TelemetryConfig: View {
Form {
ConfigHeader(title: "Telemetry", config: \.telemetryConfig, node: node, onAppear: setTelemetryValues)
Section(header: Text("update.interval")) {
Section(header: Text("Update Interval")) {
Picker("Device Metrics", selection: $deviceUpdateInterval ) {
ForEach(UpdateIntervals.allCases) { ui in
if ui.rawValue >= 900 {
@ -46,7 +46,7 @@ struct TelemetryConfig: View {
.foregroundColor(.gray)
.font(.callout)
.listRowSeparator(.visible)
Picker("Sensor Metrics", selection: $environmentUpdateInterval ) {
Picker("Environment Metrics", selection: $environmentUpdateInterval ) {
ForEach(UpdateIntervals.allCases) { ui in
if ui.rawValue >= 900 {
Text(ui.description)
@ -55,7 +55,7 @@ struct TelemetryConfig: View {
}
.pickerStyle(DefaultPickerStyle())
.listRowSeparator(.hidden)
Text("How often sensor metrics are sent out over the mesh. Default is 30 minutes.")
Text("How often environment metrics are sent out over the mesh. Default is 30 minutes.")
.foregroundColor(.gray)
.font(.callout)
}
@ -64,7 +64,7 @@ struct TelemetryConfig: View {
.foregroundColor(.gray)
.font(.callout)
Toggle(isOn: $environmentMeasurementEnabled) {
Label("enabled", systemImage: "chart.xyaxis.line")
Label("Enabled", systemImage: "chart.xyaxis.line")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Toggle(isOn: $environmentScreenEnabled) {
@ -78,7 +78,7 @@ struct TelemetryConfig: View {
}
Section(header: Text("Power Options")) {
Toggle(isOn: $powerMeasurementEnabled) {
Label("enabled", systemImage: "bolt")
Label("Enabled", systemImage: "bolt")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.listRowSeparator(.visible)
@ -124,7 +124,7 @@ struct TelemetryConfig: View {
}
}
}
.navigationTitle("telemetry.config")
.navigationTitle("Telemetry Config")
.navigationBarItems(
trailing: ZStack {
ConnectedDevice(