mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #179 from meshtastic/firmware_version_sheet
Firmware version sheet
This commit is contained in:
commit
8a7d15840e
6 changed files with 163 additions and 185 deletions
|
|
@ -43,7 +43,6 @@
|
|||
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */; };
|
||||
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */; };
|
||||
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; };
|
||||
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B85A728009258000ACD6B /* ShareChannel.swift */; };
|
||||
DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* LocationHistory.swift */; };
|
||||
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; };
|
||||
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; };
|
||||
|
|
@ -157,7 +156,6 @@
|
|||
DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalNotificationConfig.swift; sourceTree = "<group>"; };
|
||||
DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = "<group>"; };
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = "<group>"; };
|
||||
DD6B85A728009258000ACD6B /* ShareChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannel.swift; sourceTree = "<group>"; };
|
||||
DD73FD1028750779000852D6 /* LocationHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHistory.swift; sourceTree = "<group>"; };
|
||||
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
|
||||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -300,7 +298,6 @@
|
|||
DD3501882852FC3B000FC853 /* Settings.swift */,
|
||||
DD4A911D2708C65400501B7E /* AppSettings.swift */,
|
||||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */,
|
||||
DD6B85A728009258000ACD6B /* ShareChannel.swift */,
|
||||
DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */,
|
||||
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */,
|
||||
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */,
|
||||
|
|
@ -711,7 +708,6 @@
|
|||
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 */,
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
DDC4D568275499A500A4208E /* Persistence.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
@Published var connectedPeripheral: Peripheral!
|
||||
@Published var lastConnectionError: String
|
||||
@Published var minimumVersion = "1.3.41"
|
||||
@Published var connectedVersion: String
|
||||
@Published var invalidVersion = false
|
||||
@Published var preferredPeripheral = false
|
||||
|
||||
@Published var isSwitchedOn: Bool = false
|
||||
@Published var isScanning: Bool = false
|
||||
|
|
@ -41,6 +44,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
var timeoutTimer: Timer?
|
||||
var timeoutTimerCount = 0
|
||||
|
||||
var configTimeoutTimer: Timer?
|
||||
|
||||
var positionTimer: Timer?
|
||||
|
||||
let broadcastNodeNum: UInt32 = 4294967295
|
||||
|
|
@ -102,7 +107,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if isSwitchedOn {
|
||||
|
||||
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: nil)
|
||||
self.isScanning = self.centralManager.isScanning
|
||||
isScanning = centralManager.isScanning
|
||||
|
||||
print("✅ Scanning Started")
|
||||
}
|
||||
|
|
@ -114,7 +119,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if centralManager.isScanning {
|
||||
|
||||
self.centralManager.stopScan()
|
||||
self.isScanning = self.centralManager.isScanning
|
||||
isScanning = centralManager.isScanning
|
||||
print("🛑 Stopped Scanning")
|
||||
}
|
||||
}
|
||||
|
|
@ -155,6 +160,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)") }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: BLE Connect functions
|
||||
/// The action after the timeout-timer has fired
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - timer: The time that fired the event
|
||||
///
|
||||
@objc func configTimeoutTimerFired(timer: Timer) {
|
||||
|
||||
self.lastConnectionError = "🚨 Update your firmware"
|
||||
self.connectedVersion = "1.2.65"
|
||||
self.invalidVersion = true
|
||||
self.timeoutTimer!.invalidate()
|
||||
}
|
||||
|
||||
// Connect to a specific peripheral
|
||||
func connectTo(peripheral: CBPeripheral) {
|
||||
|
|
@ -170,7 +189,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
self.disconnectPeripheral()
|
||||
}
|
||||
|
||||
self.connectedVersion = "0.0.0"
|
||||
self.centralManager?.connect(peripheral)
|
||||
|
||||
// Invalidate any existing timer
|
||||
|
|
@ -192,6 +210,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
guard let connectedPeripheral = connectedPeripheral else { return }
|
||||
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
|
||||
self.isConnected = false
|
||||
self.invalidVersion = false
|
||||
self.connectedVersion = "0.0.0"
|
||||
}
|
||||
|
||||
// Called each time a peripheral is discovered
|
||||
|
|
@ -416,10 +436,45 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
if (![FROMNUM_characteristic, FROMNUM_characteristic, TORADIO_characteristic].contains(nil)) {
|
||||
|
||||
sendWantConfig()
|
||||
|
||||
self.configTimeoutTimer = Timer.scheduledTimer(timeInterval: TimeInterval(7), target: self, selector: #selector(configTimeoutTimerFired), userInfo: context, repeats: false)
|
||||
RunLoop.current.add(self.configTimeoutTimer!, forMode: .common)
|
||||
}
|
||||
}
|
||||
|
||||
func requestDeviceMetadata() {
|
||||
guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return }
|
||||
|
||||
MeshLogger.log("ℹ️ Requesting Device Metadata for \(connectedPeripheral!.peripheral.name ?? "Unknown")")
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.getDeviceMetadataRequest = 0
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
dataMessage.wantResponse = true
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
var toRadio: ToRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
connectedPeripheral!.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
|
||||
// Either Read the config complete value or from num notify value
|
||||
connectedPeripheral!.peripheral.readValue(for: FROMRADIO_characteristic)
|
||||
}
|
||||
|
||||
func sendWantConfig() {
|
||||
guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return }
|
||||
|
||||
|
|
@ -504,22 +559,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
nowKnown = true
|
||||
connectedVersion = String(version)
|
||||
|
||||
let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!)
|
||||
let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame
|
||||
|
||||
if myInfo != nil {
|
||||
|
||||
if !supportedVersion {
|
||||
|
||||
self.connectedPeripheral.bitrate = myInfo!.bitrate
|
||||
self.connectedPeripheral.num = myInfo!.myNodeNum
|
||||
invalidVersion = true
|
||||
self.lastConnectionError = "🚨 Update your firmware"
|
||||
|
||||
self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown"
|
||||
self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown"
|
||||
self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown"
|
||||
self.connectedPeripheral.maxChannels = myInfo!.maxChannels
|
||||
return
|
||||
|
||||
} else {
|
||||
|
||||
let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!)
|
||||
|
||||
if myInfo != nil {
|
||||
|
||||
self.connectedPeripheral.bitrate = myInfo!.bitrate
|
||||
self.connectedPeripheral.num = myInfo!.myNodeNum
|
||||
|
||||
self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown"
|
||||
self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown"
|
||||
self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown"
|
||||
self.connectedPeripheral.maxChannels = myInfo!.maxChannels
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// NodeInfo
|
||||
if decodedInfo.nodeInfo.num != 0 {
|
||||
if decodedInfo.nodeInfo.num != 0 && !invalidVersion {
|
||||
|
||||
nowKnown = true
|
||||
let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, meshLogging: meshLoggingEnabled, context: context!)
|
||||
|
|
@ -540,14 +608,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
// Config
|
||||
if decodedInfo.config.isInitialized {
|
||||
if decodedInfo.config.isInitialized && !invalidVersion {
|
||||
|
||||
nowKnown = true
|
||||
localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
||||
}
|
||||
// Module Config
|
||||
if decodedInfo.moduleConfig.isInitialized {
|
||||
if decodedInfo.moduleConfig.isInitialized && !invalidVersion {
|
||||
|
||||
nowKnown = true
|
||||
moduleConfig(config: decodedInfo.moduleConfig, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
|
@ -567,9 +635,17 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
case .waypointApp:
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Waypoint App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .nodeinfoApp:
|
||||
nodeInfoAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
|
||||
if !invalidVersion {
|
||||
|
||||
nodeInfoAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
}
|
||||
case .routingApp:
|
||||
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
|
||||
if !invalidVersion {
|
||||
|
||||
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
}
|
||||
case .adminApp:
|
||||
adminAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
case .replyApp:
|
||||
|
|
@ -583,7 +659,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
case .rangeTestApp:
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Range Test App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .telemetryApp:
|
||||
telemetryPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
|
||||
if !invalidVersion {
|
||||
|
||||
telemetryPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
|
||||
}
|
||||
case .textMessageCompressedApp:
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Text Message Compressed App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
case .zpsApp:
|
||||
|
|
@ -636,7 +716,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
|
||||
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce {
|
||||
|
||||
|
||||
if configTimeoutTimer != nil {
|
||||
|
||||
configTimeoutTimer?.invalidate()
|
||||
}
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)") }
|
||||
self.connectedPeripheral.subscribed = true
|
||||
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
|
||||
|
|
@ -657,6 +742,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
case FROMNUM_UUID :
|
||||
|
||||
|
|
@ -1320,7 +1406,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
var adminPacket = AdminMessage()
|
||||
adminPacket.getChannelRequest = channelIndex
|
||||
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
|
||||
|
|
@ -1342,23 +1427,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a Get Channel Request Admin Message for node: \(String(connectedPeripheral.num))") }
|
||||
if meshLoggingEnabled { MeshLogger.log("🛎️ Send Get Channel \(channelIndex) Request Admin Message for node: \(String(connectedPeripheral.num))") }
|
||||
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
return true
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ struct Connect: View {
|
|||
@State var initialLoad: Bool = true
|
||||
@State var isPreferredRadio: Bool = false
|
||||
|
||||
@State var firmwareVersion = "0.0.0"
|
||||
@State var minimumVersion = "1.3.41"
|
||||
@State var invalidVersion = false
|
||||
@State var invalidFirmwareVersion = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -100,13 +98,15 @@ struct Connect: View {
|
|||
}
|
||||
|
||||
userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString
|
||||
|
||||
bleManager.preferredPeripheral = true
|
||||
|
||||
} else {
|
||||
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId {
|
||||
|
||||
userSettings.preferredPeripheralId = ""
|
||||
userSettings.preferredPeripheralName = ""
|
||||
bleManager.preferredPeripheral = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -260,22 +260,15 @@ struct Connect: View {
|
|||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.sheet(isPresented: $invalidVersion) {
|
||||
.sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) {
|
||||
|
||||
InvalidVersion(errorText: "1.3 Beta this version of the app supports only version \(minimumVersion) and above. Your device has been disconnected.")
|
||||
InvalidVersion(minimumVersion: self.bleManager.minimumVersion, version: self.bleManager.connectedVersion)
|
||||
}
|
||||
|
||||
.onChange(of: (self.bleManager.connectedVersion)) { ic in
|
||||
.onChange(of: (self.bleManager.invalidVersion)) { cv in
|
||||
|
||||
firmwareVersion = self.bleManager.connectedVersion
|
||||
let supportedVersion = firmwareVersion == "0.0.0" || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedSame
|
||||
invalidFirmwareVersion = self.bleManager.invalidVersion
|
||||
|
||||
invalidVersion = !supportedVersion
|
||||
|
||||
if invalidVersion {
|
||||
|
||||
bleManager.disconnectPeripheral()
|
||||
}
|
||||
}
|
||||
.onAppear(perform: {
|
||||
|
||||
|
|
@ -303,6 +296,10 @@ struct Connect: View {
|
|||
}
|
||||
})
|
||||
}
|
||||
func didDismissSheet() {
|
||||
|
||||
bleManager.disconnectPeripheral()
|
||||
}
|
||||
}
|
||||
|
||||
struct Connect_Previews: PreviewProvider {
|
||||
|
|
|
|||
|
|
@ -8,29 +8,62 @@ import SwiftUI
|
|||
|
||||
struct InvalidVersion: View {
|
||||
|
||||
@State var errorText = ""
|
||||
@State var minimumVersion = ""
|
||||
@State var version = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
Text("🚨 Unsupported Firmware Version")
|
||||
Text("Update your firmware")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.red)
|
||||
.foregroundColor(.orange)
|
||||
|
||||
Text(errorText)
|
||||
.font(.title2)
|
||||
.padding()
|
||||
Divider()
|
||||
|
||||
Text("Version 1.3 includes breaking changes to devices and the client apps. The version 1.3 app does not support 1.2 nodes, there is one build for 1.2 under Versions & Build Groups in TestFlight that will be available until the end of November 2022.")
|
||||
.font(.callout)
|
||||
.padding()
|
||||
VStack {
|
||||
|
||||
Text("The Meshtastic Apple apps support firmware version \(minimumVersion) and above. You are running version \(version)")
|
||||
.font(.title2)
|
||||
.padding(.bottom)
|
||||
|
||||
Link("Firmware update docs", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/")!)
|
||||
.font(.title)
|
||||
.padding()
|
||||
|
||||
Link("Additional help", destination: URL(string: "https://meshtastic.org/docs/faq")!)
|
||||
.font(.title)
|
||||
.padding()
|
||||
|
||||
|
||||
}
|
||||
.padding()
|
||||
|
||||
Link("Upgrade your Firmware", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/")!)
|
||||
|
||||
Text("Only manual firmware upgrade methods are working for version 1.3.")
|
||||
.padding()
|
||||
if version == "1.2.65" {
|
||||
|
||||
Divider()
|
||||
.padding(.top)
|
||||
|
||||
VStack{
|
||||
|
||||
Text("🦕 Your device is Version 1.2 🦖 ☄️")
|
||||
.font(.title3)
|
||||
.foregroundColor(.orange)
|
||||
.padding(.bottom)
|
||||
|
||||
Text("Version 1.3 includes breaking changes to devices and the client apps. The version 1.3 app does not support 1.2 or 1.0 nodes.")
|
||||
.font(.caption)
|
||||
.padding([.leading, .trailing])
|
||||
|
||||
Text("There is a build for 1.2 EOL under Other Versions in TestFlight that will be available until the end of November 2022.")
|
||||
.font(.caption)
|
||||
.padding()
|
||||
|
||||
Link("Version 1.2 End of life (EOL) Info", destination: URL(string: "https://meshtastic.org/docs/1.2-End-of-life/")!)
|
||||
.font(.callout)
|
||||
|
||||
}.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,15 +37,6 @@ struct Settings: View {
|
|||
|
||||
Section("Radio Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
ShareChannel(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
Image(systemName: "qrcode")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Share Channel QR Code")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
UserConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
//
|
||||
// ShareChannel.swift
|
||||
// MeshtasticApple
|
||||
//
|
||||
// Created by Garth Vander Houwen on 4/8/22.
|
||||
//
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import CoreImage.CIFilterBuiltins
|
||||
|
||||
|
||||
struct QrCodeImage {
|
||||
let context = CIContext()
|
||||
|
||||
func generateQRCode(from text: String) -> UIImage {
|
||||
var qrImage = UIImage(systemName: "xmark.circle") ?? UIImage()
|
||||
let data = Data(text.utf8)
|
||||
let filter = CIFilter.qrCodeGenerator()
|
||||
filter.setValue(data, forKey: "inputMessage")
|
||||
|
||||
let transform = CGAffineTransform(scaleX: 20, y: 20)
|
||||
if let outputImage = filter.outputImage?.transformed(by: transform) {
|
||||
if let image = context.createCGImage(
|
||||
outputImage,
|
||||
from: outputImage.extent) {
|
||||
qrImage = UIImage(cgImage: image)
|
||||
}
|
||||
}
|
||||
|
||||
return qrImage
|
||||
}
|
||||
}
|
||||
|
||||
struct ShareChannel: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var text = "https://meshtastic.org/E/#test"
|
||||
var qrCodeImage = QrCodeImage()
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
GeometryReader { bounds in
|
||||
|
||||
let smallest = min(bounds.size.width, bounds.size.height)
|
||||
|
||||
ScrollView {
|
||||
|
||||
VStack {
|
||||
Text("Scan the QR code below with the Apple or Android device you would like to share your channel settings with.")
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.font(.callout)
|
||||
|
||||
let image = qrCodeImage.generateQRCode(from: text)
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(
|
||||
minWidth: smallest * 0.8,
|
||||
maxWidth: smallest * 0.8,
|
||||
minHeight: smallest * 0.8,
|
||||
maxHeight: smallest * 0.8,
|
||||
alignment: .center
|
||||
)
|
||||
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
|
||||
HStack {
|
||||
|
||||
let preset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset))
|
||||
Text("Modem Preset \(preset!.description)").font(.title3)
|
||||
}
|
||||
}
|
||||
VStack {
|
||||
|
||||
Text("Number of Channels: \(node!.myInfo!.maxChannels)").font(.title2)
|
||||
|
||||
ForEach(node!.myInfo!.channels?.array.sorted(by: { ($0 as! ChannelEntity).index < ($1 as! ChannelEntity).index }) as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in
|
||||
|
||||
VStack {
|
||||
|
||||
|
||||
Text("Channel: \(channel.index) Name: \(channel.name ?? "")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: bounds.size.width, height: bounds.size.height)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Share Channel")
|
||||
.navigationBarTitleDisplayMode(.automatic)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue