2023-03-10 19:41:26 -08:00
//
// F i r m w a r e . s w i f t
// M e s h t a s t i c
//
// C r e a t e d b y G a r t h V a n d e r H o u w e n o n 3 / 1 0 / 2 3 .
//
//
// A b o u t . s w i f t
// M e s h t a s t i c
//
// C o p y r i g h t ( c ) G a r t h V a n d e r H o u w e n 1 0 / 6 / 2 2 .
//
import SwiftUI
import StoreKit
struct Firmware : View {
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
var node : NodeInfoEntity ?
2023-03-15 16:18:43 -07:00
@ State var minimumVersion = " 2.1.0 "
@ State var version = " "
2023-03-10 19:41:26 -08:00
@ State private var firmwareReleaseData : FirmwareRelease = FirmwareRelease ( )
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
var body : some View {
2023-03-14 12:44:10 -07:00
// N a v i g a t i o n S p l i t V i e w {
2023-03-10 19:41:26 -08:00
NavigationStack {
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
let hwModel : HardwareModels = HardwareModels . allCases . first ( where : { $0 . rawValue = = node ? . user ? . hwModel ? ? " UNSET " } ) ? ? HardwareModels . UNSET
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
VStack ( alignment : . leading ) {
2023-03-15 16:54:06 -07:00
Text ( " Current Version: \( bleManager . connectedVersion ) " )
. font ( . largeTitle )
2023-03-15 16:18:43 -07:00
Text ( " Your device supports the following firmware: " )
2023-03-10 19:41:26 -08:00
. font ( . callout )
2023-03-15 16:18:43 -07:00
HStack {
ForEach ( hwModel . firmwareStrings , id : \ . self ) { fs in
Text ( fs ) . font ( . callout )
}
}
. padding ( . bottom )
2023-03-17 23:30:01 -07:00
if hwModel . platform ( ) = = HardwarePlatforms . nrf52 {
2023-03-15 16:18:43 -07:00
VStack ( alignment : . leading ) {
2023-03-15 16:54:06 -07:00
if hwModel = = HardwareModels . RAK4631 {
Text ( " nRF OTA Device Firmware Update App " )
. font ( . title3 )
Text ( " You can update your Meshtastic device over bluetooth using the Nordic DFU app. This currently works for RAK NRF devices. " )
. font ( . caption )
Link ( " Get NRF DFU from the App Store " , destination : URL ( string : " https://apps.apple.com/us/app/nrf-device-firmware-update/id1624454660 " ) ! )
. font ( . callout )
} else {
Text ( " OTA Updates are not supported on the this NRF Device. " )
. font ( . title3 )
Link ( " Drag & Drop Firmware Update " , destination : URL ( string : " https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/drag-n-drop " ) ! )
. font ( . callout )
}
2023-03-15 16:18:43 -07:00
}
2023-03-17 23:30:01 -07:00
} else if hwModel . platform ( ) = = HardwarePlatforms . esp32 {
2023-03-15 16:18:43 -07:00
VStack ( alignment : . leading ) {
Text ( " ESP32 Device Firmware Update " )
. font ( . title3 )
Text ( " Currently the reccomended way to update ESP32 devices is using the web flasher from a chrome based browser. It does not work on mobile devices or over BLE. " )
. font ( . caption )
Link ( " Web Flasher " , destination : URL ( string : " https://flasher.meshtastic.org " ) ! )
. font ( . callout )
. padding ( . bottom )
Text ( " ESP 32 OTA update is a work in progress, click the button below to sent your device a reboot into ota admin message. " )
. font ( . caption )
HStack ( alignment : . center ) {
Spacer ( )
Button {
2023-03-19 09:40:57 -07:00
let connectedNode = getNodeInfo ( id : bleManager . connectedPeripheral ? . num ? ? 0 , context : context )
2023-03-15 16:18:43 -07:00
if connectedNode != nil {
if ! bleManager . sendRebootOta ( fromUser : connectedNode ! . user ! , toUser : node ! . user ! , adminIndex : connectedNode ! . myInfo ! . adminIndex ) {
print ( " Reboot Failed " )
2023-03-16 17:46:44 -07:00
}
2023-03-15 16:18:43 -07:00
}
} label : {
Label ( " Send Reboot OTA " , systemImage : " square.and.arrow.down " )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:18:43 -07:00
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . regular )
. padding ( 5 )
Spacer ( )
2023-03-10 19:41:26 -08:00
}
}
2023-03-15 16:54:06 -07:00
} else {
Text ( " OTA Updates are not supported on your platform. " )
. font ( . title3 )
2023-03-17 08:32:04 -07:00
Text ( node ? . user ? . hwModel ? ? " UNSET " )
. font ( . title3 )
Text ( hwModel . platform ( ) . description )
. font ( . title3 )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:54:06 -07:00
2023-03-15 16:18:43 -07:00
} . padding ( )
2023-03-14 12:44:10 -07:00
VStack ( alignment : . leading ) {
2023-03-10 19:41:26 -08:00
Text ( " Firmware Releases " )
. font ( . title3 )
. padding ( [ . leading , . trailing ] )
List {
Section ( header : Text ( " Stable " ) ) {
ForEach ( firmwareReleaseData . releases ? . stable ? ? [ ] , id : \ . id ) { fr in
2023-03-11 09:26:52 -08:00
Link ( destination : URL ( string : fr . zipUrl ? ? " " ) ! ) {
2023-03-14 12:44:10 -07:00
HStack {
2023-03-11 09:26:52 -08:00
Text ( fr . title ? ? " Unknown " )
. font ( . caption )
Spacer ( )
Image ( systemName : " square.and.arrow.down " )
. font ( . title3 )
2023-03-10 19:41:26 -08:00
}
}
}
}
Section ( " Alpha " ) {
ForEach ( firmwareReleaseData . releases ? . alpha ? ? [ ] , id : \ . id ) { fr in
2023-03-11 09:26:52 -08:00
Link ( destination : URL ( string : fr . zipUrl ? ? " " ) ! ) {
2023-03-14 12:44:10 -07:00
HStack {
2023-03-11 09:26:52 -08:00
Text ( fr . title ? ? " Unknown " )
. font ( . caption )
Spacer ( )
Image ( systemName : " square.and.arrow.down " )
. font ( . title3 )
2023-03-10 19:41:26 -08:00
}
}
}
}
Section ( " Pull Requests " ) {
ForEach ( firmwareReleaseData . pullRequests ? ? [ ] , id : \ . id ) { fr in
2023-03-11 09:26:52 -08:00
Link ( destination : URL ( string : fr . zipUrl ? ? " " ) ! ) {
2023-03-14 12:44:10 -07:00
HStack {
2023-03-11 09:26:52 -08:00
Text ( fr . title ? ? " Unknown " )
. font ( . caption )
Spacer ( )
Image ( systemName : " square.and.arrow.down " )
. font ( . title3 )
2023-03-10 19:41:26 -08:00
}
}
}
}
}
}
2023-03-15 16:54:06 -07:00
. padding ( . bottom , 5 )
2023-03-10 19:41:26 -08:00
. onAppear ( perform : loadData )
. navigationTitle ( " Firmware Updates " )
. navigationBarTitleDisplayMode ( . inline )
}
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
func loadData ( ) {
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
guard let url = URL ( string : " https://api.meshtastic.org/github/firmware/list " ) else {
return
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
let request = URLRequest ( url : url )
2023-03-14 12:44:10 -07:00
URLSession . shared . dataTask ( with : request ) { data , _ , _ in
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
if let data = data {
if let response_obj = try ? JSONDecoder ( ) . decode ( FirmwareRelease . self , from : data ) {
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
DispatchQueue . main . async {
self . firmwareReleaseData = response_obj
}
}
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
} . resume ( )
}
}
struct FirmwareRelease : Codable {
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
var releases : Releases ? = Releases ( )
var pullRequests : [ PullRequests ] ? = [ ]
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
enum CodingKeys : String , CodingKey {
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
case releases = " releases "
case pullRequests = " pullRequests "
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
init ( from decoder : Decoder ) throws {
let values = try decoder . container ( keyedBy : CodingKeys . self )
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
releases = try values . decodeIfPresent ( Releases . self , forKey : . releases )
pullRequests = try values . decodeIfPresent ( [ PullRequests ] . self , forKey : . pullRequests )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
init ( ) {
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
}
}
struct Releases : Codable {
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
var stable : [ Stable ] ? = [ ]
var alpha : [ Alpha ] ? = [ ]
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
enum CodingKeys : String , CodingKey {
case stable = " stable "
case alpha = " alpha "
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
init ( from decoder : Decoder ) throws {
let values = try decoder . container ( keyedBy : CodingKeys . self )
2023-03-14 12:44:10 -07:00
stable = try values . decodeIfPresent ( [ Stable ] . self , forKey : . stable )
alpha = try values . decodeIfPresent ( [ Alpha ] . self , forKey : . alpha )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:18:43 -07:00
2023-03-13 09:50:24 -07:00
init ( ) { }
2023-03-10 19:41:26 -08:00
}
struct Alpha : Codable {
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
var id : String ?
var title : String ?
var pageUrl : String ?
var zipUrl : String ?
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
enum CodingKeys : String , CodingKey {
case id = " id "
case title = " title "
case pageUrl = " page_url "
case zipUrl = " zip_url "
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
init ( from decoder : Decoder ) throws {
let values = try decoder . container ( keyedBy : CodingKeys . self )
2023-03-14 12:44:10 -07:00
id = try values . decodeIfPresent ( String . self , forKey : . id )
title = try values . decodeIfPresent ( String . self , forKey : . title )
pageUrl = try values . decodeIfPresent ( String . self , forKey : . pageUrl )
zipUrl = try values . decodeIfPresent ( String . self , forKey : . zipUrl )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:18:43 -07:00
2023-03-13 09:50:24 -07:00
init ( ) { }
2023-03-10 19:41:26 -08:00
}
struct Stable : Codable {
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
var id : String ?
var title : String ?
var pageUrl : String ?
var zipUrl : String ?
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
enum CodingKeys : String , CodingKey {
case id = " id "
case title = " title "
case pageUrl = " page_url "
case zipUrl = " zip_url "
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
init ( from decoder : Decoder ) throws {
let values = try decoder . container ( keyedBy : CodingKeys . self )
2023-03-14 12:44:10 -07:00
id = try values . decodeIfPresent ( String . self , forKey : . id )
title = try values . decodeIfPresent ( String . self , forKey : . title )
pageUrl = try values . decodeIfPresent ( String . self , forKey : . pageUrl )
zipUrl = try values . decodeIfPresent ( String . self , forKey : . zipUrl )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:18:43 -07:00
2023-03-13 09:50:24 -07:00
init ( ) { }
2023-03-10 19:41:26 -08:00
}
struct PullRequests : Codable {
2023-03-15 16:18:43 -07:00
2023-03-14 12:44:10 -07:00
var id : String ?
var title : String ?
var pageUrl : String ?
var zipUrl : String ?
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
enum CodingKeys : String , CodingKey {
case id = " id "
case title = " title "
case pageUrl = " page_url "
case zipUrl = " zip_url "
}
2023-03-15 16:18:43 -07:00
2023-03-10 19:41:26 -08:00
init ( from decoder : Decoder ) throws {
let values = try decoder . container ( keyedBy : CodingKeys . self )
2023-03-14 12:44:10 -07:00
id = try values . decodeIfPresent ( String . self , forKey : . id )
title = try values . decodeIfPresent ( String . self , forKey : . title )
pageUrl = try values . decodeIfPresent ( String . self , forKey : . pageUrl )
zipUrl = try values . decodeIfPresent ( String . self , forKey : . zipUrl )
2023-03-10 19:41:26 -08:00
}
2023-03-15 16:18:43 -07:00
2023-03-13 09:50:24 -07:00
init ( ) { }
2023-03-10 19:41:26 -08:00
}