2022-09-04 21:45:03 -07:00
//
// M Q T 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 9 / 4 / 2 2 .
//
import SwiftUI
struct MQTTConfig : View {
2023-03-06 10:33:18 -08:00
2022-09-04 21:45:03 -07:00
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-12-09 18:19:00 -08:00
@ Environment ( \ . dismiss ) private var goBack
2022-09-04 21:45:03 -07:00
var node : NodeInfoEntity ?
@ State private var isPresentingSaveConfirm : Bool = false
@ State var hasChanges : Bool = false
@ State var enabled = false
2023-08-06 17:41:46 -07:00
@ State var proxyToClientEnabled = false
2022-09-04 21:45:03 -07:00
@ State var address = " "
@ State var username = " "
@ State var password = " "
2023-10-03 08:41:44 -07:00
@ State var encryptionEnabled = true
2022-09-04 21:45:03 -07:00
@ State var jsonEnabled = false
2023-05-04 21:49:35 -07:00
@ State var tlsEnabled = true
@ State var root = " msh "
2024-01-20 21:25:02 -08:00
@ State var mqttConnected : Bool = false
2024-01-20 22:49:55 -08:00
2023-03-06 10:33:18 -08:00
2022-09-04 21:45:03 -07:00
var body : some View {
2023-12-06 12:32:17 -08:00
VStack {
Form {
2024-02-06 09:13:57 -08:00
if node != nil && node ? . loRaConfig != nil {
let rc = RegionCodes ( rawValue : Int ( node ? . loRaConfig ? . regionCode ? ? 0 ) )
if rc ? . dutyCycle ? ? 0 <= 10 {
2024-02-14 21:43:29 -08:00
Text ( " Your region has a \( rc ? . dutyCycle ? ? 0 ) % duty cycle. MQTT is not advised when you are duty cycle restricted, the extra traffic will quickly overwhelm your LoRa mesh. " )
2024-02-06 09:13:57 -08:00
. font ( . callout )
. foregroundColor ( . red )
}
}
2024-02-17 22:39:22 -07:00
ConfigHeader ( title : " MQTT " , config : \ . mqttConfig , node : node , onAppear : setMqttValues )
2023-12-06 12:32:17 -08:00
Section ( header : Text ( " options " ) ) {
2024-01-20 21:25:02 -08:00
2023-12-06 12:32:17 -08:00
Toggle ( isOn : $ enabled ) {
Label ( " enabled " , systemImage : " dot.radiowaves.right " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ proxyToClientEnabled ) {
Label ( " mqtt.clientproxy " , systemImage : " iphone.radiowaves.left.and.right " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-01-20 21:25:02 -08:00
if enabled && proxyToClientEnabled {
Toggle ( isOn : $ mqttConnected ) {
Label ( mqttConnected ? " mqtt.disconnect " . localized : " mqtt.connect " . localized , systemImage : " server.rack " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
}
Text ( " If both MQTT and the client proxy are enabled your mobile device will utilize an available network connection to connect to the specified MQTT server. " )
2023-12-06 12:32:17 -08:00
. font ( . caption2 )
Toggle ( isOn : $ encryptionEnabled ) {
Label ( " Encryption Enabled " , systemImage : " lock.icloud " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ jsonEnabled ) {
Label ( " JSON Enabled " , systemImage : " ellipsis.curlybraces " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-02-06 09:13:57 -08:00
Text ( " JSON mode is a limited, unencrypted MQTT output that can crash your node it should not be enabled unless you are locally integrating with home assistant " )
2023-12-06 12:32:17 -08:00
. font ( . caption2 )
Toggle ( isOn : $ tlsEnabled ) {
Label ( " TLS Enabled " , systemImage : " checkmark.shield.fill " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Text ( " Your MQTT Server must support TLS. " )
. font ( . caption2 )
2022-12-30 11:08:59 -08:00
}
2023-12-06 12:32:17 -08:00
Section ( header : Text ( " Custom Server " ) ) {
HStack {
Label ( " Address " , systemImage : " server.rack " )
TextField ( " Server Address " , text : $ address )
. foregroundColor ( . gray )
. autocapitalization ( . none )
. disableAutocorrection ( true )
. onChange ( of : address , perform : { _ in
let totalBytes = address . utf8 . count
// O n l y m e s s w i t h t h e v a l u e i f i t i s t o o b i g
if totalBytes > 62 {
let firstNBytes = Data ( username . utf8 . prefix ( 62 ) )
if let maxBytesString = String ( data : firstNBytes , encoding : String . Encoding . utf8 ) {
// S e t t h e s h o r t N a m e b a c k t o t h e l a s t p l a c e w h e r e i t w a s t h e r i g h t s i z e
address = maxBytesString
}
2022-12-30 11:08:59 -08:00
}
2023-12-06 12:32:17 -08:00
hasChanges = true
} )
. foregroundColor ( . gray )
. keyboardType ( . default )
}
. autocorrectionDisabled ( )
HStack {
Label ( " mqtt.username " , systemImage : " person.text.rectangle " )
TextField ( " mqtt.username " , text : $ username )
. foregroundColor ( . gray )
. autocapitalization ( . none )
. disableAutocorrection ( true )
. onChange ( of : username , perform : { _ in
let totalBytes = username . utf8 . count
// O n l y m e s s w i t h t h e v a l u e i f i t i s t o o b i g
if totalBytes > 62 {
let firstNBytes = Data ( username . utf8 . prefix ( 62 ) )
if let maxBytesString = String ( data : firstNBytes , encoding : String . Encoding . utf8 ) {
// S e t t h e s h o r t N a m e b a c k t o t h e l a s t p l a c e w h e r e i t w a s t h e r i g h t s i z e
username = maxBytesString
}
2022-12-30 11:08:59 -08:00
}
2023-12-06 12:32:17 -08:00
hasChanges = true
} )
. foregroundColor ( . gray )
}
. keyboardType ( . default )
. scrollDismissesKeyboard ( . interactively )
HStack {
Label ( " password " , systemImage : " wallet.pass " )
TextField ( " password " , text : $ password )
. foregroundColor ( . gray )
. autocapitalization ( . none )
. disableAutocorrection ( true )
. onChange ( of : password , perform : { _ in
let totalBytes = password . utf8 . count
// O n l y m e s s w i t h t h e v a l u e i f i t i s t o o b i g
if totalBytes > 62 {
let firstNBytes = Data ( password . utf8 . prefix ( 62 ) )
if let maxBytesString = String ( data : firstNBytes , encoding : String . Encoding . utf8 ) {
// S e t t h e s h o r t N a m e b a c k t o t h e l a s t p l a c e w h e r e i t w a s t h e r i g h t s i z e
password = maxBytesString
}
2022-09-04 21:45:03 -07:00
}
2023-12-06 12:32:17 -08:00
hasChanges = true
} )
. foregroundColor ( . gray )
}
. keyboardType ( . default )
. scrollDismissesKeyboard ( . interactively )
HStack {
Label ( " Root Topic " , systemImage : " tree " )
TextField ( " Root Topic " , text : $ root )
. foregroundColor ( . gray )
. onChange ( of : root , perform : { _ in
let totalBytes = root . utf8 . count
// O n l y m e s s w i t h t h e v a l u e i f i t i s t o o b i g
if totalBytes > 14 {
let firstNBytes = Data ( root . utf8 . prefix ( 14 ) )
if let maxBytesString = String ( data : firstNBytes , encoding : String . Encoding . utf8 ) {
// S e t t h e s h o r t N a m e b a c k t o t h e l a s t p l a c e w h e r e i t w a s t h e r i g h t s i z e
root = maxBytesString
}
2023-05-04 21:49:35 -07:00
}
2023-12-06 12:32:17 -08:00
} )
. foregroundColor ( . gray )
}
. keyboardType ( . asciiCapable )
. scrollDismissesKeyboard ( . interactively )
. disableAutocorrection ( true )
Text ( " The root topic to use for MQTT messages. Default is \" msh \" . This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs " )
. font ( . caption2 )
2023-05-04 21:49:35 -07:00
}
2023-12-06 12:32:17 -08:00
Text ( " You can set uplink and downlink for each channel. " )
. font ( . callout )
2022-09-04 21:45:03 -07:00
}
2023-12-06 12:32:17 -08:00
. scrollDismissesKeyboard ( . interactively )
. disabled ( self . bleManager . connectedPeripheral = = nil || node ? . mqttConfig = = nil )
2022-12-30 11:08:59 -08:00
}
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 ,
titleVisibility : . visible
) {
2023-02-22 20:31:08 -08:00
let connectedNode = getNodeInfo ( id : bleManager . connectedPeripheral ? . num ? ? - 1 , context : context )
2023-02-06 18:45:03 -08:00
if connectedNode != nil {
2023-05-05 09:27:24 -07:00
let nodeName = node ? . user ? . longName ? ? " unknown " . localized
let buttonText = String . localizedStringWithFormat ( " save.config %@ " . localized , nodeName )
2023-02-06 18:45:03 -08:00
Button ( buttonText ) {
var mqtt = ModuleConfig . MQTTConfig ( )
mqtt . enabled = self . enabled
2023-08-06 17:41:46 -07:00
mqtt . proxyToClientEnabled = self . proxyToClientEnabled
2023-02-06 18:45:03 -08:00
mqtt . address = self . address
mqtt . username = self . username
mqtt . password = self . password
2023-05-04 21:49:35 -07:00
mqtt . root = self . root
2023-02-06 18:45:03 -08:00
mqtt . encryptionEnabled = self . encryptionEnabled
mqtt . jsonEnabled = self . jsonEnabled
2023-05-04 21:49:35 -07:00
mqtt . tlsEnabled = self . tlsEnabled
2023-02-06 18:45:03 -08:00
let adminMessageId = bleManager . saveMQTTConfig ( config : mqtt , fromUser : connectedNode ! . user ! , toUser : node ! . user ! , adminIndex : connectedNode ? . myInfo ? . adminIndex ? ? 0 )
if adminMessageId > 0 {
// S h o u l d s h o w a s a v e d s u c c e s s f u l l y a l e r t o n c e I k n o w t h a t t o b e t r u e
// f o r n o w j u s t d i s a b l e t h e b u t t o n a f t e r a s u c c e s s f u l s a v e
hasChanges = false
goBack ( )
}
2022-09-04 21:45:03 -07:00
}
}
}
2022-12-30 11:08:59 -08:00
message : {
Text ( " config.save.confirm " )
}
2022-12-13 07:49:46 -08:00
. navigationTitle ( " mqtt.config " )
2022-09-04 21:45:03 -07:00
. navigationBarItems ( trailing :
ZStack {
2024-01-20 22:49:55 -08:00
ConnectedDevice ( bluetoothOn : bleManager . isSwitchedOn , deviceConnected : bleManager . connectedPeripheral != nil , name : ( bleManager . connectedPeripheral != nil ) ? bleManager . connectedPeripheral . shortName : " ? " , mqttProxyConnected : bleManager . mqttProxyConnected )
2022-09-04 21:45:03 -07:00
} )
. onAppear {
2024-01-14 11:25:00 -08:00
if self . bleManager . context = = nil {
self . bleManager . context = context
}
2023-03-19 18:37:23 -07:00
setMqttValues ( )
2023-02-01 09:19:45 -08:00
// N e e d t o r e q u e s t a T e l e m e t r y M o d u l e C o n f i g f r o m t h e r e m o t e n o d e b e f o r e a l l o w i n g c h a n g e s
2023-03-19 08:31:16 -07:00
if bleManager . connectedPeripheral != nil && node ? . mqttConfig = = nil {
2023-02-01 09:19:45 -08:00
print ( " empty mqtt module config " )
let connectedNode = getNodeInfo ( id : bleManager . connectedPeripheral . num , context : context )
2023-03-05 14:40:07 -08:00
if node != nil && connectedNode != nil {
2023-02-06 18:45:03 -08:00
_ = bleManager . requestMqttModuleConfig ( fromUser : connectedNode ! . user ! , toUser : node ! . user ! , adminIndex : connectedNode ? . myInfo ? . adminIndex ? ? 0 )
2023-02-01 09:19:45 -08:00
}
}
2022-09-04 21:45:03 -07:00
}
2023-05-04 21:49:35 -07:00
. onChange ( of : address ) { newAddress in
if node != nil && node ? . mqttConfig != nil {
if newAddress != node ! . mqttConfig ! . address { hasChanges = true }
}
}
. onChange ( of : username ) { newUsername in
if node != nil && node ? . mqttConfig != nil {
if newUsername != node ! . mqttConfig ! . username { hasChanges = true }
}
}
. onChange ( of : password ) { newPassword in
if node != nil && node ? . mqttConfig != nil {
if newPassword != node ! . mqttConfig ! . password { hasChanges = true }
}
}
. onChange ( of : root ) { newRoot in
if node != nil && node ? . mqttConfig != nil {
if newRoot != node ! . mqttConfig ! . root { hasChanges = true }
}
}
2022-09-04 21:45:03 -07:00
. onChange ( of : enabled ) { newEnabled in
2023-01-02 20:11:58 -08:00
if node != nil && node ? . mqttConfig != nil {
2022-09-10 17:14:03 -07:00
if newEnabled != node ! . mqttConfig ! . enabled { hasChanges = true }
2022-09-04 21:45:03 -07:00
}
}
2023-08-06 17:41:46 -07:00
. onChange ( of : proxyToClientEnabled ) { newProxyToClientEnabled in
if node != nil && node ? . mqttConfig != nil {
if newProxyToClientEnabled != node ! . mqttConfig ! . proxyToClientEnabled { hasChanges = true }
2023-10-25 18:54:52 -07:00
if newProxyToClientEnabled {
jsonEnabled = false
}
2023-08-06 17:41:46 -07:00
}
}
2022-09-04 21:45:03 -07:00
. onChange ( of : encryptionEnabled ) { newEncryptionEnabled in
2023-01-02 20:11:58 -08:00
if node != nil && node ? . mqttConfig != nil {
2022-09-04 21:45:03 -07:00
if newEncryptionEnabled != node ! . mqttConfig ! . encryptionEnabled { hasChanges = true }
}
}
. onChange ( of : jsonEnabled ) { newJsonEnabled in
2023-01-02 20:11:58 -08:00
if node != nil && node ? . mqttConfig != nil {
2022-09-04 21:45:03 -07:00
if newJsonEnabled != node ! . mqttConfig ! . jsonEnabled { hasChanges = true }
}
}
2023-05-04 21:49:35 -07:00
. onChange ( of : tlsEnabled ) { newTlsEnabled in
if node != nil && node ? . mqttConfig != nil {
if newTlsEnabled != node ! . mqttConfig ! . tlsEnabled { hasChanges = true }
}
}
2024-01-20 21:25:02 -08:00
. onChange ( of : mqttConnected ) { newMqttConnected in
if newMqttConnected = = false {
if bleManager . mqttProxyConnected {
bleManager . mqttManager . disconnect ( )
}
} else {
if ! bleManager . mqttProxyConnected && node != nil {
bleManager . mqttManager . connectFromConfigSettings ( node : node ! )
}
}
}
2022-09-04 21:45:03 -07:00
}
2023-03-19 18:37:23 -07:00
func setMqttValues ( ) {
self . enabled = ( node ? . mqttConfig ? . enabled ? ? false )
2023-08-06 17:41:46 -07:00
self . proxyToClientEnabled = ( node ? . mqttConfig ? . proxyToClientEnabled ? ? false )
2023-03-19 18:37:23 -07:00
self . address = node ? . mqttConfig ? . address ? ? " "
self . username = node ? . mqttConfig ? . username ? ? " "
self . password = node ? . mqttConfig ? . password ? ? " "
2023-05-04 21:49:35 -07:00
self . root = node ? . mqttConfig ? . root ? ? " msh "
2023-03-19 18:37:23 -07:00
self . encryptionEnabled = ( node ? . mqttConfig ? . encryptionEnabled ? ? false )
self . jsonEnabled = ( node ? . mqttConfig ? . jsonEnabled ? ? false )
2023-05-04 21:49:35 -07:00
self . tlsEnabled = ( node ? . mqttConfig ? . tlsEnabled ? ? false )
2024-01-20 21:25:02 -08:00
self . mqttConnected = bleManager . mqttProxyConnected
2023-03-19 18:37:23 -07:00
self . hasChanges = false
}
2022-09-04 21:45:03 -07:00
}