2022-06-22 09:05:56 -07:00
//
// E x t e r n a l N o t i f i c a t i o n C o n f i g . s w i f t
// M e s h t a s t i c A p p l e
//
// 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 6 / 2 2 / 2 2 .
//
2024-06-07 22:09:20 -05:00
import MeshtasticProtobufs
2024-06-03 02:17:55 -07:00
import OSLog
2024-06-07 22:09:20 -05:00
import SwiftUI
2022-06-22 09:05:56 -07:00
struct ExternalNotificationConfig : View {
2023-03-06 10:33:18 -08:00
2022-06-22 09:05:56 -07:00
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-12-09 18:19:00 -08:00
@ Environment ( \ . dismiss ) private var goBack
2023-03-06 10:33:18 -08:00
2022-07-07 00:29:52 -07:00
var node : NodeInfoEntity ?
2023-03-06 10:33:18 -08:00
2022-07-01 10:57:54 -07:00
@ State private var isPresentingSaveConfirm : Bool = false
@ State var hasChanges = false
2022-06-22 09:05:56 -07:00
@ State var enabled = false
@ State var alertBell = false
2022-12-23 11:30:55 -08:00
@ State var alertBellBuzzer = false
@ State var alertBellVibra = false
2022-07-01 10:57:54 -07:00
@ State var alertMessage = false
2022-12-23 11:30:55 -08:00
@ State var alertMessageBuzzer = false
@ State var alertMessageVibra = false
2022-07-01 10:57:54 -07:00
@ State var active = false
2022-12-23 14:00:48 -08:00
@ State var usePWM = false
2022-07-01 10:57:54 -07:00
@ State var output = 0
2022-12-23 11:30:55 -08:00
@ State var outputBuzzer = 0
@ State var outputVibra = 0
2022-07-01 10:57:54 -07:00
@ State var outputMilliseconds = 0
2022-12-23 11:30:55 -08:00
@ State var nagTimeout = 0
2023-12-20 10:24:01 -08:00
@ State var useI2SAsBuzzer = false
2023-03-06 10:33:18 -08:00
2022-06-22 09:05:56 -07:00
var body : some View {
2023-12-06 12:32:17 -08:00
VStack {
Form {
2024-02-17 22:39:22 -07:00
ConfigHeader ( title : " External notification " , config : \ . externalNotificationConfig , node : node , onAppear : setExternalNotificationValues )
2025-04-26 16:05:09 -07:00
Section ( header : Text ( " Options " ) ) {
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Toggle ( isOn : $ enabled ) {
2025-04-26 16:05:09 -07:00
Label ( " Enabled " , systemImage : " megaphone " )
2022-06-22 09:05:56 -07:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Toggle ( isOn : $ alertBell ) {
Label ( " Alert when receiving a bell " , systemImage : " bell " )
2022-06-22 09:05:56 -07:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Toggle ( isOn : $ alertMessage ) {
Label ( " Alert when receiving a message " , systemImage : " message " )
2022-06-22 09:05:56 -07:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Toggle ( isOn : $ usePWM ) {
Label ( " Use PWM Buzzer " , systemImage : " light.beacon.max.fill " )
2024-02-21 23:35:28 -08:00
Text ( " Use a PWM output (like the RAK Buzzer) for tunes instead of an on/off output. This will ignore the output, output duration and active settings and use the device config buzzer GPIO option instead. " )
2022-06-22 09:05:56 -07:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-05-29 16:40:07 -05:00
2023-12-20 10:24:01 -08:00
Toggle ( isOn : $ useI2SAsBuzzer ) {
Label ( " Use I2S As Buzzer " , systemImage : " light.beacon.max.fill " )
2024-02-21 23:35:28 -08:00
Text ( " Enables devices with native I2S audio output to use the RTTTL over speaker like a buzzer. T-Watch S3 and T-Deck for example have this capability. " )
2023-12-20 10:24:01 -08:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2023-12-06 12:32:17 -08:00
}
Section ( header : Text ( " Advanced GPIO Options " ) ) {
Section ( header : Text ( " Primary GPIO " )
. font ( . caption )
. foregroundColor ( . gray )
. textCase ( . uppercase ) ) {
Toggle ( isOn : $ active ) {
Label ( " Active " , systemImage : " togglepower " )
2024-02-21 23:35:28 -08:00
Text ( " If enabled, the 'output' Pin will be pulled active high, disabled means active low. " )
2023-12-06 12:32:17 -08:00
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Picker ( " Output pin GPIO " , selection : $ output ) {
2024-01-22 13:21:17 -08:00
ForEach ( 0. . < 49 ) {
2023-12-06 12:32:17 -08:00
if $0 = = 0 {
2025-04-27 16:19:10 -07:00
Text ( " Unset " )
2023-12-06 12:32:17 -08:00
} else {
Text ( " Pin \( $0 ) " )
}
}
2022-06-22 15:52:51 -07:00
}
2023-12-06 12:32:17 -08:00
. pickerStyle ( DefaultPickerStyle ( ) )
2024-02-21 23:35:28 -08:00
. listRowSeparator ( . visible )
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Picker ( " GPIO Output Duration " , selection : $ outputMilliseconds ) {
ForEach ( OutputIntervals . allCases ) { oi in
Text ( oi . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2024-02-21 23:35:28 -08:00
. listRowSeparator ( . hidden )
2023-12-06 12:32:17 -08:00
Text ( " When using in GPIO mode, keep the output on for this long. " )
2024-02-21 23:35:28 -08:00
. foregroundColor ( . gray )
. font ( . callout )
. listRowSeparator ( . visible )
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Picker ( " Nag timeout " , selection : $ nagTimeout ) {
2024-05-31 20:25:37 -07:00
ForEach ( NagIntervals . allCases ) { oi in
2023-12-06 12:32:17 -08:00
Text ( oi . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2024-02-21 23:35:28 -08:00
. listRowSeparator ( . hidden )
2023-12-06 12:32:17 -08:00
Text ( " Specifies how long the monitored GPIO should output. " )
2024-02-21 23:35:28 -08:00
. foregroundColor ( . gray )
. font ( . callout )
2022-06-22 09:05:56 -07:00
}
2024-05-29 16:40:07 -05:00
2023-12-06 12:32:17 -08:00
Section ( header : Text ( " Optional GPIO " )
. font ( . caption )
. foregroundColor ( . gray )
. textCase ( . uppercase ) ) {
Toggle ( isOn : $ alertBellBuzzer ) {
Label ( " Alert GPIO buzzer when receiving a bell " , systemImage : " bell " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ alertBellVibra ) {
Label ( " Alert GPIO vibra motor when receiving a bell " , systemImage : " bell " )
2022-11-29 21:24:20 -08:00
}
2023-12-06 12:32:17 -08:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ alertMessageBuzzer ) {
Label ( " Alert GPIO buzzer when receiving a message " , systemImage : " message " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Toggle ( isOn : $ alertMessageBuzzer ) {
Label ( " Alert GPIO vibra motor when receiving a message " , systemImage : " message " )
}
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
Picker ( " Output pin buzzer GPIO " , selection : $ outputBuzzer ) {
2024-01-22 13:21:17 -08:00
ForEach ( 0. . < 49 ) {
2023-12-06 12:32:17 -08:00
if $0 = = 0 {
2025-04-27 16:19:10 -07:00
Text ( " Unset " )
2023-12-06 12:32:17 -08:00
} else {
Text ( " Pin \( $0 ) " )
}
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
Picker ( " Output pin vibra GPIO " , selection : $ outputVibra ) {
2024-01-22 13:21:17 -08:00
ForEach ( 0. . < 49 ) {
2023-12-06 12:32:17 -08:00
if $0 = = 0 {
2025-04-27 16:19:10 -07:00
Text ( " Unset " )
2023-12-06 12:32:17 -08:00
} else {
Text ( " Pin \( $0 ) " )
}
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2022-06-29 20:04:20 -07:00
}
2022-07-01 10:57:54 -07:00
}
}
2023-12-06 12:32:17 -08:00
. disabled ( self . bleManager . connectedPeripheral = = nil || node ? . externalNotificationConfig = = nil )
2022-12-23 11:30:55 -08:00
}
2024-02-18 00:03:34 -07:00
SaveConfigButton ( node : node , hasChanges : $ hasChanges ) {
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 {
2024-02-18 00:03:34 -07:00
var enc = ModuleConfig . ExternalNotificationConfig ( )
enc . enabled = enabled
enc . alertBell = alertBell
enc . alertBellBuzzer = alertBellBuzzer
enc . alertBellVibra = alertBellVibra
enc . alertMessage = alertMessage
enc . alertMessageBuzzer = alertMessageBuzzer
enc . alertMessageVibra = alertMessageVibra
enc . active = active
enc . output = UInt32 ( output )
enc . nagTimeout = UInt32 ( nagTimeout )
enc . outputBuzzer = UInt32 ( outputBuzzer )
enc . outputVibra = UInt32 ( outputVibra )
enc . outputMs = UInt32 ( outputMilliseconds )
enc . usePwm = usePWM
enc . useI2SAsBuzzer = useI2SAsBuzzer
2025-06-14 14:25:46 -07:00
let adminMessageId = bleManager . saveExternalNotificationModuleConfig ( config : enc , fromUser : connectedNode ! . user ! , toUser : node ! . user ! )
2024-02-18 00:03:34 -07:00
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-07-11 16:18:16 -07:00
}
2022-07-02 12:34:45 -07:00
}
2022-12-23 11:30:55 -08:00
}
2025-05-08 22:50:44 -07:00
. navigationTitle ( " External Notification Config " )
2024-08-11 09:07:22 -07:00
. navigationBarItems (
trailing : ZStack {
ConnectedDevice (
bluetoothOn : bleManager . isSwitchedOn ,
deviceConnected : bleManager . connectedPeripheral != nil ,
name : bleManager . connectedPeripheral ? . shortName ? ? " ? "
)
}
)
2024-09-05 19:31:29 -07:00
. onFirstAppear {
// N e e d t o r e q u e s t a E x t e r n a l N o t i f i c a t i o n 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
if let connectedPeripheral = bleManager . connectedPeripheral , let node {
let connectedNode = getNodeInfo ( id : connectedPeripheral . num , context : context )
if let connectedNode {
if node . num != connectedNode . num {
if UserDefaults . enableAdministration && node . num != connectedNode . num {
// / 2 . 5 A d m i n i s t r a t i o n w i t h s e s s i o n p a s s k e y
let expiration = node . sessionExpiration ? ? Date ( )
if expiration < Date ( ) || node . externalNotificationConfig = = nil {
2024-11-29 13:15:46 -08:00
Logger . mesh . info ( " ⚙️ Empty or expired external notificaiton module config requesting via PKI admin " )
2025-06-14 14:25:46 -07:00
_ = bleManager . requestExternalNotificationModuleConfig ( fromUser : connectedNode . user ! , toUser : node . user ! )
2024-09-05 19:31:29 -07:00
}
} else {
// / L e g a c y A d m i n i s t r a t i o n
2025-05-13 06:19:27 -07:00
Logger . mesh . info ( " ☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware. " )
2024-09-05 19:31:29 -07:00
}
}
2023-02-01 09:19:45 -08:00
}
}
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : enabled ) { _ , newEnabled in
if newEnabled != node ? . externalNotificationConfig ? . enabled { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : alertBell ) { _ , newAlertBell in
if newAlertBell != node ? . externalNotificationConfig ? . alertBell { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : alertBellBuzzer ) { _ , newAlertBellBuzzer in
if newAlertBellBuzzer != node ? . externalNotificationConfig ? . alertBellBuzzer { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : alertBellVibra ) { _ , newAlertBellVibra in
if newAlertBellVibra != node ? . externalNotificationConfig ? . alertBellVibra { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : alertMessage ) { _ , newAlertMessage in
if newAlertMessage != node ? . externalNotificationConfig ? . alertMessage { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : alertMessageBuzzer ) { _ , newAlertMessageBuzzer in
if newAlertMessageBuzzer != node ? . externalNotificationConfig ? . alertMessageBuzzer { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : alertMessageVibra ) { _ , newAlertMessageVibra in
if newAlertMessageVibra != node ? . externalNotificationConfig ? . alertMessageVibra { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : active ) { _ , newActive in
if newActive != node ? . externalNotificationConfig ? . active { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : output ) { _ , newOutput in
if newOutput != node ? . externalNotificationConfig ? . output ? ? - 1 { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : output ) { _ , newOutputBuzzer in
if newOutputBuzzer != node ? . externalNotificationConfig ? . outputBuzzer ? ? - 1 { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : output ) { _ , newOutputVibra in
if newOutputVibra != node ? . externalNotificationConfig ? . outputVibra ? ? - 1 { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : outputMilliseconds ) { _ , newOutputMs in
if newOutputMs != node ? . externalNotificationConfig ? . outputMilliseconds ? ? - 1 { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : usePWM ) { _ , newPWM in
if newPWM != node ? . externalNotificationConfig ? . usePWM { hasChanges = true }
2022-12-23 11:30:55 -08:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : nagTimeout ) { _ , newNagTimeout in
if newNagTimeout != node ? . externalNotificationConfig ? . nagTimeout ? ? - 1 { hasChanges = true }
2022-06-22 09:05:56 -07:00
}
2024-10-06 08:50:12 -07:00
. onChange ( of : useI2SAsBuzzer ) { _ , newUseI2SAsBuzzer in
if newUseI2SAsBuzzer != node ? . externalNotificationConfig ? . useI2SAsBuzzer { hasChanges = true }
2023-12-20 10:24:01 -08:00
}
2022-06-22 09:05:56 -07:00
}
2023-03-19 18:37:23 -07:00
func setExternalNotificationValues ( ) {
self . enabled = node ? . externalNotificationConfig ? . enabled ? ? false
self . alertBell = node ? . externalNotificationConfig ? . alertBell ? ? false
self . alertBellBuzzer = node ? . externalNotificationConfig ? . alertBellBuzzer ? ? false
self . alertBellVibra = node ? . externalNotificationConfig ? . alertBellVibra ? ? false
self . alertMessage = node ? . externalNotificationConfig ? . alertMessage ? ? false
self . alertMessageBuzzer = node ? . externalNotificationConfig ? . alertMessageBuzzer ? ? false
self . alertMessageVibra = node ? . externalNotificationConfig ? . alertMessageVibra ? ? false
self . active = node ? . externalNotificationConfig ? . active ? ? false
self . output = Int ( node ? . externalNotificationConfig ? . output ? ? 0 )
self . outputBuzzer = Int ( node ? . externalNotificationConfig ? . outputBuzzer ? ? 0 )
self . outputVibra = Int ( node ? . externalNotificationConfig ? . outputVibra ? ? 0 )
self . outputMilliseconds = Int ( node ? . externalNotificationConfig ? . outputMilliseconds ? ? 0 )
self . nagTimeout = Int ( node ? . externalNotificationConfig ? . nagTimeout ? ? 0 )
self . usePWM = node ? . externalNotificationConfig ? . usePWM ? ? false
2023-12-20 10:24:01 -08:00
self . useI2SAsBuzzer = node ? . externalNotificationConfig ? . useI2SAsBuzzer ? ? false
2023-03-19 18:37:23 -07:00
self . hasChanges = false
}
2022-06-22 09:05:56 -07:00
}