mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #162 from meshtastic/text_size_cleanup
Assorted Layout Cleanup, MQTT Config
This commit is contained in:
commit
e89b134827
16 changed files with 605 additions and 58 deletions
|
|
@ -16,6 +16,7 @@
|
|||
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; };
|
||||
DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */; };
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; };
|
||||
DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2160AE28C5552500C17253 /* MQTTConfig.swift */; };
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; };
|
||||
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553562855B02500E55709 /* LoRaConfig.swift */; };
|
||||
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; };
|
||||
|
|
@ -121,6 +122,8 @@
|
|||
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = "<group>"; };
|
||||
DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = storeforward.pb.swift; sourceTree = "<group>"; };
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
|
||||
DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 10.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = "<group>"; };
|
||||
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
|
||||
DD2553562855B02500E55709 /* LoRaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaConfig.swift; sourceTree = "<group>"; };
|
||||
DD2553582855B52700E55709 /* PositionConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfig.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -260,6 +263,13 @@
|
|||
path = Custom;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD2160AC28C5019400C17253 /* Messages */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Messages;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD47E3CA26F0E50300029299 /* Nodes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -318,6 +328,7 @@
|
|||
DD41582928585C32009B0E59 /* RangeTestConfig.swift */,
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */,
|
||||
DD415827285859C4009B0E59 /* TelemetryConfig.swift */,
|
||||
DD2160AE28C5552500C17253 /* MQTTConfig.swift */,
|
||||
);
|
||||
path = Module;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -488,6 +499,7 @@
|
|||
DDC2E18D26CE25CB0042C5E4 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD2160AC28C5019400C17253 /* Messages */,
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */,
|
||||
DD47E3D826F3093800029299 /* MessageBubble.swift */,
|
||||
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */,
|
||||
|
|
@ -705,6 +717,7 @@
|
|||
DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */,
|
||||
DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */,
|
||||
DD35018B2852FC79000FC853 /* UserSettings.swift in Sources */,
|
||||
DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */,
|
||||
DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */,
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
|
||||
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */,
|
||||
|
|
@ -936,7 +949,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.3.39;
|
||||
MARKETING_VERSION = 1.3.40;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -968,7 +981,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.3.39;
|
||||
MARKETING_VERSION = 1.3.40;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1141,6 +1154,7 @@
|
|||
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */,
|
||||
DD5929A528C0F292003DB21D /* MeshtasticDataModel v 9.xcdatamodel */,
|
||||
DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */,
|
||||
DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */,
|
||||
|
|
@ -1151,7 +1165,7 @@
|
|||
DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */,
|
||||
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD5929A528C0F292003DB21D /* MeshtasticDataModel v 9.xcdatamodel */;
|
||||
currentVersion = DD2160AD28C5536B00C17253 /* MeshtasticDataModel v 10.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -1418,6 +1418,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return 0
|
||||
}
|
||||
|
||||
public func saveMQTTConfig(config: ModuleConfig.MQTTConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setModuleConfig.mqtt = config
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
meshPacket.hopLimit = 0
|
||||
|
||||
var dataMessage = DataMessage()
|
||||
dataMessage.payload = try! adminPacket.serializedData()
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
|
||||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "Saved WiFi Config for \(toUser.longName ?? "Unknown")"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
public func saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
|
|
|
|||
|
|
@ -497,7 +497,6 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
newWiFiConfig.password = config.wifi.psk
|
||||
newWiFiConfig.mode = Int32(config.wifi.mode.rawValue)
|
||||
}
|
||||
newWiFiConfig.num = fetchedNode[0].num
|
||||
fetchedNode[0].wiFiConfig = newWiFiConfig
|
||||
|
||||
} else {
|
||||
|
|
@ -596,8 +595,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
|
|||
newCannedMessageConfig.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue)
|
||||
newCannedMessageConfig.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue)
|
||||
}
|
||||
|
||||
newCannedMessageConfig.num = nodeNum
|
||||
fetchedNode[0].cannedMessageConfig = newCannedMessageConfig
|
||||
|
||||
} else {
|
||||
|
|
@ -628,7 +625,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
|
|||
fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue)
|
||||
fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue)
|
||||
}
|
||||
fetchedNode[0].cannedMessageConfig?.num = nodeNum
|
||||
}
|
||||
|
||||
do {
|
||||
|
|
@ -703,8 +699,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
|
|||
newExternalNotificationConfig.output = Int32(config.externalNotification.output)
|
||||
newExternalNotificationConfig.outputMilliseconds = Int32(config.externalNotification.outputMs)
|
||||
}
|
||||
|
||||
newExternalNotificationConfig.num = nodeNum
|
||||
fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig
|
||||
|
||||
} else {
|
||||
|
|
@ -727,7 +721,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
|
|||
fetchedNode[0].externalNotificationConfig?.output = Int32(config.externalNotification.output)
|
||||
fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.externalNotification.outputMs)
|
||||
}
|
||||
fetchedNode[0].externalNotificationConfig?.num = nodeNum
|
||||
}
|
||||
|
||||
do {
|
||||
|
|
@ -789,7 +782,6 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
|
|||
newRangeTestConfig.enabled = config.rangeTest.enabled
|
||||
newRangeTestConfig.save = config.rangeTest.save
|
||||
}
|
||||
newRangeTestConfig.num = nodeNum
|
||||
fetchedNode[0].rangeTestConfig = newRangeTestConfig
|
||||
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModel v 9.xcdatamodel</string>
|
||||
<string>MeshtasticDataModel v 10.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,233 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<fetchedProperty name="tapbacks" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<relationship name="wiFiConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="WiFiConfigEntity" inverseName="wiFiConfigNode" inverseEntity="WiFiConfigEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="team" optional="YES" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="adminMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(toUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isEmoji == false AND admin = false"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="wiFiConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="wiFiConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="BluetoothConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
|
||||
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="194"/>
|
||||
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="89"/>
|
||||
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
|
||||
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="134"/>
|
||||
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="344"/>
|
||||
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="89"/>
|
||||
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="149"/>
|
||||
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="119"/>
|
||||
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
|
||||
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
|
||||
<element name="MQTTConfigEntity" positionX="45" positionY="144" width="128" height="134"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -732,8 +732,8 @@ struct Config {
|
|||
var region: Config.LoRaConfig.RegionCode = .unset
|
||||
|
||||
///
|
||||
/// Overrides HOPS_RELIABLE and sets the maximum number of hops. This can't be greater than 7.
|
||||
/// 0 for default of 3
|
||||
/// Maximum number of hops. This can't be greater than 7.
|
||||
/// Default of 3
|
||||
var hopLimit: UInt32 = 0
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -205,6 +205,10 @@ struct ModuleConfig {
|
|||
/// Decrypted packets may be useful for external systems that want to consume meshtastic packets
|
||||
var encryptionEnabled: Bool = false
|
||||
|
||||
///
|
||||
/// Whether to send / consume json packets on MQTT
|
||||
var jsonEnabled: Bool = false
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -850,6 +854,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
3: .same(proto: "username"),
|
||||
4: .same(proto: "password"),
|
||||
5: .standard(proto: "encryption_enabled"),
|
||||
6: .standard(proto: "json_enabled"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -863,6 +868,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
case 3: try { try decoder.decodeSingularStringField(value: &self.username) }()
|
||||
case 4: try { try decoder.decodeSingularStringField(value: &self.password) }()
|
||||
case 5: try { try decoder.decodeSingularBoolField(value: &self.encryptionEnabled) }()
|
||||
case 6: try { try decoder.decodeSingularBoolField(value: &self.jsonEnabled) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -884,6 +890,9 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
if self.encryptionEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.encryptionEnabled, fieldNumber: 5)
|
||||
}
|
||||
if self.jsonEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.jsonEnabled, fieldNumber: 6)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -893,6 +902,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
if lhs.username != rhs.username {return false}
|
||||
if lhs.password != rhs.password {return false}
|
||||
if lhs.encryptionEnabled != rhs.encryptionEnabled {return false}
|
||||
if lhs.jsonEnabled != rhs.jsonEnabled {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ struct Connect: View {
|
|||
@State var isPreferredRadio: Bool = false
|
||||
|
||||
@State var firmwareVersion = "0.0.0"
|
||||
@State var minimumVersion = "1.3.39"
|
||||
@State var minimumVersion = "1.3.40"
|
||||
@State var invalidVersion = false
|
||||
|
||||
var body: some View {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ struct Contacts: View {
|
|||
var body: some View {
|
||||
|
||||
NavigationView {
|
||||
|
||||
// Display Contact for Primary Channel
|
||||
// Display Contacts for DM's on the Primary Channel
|
||||
// Display Contacts for the rest of the non admin channels
|
||||
|
||||
|
||||
List(users) { (user: UserEntity) in
|
||||
|
||||
|
|
|
|||
|
|
@ -64,17 +64,21 @@ struct NodeDetail: View {
|
|||
)
|
||||
}
|
||||
.ignoresSafeArea(.all, edges: [.leading, .trailing])
|
||||
.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.75)
|
||||
.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.6)
|
||||
}
|
||||
}
|
||||
Text("Sats: \(mostRecent.satsInView)").offset( y:-40)
|
||||
} else {
|
||||
|
||||
Image(node.user?.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(10)
|
||||
.frame(width: bounds.size.width, height: bounds.size.height / 2)
|
||||
HStack {
|
||||
Image(node.user?.hwModel ?? "UNSET")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.cornerRadius(10)
|
||||
.frame(width: bounds.size.width, height: bounds.size.height / 2.3)
|
||||
.padding([.top], 40)
|
||||
}
|
||||
.offset( y:-40)
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
|
|
@ -168,6 +172,7 @@ struct NodeDetail: View {
|
|||
|
||||
Image(hwModelString)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 90, height: 90)
|
||||
.cornerRadius(5)
|
||||
|
||||
|
|
@ -429,16 +434,16 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.frame(minHeight:170)
|
||||
.frame(minHeight: 170)
|
||||
.padding(0)
|
||||
}
|
||||
.offset( y: (node.myInfo!.hasGps ? 0 : -40))
|
||||
.offset( y: (node.myInfo?.hasGps ?? false ? 0 : -40))
|
||||
}
|
||||
.edgesIgnoringSafeArea([.leading, .trailing])
|
||||
}
|
||||
}
|
||||
.navigationTitle((node.user != nil) ? String(node.user!.longName ?? "Unknown") : "Unknown")
|
||||
.navigationBarTitleDisplayMode(.automatic)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ struct NodeList: View {
|
|||
}
|
||||
.padding([.leading, .top, .bottom])
|
||||
}
|
||||
.isDetailLink(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ struct CannedMessagesConfig: View {
|
|||
/// Generate input event on Press of this kind.
|
||||
@State var inputbrokerEventPress = 0
|
||||
|
||||
@State var messagesPart1 = ""
|
||||
@State var messages = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
@ -160,10 +160,34 @@ struct CannedMessagesConfig: View {
|
|||
.padding(.bottom, 10)
|
||||
|
||||
}
|
||||
Section(header: Text("Messages")) {
|
||||
|
||||
TextEditor(text: $messagesPart1)
|
||||
|
||||
HStack {
|
||||
Label("Messages", systemImage: "message.fill")
|
||||
TextField("Messages seperate with |", text: $messages)
|
||||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: messages, perform: { value in
|
||||
|
||||
let totalBytes = messages.utf8.count
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 198 {
|
||||
|
||||
let firstNBytes = Data(messages.utf8.prefix(198))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
messages = maxBytesString
|
||||
}
|
||||
}
|
||||
hasMessagesChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
|
||||
Section(header: Text("Control Type")) {
|
||||
|
||||
|
||||
|
|
@ -331,7 +355,7 @@ struct CannedMessagesConfig: View {
|
|||
|
||||
if hasMessagesChanges {
|
||||
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messagesPart1, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messages, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -465,13 +489,6 @@ struct CannedMessagesConfig: View {
|
|||
if newKeyPress != node!.cannedMessageConfig!.inputbrokerEventPress { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: messagesPart1) { newMessagesChanges in
|
||||
|
||||
if node != nil && node!.cannedMessageConfig != nil {
|
||||
|
||||
hasMessagesChanges = true
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
233
Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift
Normal file
233
Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
//
|
||||
// MQTT.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright (c) Garth Vander Houwen 9/4/22.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct MQTTConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var initialLoad: Bool = true
|
||||
@State var hasChanges: Bool = false
|
||||
|
||||
@State var enabled = false
|
||||
@State var address = ""
|
||||
@State var username = ""
|
||||
@State var password = ""
|
||||
@State var encryptionEnabled = false
|
||||
@State var jsonEnabled = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
Form {
|
||||
|
||||
Text("WiFi must also be enabled for MQTT to work. You can set uplink and downlink for each channel.")
|
||||
.font(.title3)
|
||||
|
||||
Section(header: Text("Options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
|
||||
Label("Enabled", systemImage: "dot.radiowaves.right")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
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))
|
||||
|
||||
}
|
||||
Section(header: Text("Custom Server")) {
|
||||
|
||||
HStack {
|
||||
Label("Address", systemImage: "server.rack")
|
||||
TextField("Server Address", text: $username)
|
||||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: address, perform: { value in
|
||||
|
||||
let totalBytes = address.utf8.count
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
|
||||
let firstNBytes = Data(username.utf8.prefix(30))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
address = maxBytesString
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
|
||||
HStack {
|
||||
Label("Username", systemImage: "person.text.rectangle")
|
||||
TextField("Server Username", text: $username)
|
||||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: username, perform: { value in
|
||||
|
||||
let totalBytes = username.utf8.count
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
|
||||
let firstNBytes = Data(username.utf8.prefix(30))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
username = maxBytesString
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
|
||||
|
||||
HStack {
|
||||
Label("Password", systemImage: "wallet.pass")
|
||||
TextField("Server Password", text: $password)
|
||||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: password, perform: { value in
|
||||
|
||||
let totalBytes = password.utf8.count
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
|
||||
let firstNBytes = Data(password.utf8.prefix(30))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
password = maxBytesString
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
}
|
||||
}
|
||||
.disabled(!(node != nil && node!.myInfo?.hasWifi ?? false))
|
||||
|
||||
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
|
||||
) {
|
||||
Button("Save WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
|
||||
|
||||
var mqtt = ModuleConfig.MQTTConfig()
|
||||
mqtt.enabled = self.enabled
|
||||
mqtt.address = self.address
|
||||
mqtt.username = self.username
|
||||
mqtt.password = self.password
|
||||
mqtt.encryptionEnabled = self.encryptionEnabled
|
||||
mqtt.jsonEnabled = self.jsonEnabled
|
||||
|
||||
let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: node!.user!, toUser: node!.user!)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
self.hasChanges = false
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("MQTT Config")
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
|
||||
self.enabled = (node!.mqttConfig?.enabled ?? false)
|
||||
self.address = node!.mqttConfig?.address ?? ""
|
||||
self.username = node!.mqttConfig?.username ?? ""
|
||||
self.password = node!.mqttConfig?.password ?? ""
|
||||
self.encryptionEnabled = (node!.mqttConfig?.encryptionEnabled ?? false)
|
||||
self.jsonEnabled = (node!.mqttConfig?.jsonEnabled ?? false)
|
||||
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
||||
if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: encryptionEnabled) { newEncryptionEnabled in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
||||
if newEncryptionEnabled != node!.mqttConfig!.encryptionEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: jsonEnabled) { newJsonEnabled in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
||||
if newJsonEnabled != node!.mqttConfig!.jsonEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@ struct WiFiConfig: View {
|
|||
ssid = maxBytesString
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
|
@ -99,6 +100,7 @@ struct WiFiConfig: View {
|
|||
password = maxBytesString
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
|
@ -176,20 +178,6 @@ struct WiFiConfig: View {
|
|||
if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: ssid) { newSsid in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
||||
if newSsid != node!.wiFiConfig!.ssid { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: password) { newPassword in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
||||
if newPassword != node!.wiFiConfig!.password { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: mode) { newMode in
|
||||
|
||||
if node != nil && node!.wiFiConfig != nil {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,17 @@ struct Settings: View {
|
|||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
MQTTConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
||||
Image(systemName: "dot.radiowaves.right")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
Text("MQTT (ESP32 Only)")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
NavigationLink {
|
||||
RangeTestConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
|
||||
} label: {
|
||||
|
|
|
|||
|
|
@ -62,16 +62,25 @@ struct ShareChannel: View {
|
|||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(
|
||||
minWidth: smallest * 0.9,
|
||||
maxWidth: smallest * 0.9,
|
||||
minHeight: smallest * 0.9,
|
||||
maxHeight: smallest * 0.9,
|
||||
minWidth: smallest * 0.8,
|
||||
maxWidth: smallest * 0.8,
|
||||
minHeight: smallest * 0.8,
|
||||
maxHeight: smallest * 0.8,
|
||||
alignment: .center
|
||||
)
|
||||
Spacer()
|
||||
Text("Channel Name (Long/Slow)").font(.title)
|
||||
Text(String(node!.myInfo!.maxChannels))
|
||||
Spacer()
|
||||
|
||||
if node?.loRaConfig != nil {
|
||||
|
||||
HStack {
|
||||
|
||||
let preset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset))
|
||||
Text("Modem Preset \(preset!.description)").font(.title3)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
|
||||
Text("Number of Channels: \(node!.myInfo!.maxChannels)").font(.title2)
|
||||
}
|
||||
}
|
||||
.frame(width: bounds.size.width, height: bounds.size.height)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue