From b4f64988b9fc160173481077fc44d232fdc30861 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Mon, 15 Dec 2025 18:42:03 -0500 Subject: [PATCH] Improvements to ESP32 firmware screen --- Localizable.xcstrings | 37 +++++ .../Firmware/ESP32 DFU/ESP32DFUSheet.swift | 127 +++++++++++++----- .../Views/Settings/Firmware/Firmware.swift | 2 +- 3 files changed, 132 insertions(+), 34 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 874f2e5a..f077b193 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -9443,6 +9443,7 @@ } }, "Currently the recommended way to update ESP32 devices is using the web flasher on a desktop computer from a chrome based browser. It does not work on mobile devices or over BLE." : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -10190,6 +10191,10 @@ } } }, + "Desktop Required" : { + "comment" : "A heading explaining that the recommended way to update an ESP32 device is using the Web Flasher on a desktop computer.", + "isCommentAutoGenerated" : true + }, "Details" : { "comment" : "The title of the view that lists detailed information about a single database entity.", "isCommentAutoGenerated" : true @@ -13582,6 +13587,7 @@ } }, "ESP 32 OTA update is a work in progress, click the button below to send your device a reboot into ota admin message." : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -13616,6 +13622,7 @@ } }, "ESP32 Device Firmware Update" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -13649,6 +13656,10 @@ } } }, + "ESP32 Update" : { + "comment" : "The title of the view.", + "isCommentAutoGenerated" : true + }, "Ethernet Options" : { "localizations" : { "it" : { @@ -15481,6 +15492,10 @@ } } }, + "For advanced use cases, you can send a reboot command to the node using the following commands:" : { + "comment" : "A description of how to send a reboot command to an ESP32.", + "isCommentAutoGenerated" : true + }, "For all Mqtt functionality other than the map report you must also set uplink and downlink for each channel you want to bridge over Mqtt." : { "localizations" : { "it" : { @@ -24990,6 +25005,10 @@ } } }, + "Open Web Flasher" : { + "comment" : "A link label that says \"Open Web Flasher\" and has an arrow icon pointing to it.", + "isCommentAutoGenerated" : true + }, "Optimized for 2 color displays" : { "localizations" : { "it" : { @@ -32315,6 +32334,10 @@ } } }, + "Send Normal Reboot" : { + "comment" : "A button that attempts to force an ESP32 device to reboot into normal operation.", + "isCommentAutoGenerated" : true + }, "Send Notifications" : { "localizations" : { "de" : { @@ -32332,6 +32355,7 @@ } }, "Send Reboot OTA" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { @@ -36377,6 +36401,10 @@ } } }, + "The **Web Flasher** does not support updating on this device or over USB or BLE." : { + "comment" : "A footnote explaining that the Web Flasher does not support updating on this device or over USB or BLE.", + "isCommentAutoGenerated" : true + }, "The amount of time to wait before we consider your packet as done." : { "localizations" : { "it" : { @@ -36735,6 +36763,10 @@ } } }, + "The recommended way to update ESP32 devices is using the **Web Flasher** on a desktop computer (Chrome-based browser)." : { + "comment" : "A description of how to update an ESP32 device using the Web Flasher.", + "isCommentAutoGenerated" : true + }, "The region where you will be using your radios." : { "localizations" : { "de" : { @@ -40602,6 +40634,10 @@ } } }, + "Utilities" : { + "comment" : "A section header that indicates advanced utilities for the ESP32 update process.", + "isCommentAutoGenerated" : true + }, "Utilizes the network connection on your phone to connect to MQTT." : { "localizations" : { "it" : { @@ -41374,6 +41410,7 @@ } }, "Web Flasher" : { + "extractionState" : "stale", "localizations" : { "it" : { "stringUnit" : { diff --git a/Meshtastic/Views/Settings/Firmware/ESP32 DFU/ESP32DFUSheet.swift b/Meshtastic/Views/Settings/Firmware/ESP32 DFU/ESP32DFUSheet.swift index 6ba00056..6ea90071 100644 --- a/Meshtastic/Views/Settings/Firmware/ESP32 DFU/ESP32DFUSheet.swift +++ b/Meshtastic/Views/Settings/Firmware/ESP32 DFU/ESP32DFUSheet.swift @@ -14,43 +14,59 @@ struct ESP32DFUSheet: View { @Environment(\.managedObjectContext) var context var body: some View { - NavigationView { // Use a NavigationView for a title bar - VStack(alignment: .leading, spacing: 20.0) { - Text("ESP32 Device Firmware Update") - .font(.title) - Text("Currently the recommended way to update ESP32 devices is using the web flasher on a desktop computer from a chrome based browser. It does not work on mobile devices or over BLE.") - .font(.body) - Link("Web Flasher", destination: URL(string: "https://flash.meshtastic.org")!) - .font(.body) - .padding() - Text("ESP 32 OTA update is a work in progress, click the button below to send your device a reboot into ota admin message.") - .font(.body) - HStack(alignment: .center) { - Spacer() - Button { - let connectedNode = getNodeInfo(id: accessoryManager.activeDeviceNum ?? 0, context: context) - if let connectedNode, let user = connectedNode.user { - Task { - do { - try await accessoryManager.sendRebootOta(fromUser: user, toUser: user) - } catch { - Logger.mesh.error("Reboot Failed") - } + NavigationStack { + ScrollView { + VStack(spacing: 24) { + + // MARK: - Info Card + VStack(alignment: .leading, spacing: 12) { + Label("Desktop Required", systemImage: "desktopcomputer") + .font(.headline) + + Text("The recommended way to update ESP32 devices is using the **Web Flasher** on a desktop computer (Chrome-based browser).") + .fixedSize(horizontal: false, vertical: true) + + Text("The **Web Flasher** does not support updating on this device or over USB or BLE.") + .font(.caption) + .foregroundStyle(.secondary) + + Link(destination: URL(string: "https://flash.meshtastic.org")!) { + HStack { + Text("Open Web Flasher") + Image(systemName: "arrow.up.right") } + .frame(maxWidth: .infinity) } - } label: { - Label("Send Reboot OTA", systemImage: "square.and.arrow.down") + .buttonStyle(.bordered) + .controlSize(.regular) } - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.regular) - .padding(5) - Spacer() + .padding() + .background(Color(UIColor.secondarySystemBackground)) + .cornerRadius(12) + + Divider() + + // MARK: - OTA Section + VStack(alignment: .leading, spacing: 16) { + Label("Utilities", systemImage: "exclamationmark.triangle") + .font(.headline) + .foregroundStyle(.orange) + + Text("For advanced use cases, you can send a reboot command to the node using the following commands:") + .fixedSize(horizontal: false, vertical: true) + + resetIntoOTAButton() + normalRebootButton() + } + .padding(.horizontal) } - }.padding(20.0) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - // 2. Create a button that calls dismiss() + .padding(.top) + }.padding() + // Standard Navigation Bar Setup + .navigationTitle("ESP32 Update") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .cancellationAction) { // Standard placement for "Done" or "Close" Button("Done") { dismiss() } @@ -58,8 +74,53 @@ struct ESP32DFUSheet: View { } } } + + + @ViewBuilder + func normalRebootButton() -> some View { + Button { + let connectedNode = getNodeInfo(id: accessoryManager.activeDeviceNum ?? 0, context: context) + if let connectedNode, let user = connectedNode.user { + Task { + do { + try await accessoryManager.sendRebootOta(fromUser: user, toUser: user) + } catch { + Logger.mesh.error("Reboot Failed") + } + } + } + } label: { + Label("Send Normal Reboot", systemImage: "square.and.arrow.down") + }.buttonStyle(.borderedProminent) + .controlSize(.large) + .frame(maxWidth: .infinity) + .cornerRadius(10).disabled(accessoryManager.activeDeviceNum == nil) + } + + @ViewBuilder + func resetIntoOTAButton() -> some View { + Button { + let connectedNode = getNodeInfo(id: accessoryManager.activeDeviceNum ?? 0, context: context) + if let connectedNode, let user = connectedNode.user { + Task { + do { + try await accessoryManager.sendEnterDfuMode(fromUser: user, toUser: user) + } catch { + Logger.mesh.error("Reboot Failed") + } + } + } + } label: { + Label(" Send Reboot into DFU", systemImage: "square.and.arrow.down") + }.buttonStyle(.borderedProminent) + .controlSize(.large) + .frame(maxWidth: .infinity) + .cornerRadius(10).disabled(accessoryManager.activeDeviceNum == nil) + } } #Preview { ESP32DFUSheet() + // Mock environment object for preview to work + .environmentObject(AccessoryManager()) } diff --git a/Meshtastic/Views/Settings/Firmware/Firmware.swift b/Meshtastic/Views/Settings/Firmware/Firmware.swift index ced57922..f48b061d 100644 --- a/Meshtastic/Views/Settings/Firmware/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware/Firmware.swift @@ -289,7 +289,7 @@ private struct FirmwareRow: View { case .uf2: self.showInstallationSheet = .uf2 case .bin: - self.unsupporedInstallationMessage = true + self.showInstallationSheet = .bin case .otaZip: self.showInstallationSheet = .otaZip }