2022-06-14 16:45:43 -07:00
//
// T e l e m e t r y 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 / 1 3 / 2 2 .
//
import SwiftUI
2022-06-22 09:05:56 -07:00
enum SensorTypes : Int , CaseIterable , Identifiable {
// / N o e x t e r n a l t e l e m e t r y s e n s o r e x p l i c i t l y s e t
case notSet = 0
// / H i g h a c c u r a c y t e m p e r a t u r e , p r e s s u r e , h u m i d i t y
case bme280 = 6
// / H i g h a c c u r a c y t e m p e r a t u r e , p r e s s u r e , h u m i d i t y , a n d a i r r e s i s t a n c e
case bme680 = 7
// / V e r y h i g h a c c u r a c y t e m p e r a t u r e
case mcp9808 = 8
// / M o d e r a t e a c c u r a c y t e m p e r a t u r e a n d h u m i d i t y
case shtc3 = 9
// / M o d e r a t e a c c u r a c y c u r r e n t a n d v o l t a g e
case ina260 = 10
// / M o d e r a t e a c c u r a c y c u r r e n t a n d v o l t a g e
case ina219 = 11
var id : Int { self . rawValue }
var description : String {
get {
switch self {
case . notSet :
return " Not Set "
case . bme280 :
2022-06-22 15:52:51 -07:00
return " BME280 - Temp, pressure & humidity "
2022-06-22 09:05:56 -07:00
case . bme680 :
2022-06-22 15:52:51 -07:00
return " BME680 - Temp, pressure, humidity & air resistance "
2022-06-22 09:05:56 -07:00
case . mcp9808 :
2022-06-22 15:52:51 -07:00
return " MCP9808 - Temperature "
2022-06-22 09:05:56 -07:00
case . shtc3 :
2022-06-22 15:52:51 -07:00
return " SHTC3 - Temperature & humidity "
2022-06-22 09:05:56 -07:00
case . ina260 :
2022-06-22 15:52:51 -07:00
return " INA260 - Current & voltage "
2022-06-22 09:05:56 -07:00
case . ina219 :
2022-06-22 15:52:51 -07:00
return " INA219 - Current & voltage "
2022-06-22 09:05:56 -07:00
}
}
}
2022-07-01 10:57:54 -07:00
func protoEnumValue ( ) -> TelemetrySensorType {
switch self {
case . notSet :
return TelemetrySensorType . notSet
case . bme280 :
return TelemetrySensorType . bme280
case . bme680 :
return TelemetrySensorType . bme680
case . mcp9808 :
return TelemetrySensorType . mcp9808
case . shtc3 :
return TelemetrySensorType . shtc3
case . ina260 :
return TelemetrySensorType . ina260
case . ina219 :
return TelemetrySensorType . ina219
}
}
2022-06-22 09:05:56 -07:00
}
// D e f a u l t o f 0 i s o f f
enum ErrorRecoveryIntervals : Int , CaseIterable , Identifiable {
case off = 0
case fifteenSeconds = 15
case thirtySeconds = 30
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
case thirtyMinutes = 1800
case oneHour = 3600
var id : Int { self . rawValue }
var description : String {
get {
switch self {
case . off :
2022-06-22 15:52:51 -07:00
return " Unset "
2022-06-22 09:05:56 -07:00
case . fifteenSeconds :
return " Fifteen Seconds "
case . thirtySeconds :
return " Thirty Seconds "
case . oneMinute :
return " One Minute "
case . fiveMinutes :
return " Five Minutes "
case . tenMinutes :
return " Ten Minutes "
case . fifteenMinutes :
return " Fifteen Minutes "
case . thirtyMinutes :
return " Thirty Minutes "
case . oneHour :
return " One Hour "
}
}
}
}
enum UpdateIntervals : Int , CaseIterable , Identifiable {
case fifteenSeconds = 15
case thirtySeconds = 30
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
2022-06-22 15:52:51 -07:00
case fifteenMinutes = 0
2022-06-22 09:05:56 -07:00
case thirtyMinutes = 1800
case oneHour = 3600
2022-06-22 15:52:51 -07:00
case twoHours = 7200
case threeHours = 10800
case fourHours = 14400
case fiveHours = 18000
case sixHours = 21600
case twelveHours = 43200
case eighteenHours = 64800
case twentyFourHours = 86400
2022-06-22 09:05:56 -07:00
var id : Int { self . rawValue }
var description : String {
get {
switch self {
case . fifteenSeconds :
return " Fifteen Seconds "
case . thirtySeconds :
return " Thirty Seconds "
case . oneMinute :
return " One Minute "
case . fiveMinutes :
return " Five Minutes "
case . tenMinutes :
return " Ten Minutes "
case . fifteenMinutes :
return " Fifteen Minutes "
case . thirtyMinutes :
return " Thirty Minutes "
case . oneHour :
return " One Hour "
2022-06-22 15:52:51 -07:00
case . twoHours :
return " Two Hours "
case . threeHours :
return " Three Hours "
case . fourHours :
return " Four Hours "
case . fiveHours :
return " Five Hours "
case . sixHours :
return " Six Hours "
case . twelveHours :
return " Twelve Hours "
case . eighteenHours :
return " Eighteen Hours "
case . twentyFourHours :
return " Twenty Four Hours "
2022-06-22 09:05:56 -07:00
}
}
}
}
2022-06-14 16:45:43 -07:00
struct TelemetryConfig : View {
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-07-01 10:57:54 -07:00
var node : NodeInfoEntity
@ State private var isPresentingSaveConfirm : Bool = false
@ State var initialLoad : Bool = true
@ State var hasChanges = false
2022-06-22 09:05:56 -07:00
@ State var deviceUpdateInterval = 0
@ State var environmentUpdateInterval = 0
@ State var environmentMeasurementEnabled = false
@ State var environmentSensorType = 0
@ State var environmentScreenEnabled = false
@ State var environmentDisplayFahrenheit = false
@ State var environmentRecoveryInterval = 0
@ State var environmentReadErrorCountThreshold = 0
2022-06-14 16:45:43 -07:00
var body : some View {
VStack {
Form {
2022-06-22 09:05:56 -07:00
Section ( header : Text ( " Update Intervals " ) ) {
Picker ( " Device Metrics " , selection : $ deviceUpdateInterval ) {
ForEach ( UpdateIntervals . allCases ) { ui in
Text ( ui . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2022-06-22 15:52:51 -07:00
Text ( " How often device metrics are sent out over the mesh. Default is 15 minutes. " )
. font ( . caption )
2022-06-22 09:05:56 -07:00
Picker ( " Sensor Metrics " , selection : $ environmentUpdateInterval ) {
ForEach ( UpdateIntervals . allCases ) { ui in
Text ( ui . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
2022-06-22 15:52:51 -07:00
Text ( " How often sensor metrics are sent out over the mesh. Default is 15 minutes. " )
. font ( . caption )
2022-06-22 09:05:56 -07:00
}
Section ( header : Text ( " Sensor Options " ) ) {
Toggle ( isOn : $ environmentMeasurementEnabled ) {
Label ( " Enabled " , systemImage : " chart.xyaxis.line " )
}
2022-06-22 15:52:51 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-22 09:05:56 -07:00
Picker ( " Sensor " , selection : $ environmentSensorType ) {
ForEach ( SensorTypes . allCases ) { st in
Text ( st . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
Toggle ( isOn : $ environmentScreenEnabled ) {
Label ( " Show on device screen " , systemImage : " display " )
}
2022-06-22 15:52:51 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-22 09:05:56 -07:00
Toggle ( isOn : $ environmentDisplayFahrenheit ) {
Label ( " Display Fahrenheit " , systemImage : " thermometer " )
}
2022-06-22 15:52:51 -07:00
. toggleStyle ( SwitchToggleStyle ( tint : . accentColor ) )
2022-06-22 09:05:56 -07:00
}
Section ( header : Text ( " Errors " ) ) {
Picker ( " Error Count Threshold " , selection : $ environmentReadErrorCountThreshold ) {
ForEach ( 0. . < 101 ) {
if $0 = = 0 {
2022-06-22 15:52:51 -07:00
Text ( " Unset " )
2022-06-22 09:05:56 -07:00
} else if $0 % 5 = = 0 {
Text ( " \( $0 ) " )
}
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
Text ( " Sometimes sensor reads can fail. If this happens, we will retry a configurable number of attempts, each attempt will be delayed by the minimum required refresh rate for that sensor " )
. font ( . caption )
Picker ( " Error Recovery Interval " , selection : $ environmentRecoveryInterval ) {
ForEach ( ErrorRecoveryIntervals . allCases ) { eri in
Text ( eri . description )
}
}
. pickerStyle ( DefaultPickerStyle ( ) )
Text ( " Sometimes we can end up with more failures than our error count threshold. In this case, we will stop trying to read from the sensor for a while. Wait this long until trying to read from the sensor again " )
. font ( . caption )
}
2022-06-14 16:45:43 -07:00
}
2022-07-01 10:57:54 -07:00
Button {
isPresentingSaveConfirm = true
} label : {
Label ( " Save " , systemImage : " square.and.arrow.down " )
}
. disabled ( bleManager . connectedPeripheral = = nil || ! hasChanges || ! ( node . myInfo ? . hasWifi ? ? false ) )
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . large )
. padding ( )
. confirmationDialog (
" Are you sure? " ,
isPresented : $ isPresentingSaveConfirm
) {
Button ( " Save Telemetry Module Config to \( bleManager . connectedPeripheral != nil ? bleManager . connectedPeripheral . longName : " Unknown " ) ? " ) {
var tc = ModuleConfig . TelemetryConfig ( )
2022-07-02 13:25:34 -07:00
tc . deviceUpdateInterval = UInt32 ( deviceUpdateInterval )
tc . environmentUpdateInterval = UInt32 ( environmentUpdateInterval )
2022-07-01 10:57:54 -07:00
tc . environmentMeasurementEnabled = environmentMeasurementEnabled
tc . environmentSensorType = SensorTypes ( rawValue : environmentSensorType ) ! . protoEnumValue ( )
tc . environmentScreenEnabled = environmentScreenEnabled
tc . environmentDisplayFahrenheit = environmentDisplayFahrenheit
tc . environmentRecoveryInterval = UInt32 ( environmentRecoveryInterval )
tc . environmentReadErrorCountThreshold = UInt32 ( environmentReadErrorCountThreshold )
// i f b l e M a n a g e r . s a v e R a n g e T e s t M o d u l e C o n f i g ( c o n f i g : r t c , d e s t N u m : b l e M a n a g e r . c o n n e c t e d P e r i p h e r a l . n u m , w a n t R e s p o n s e : f a l s e ) {
// 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
// } e l s e {
// }
}
}
2022-06-14 16:45:43 -07:00
. navigationTitle ( " Telemetry Config " )
. navigationBarItems ( trailing :
ZStack {
2022-07-01 19:44:25 -07:00
ConnectedDevice ( bluetoothOn : bleManager . isSwitchedOn , deviceConnected : bleManager . connectedPeripheral != nil , name : ( bleManager . connectedPeripheral != nil ) ? bleManager . connectedPeripheral . shortName : " ???? " )
2022-06-14 16:45:43 -07:00
} )
. onAppear {
2022-07-02 13:43:13 -07:00
if self . initialLoad {
self . bleManager . context = context
self . deviceUpdateInterval = Int ( node . telemetryConfig ? . deviceUpdateInterval ? ? 0 )
self . environmentUpdateInterval = Int ( node . telemetryConfig ? . environmentUpdateInterval ? ? 0 )
self . environmentMeasurementEnabled = node . telemetryConfig ? . environmentMeasurementEnabled ? ? false
self . environmentSensorType = Int ( node . telemetryConfig ? . environmentSensorType ? ? 0 )
self . environmentScreenEnabled = node . telemetryConfig ? . environmentScreenEnabled ? ? false
self . environmentDisplayFahrenheit = node . telemetryConfig ? . environmentDisplayFahrenheit ? ? false
self . environmentRecoveryInterval = Int ( node . telemetryConfig ? . environmentRecoveryInterval ? ? 0 )
self . environmentReadErrorCountThreshold = Int ( node . telemetryConfig ? . environmentReadErrorCountThreshold ? ? 0 )
self . hasChanges = false
self . initialLoad = false
}
}
. onChange ( of : deviceUpdateInterval ) { newDeviceInterval in
if newDeviceInterval != node . telemetryConfig ! . deviceUpdateInterval { hasChanges = true }
}
. onChange ( of : environmentUpdateInterval ) { newEnvInterval in
if newEnvInterval != node . telemetryConfig ! . environmentUpdateInterval { hasChanges = true }
}
. onChange ( of : environmentMeasurementEnabled ) { newEnvEnabled in
if newEnvEnabled != node . telemetryConfig ! . environmentMeasurementEnabled { hasChanges = true }
}
. onChange ( of : environmentSensorType ) { newEnvSensorType in
if newEnvSensorType != node . telemetryConfig ! . environmentSensorType { hasChanges = true }
}
. onChange ( of : environmentScreenEnabled ) { newEnvScreenEnabled in
if newEnvScreenEnabled != node . telemetryConfig ! . environmentScreenEnabled { hasChanges = true }
}
. onChange ( of : environmentDisplayFahrenheit ) { newEnvDisplayF in
if newEnvDisplayF != node . telemetryConfig ! . environmentDisplayFahrenheit { hasChanges = true }
}
. onChange ( of : environmentRecoveryInterval ) { newEnvRecoveryInterval in
if newEnvRecoveryInterval != node . telemetryConfig ! . environmentRecoveryInterval { hasChanges = true }
}
. onChange ( of : environmentReadErrorCountThreshold ) { newEnvReadErrorCountThreshold in
if newEnvReadErrorCountThreshold != node . telemetryConfig ! . environmentReadErrorCountThreshold { hasChanges = true }
2022-06-14 16:45:43 -07:00
}
. navigationViewStyle ( StackNavigationViewStyle ( ) )
}
}
}