mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #465 from meshtastic/2.2.20_Working_Changes
2.2.20 working changes
This commit is contained in:
commit
4c281cb1b7
36 changed files with 891 additions and 221 deletions
|
|
@ -334,6 +334,7 @@
|
|||
DDAB580E2B0DAFBC00147258 /* LocationEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DDAD49EC2AFB39DC00B4425D /* MeshMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshMap.swift; sourceTree = "<group>"; };
|
||||
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = "<group>"; };
|
||||
DDB234392B5CA9B000DA6FB1 /* MeshtasticDataModelV 24.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 24.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = "<group>"; };
|
||||
DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothModes.swift; sourceTree = "<group>"; };
|
||||
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -1491,7 +1492,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.19;
|
||||
MARKETING_VERSION = 2.2.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1525,7 +1526,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.19;
|
||||
MARKETING_VERSION = 2.2.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1647,7 +1648,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.19;
|
||||
MARKETING_VERSION = 2.2.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1680,7 +1681,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2.19;
|
||||
MARKETING_VERSION = 2.2.20;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1791,6 +1792,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DDB234392B5CA9B000DA6FB1 /* MeshtasticDataModelV 24.xcdatamodel */,
|
||||
DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */,
|
||||
DD295CE92B323ED9002CC4AC /* MeshtasticDataModelV22.xcdatamodel */,
|
||||
DD3619132B1EE20700C41C8C /* MeshtasticDataModelV21.xcdatamodel */,
|
||||
|
|
@ -1815,7 +1817,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD33DB602B3D1ECC003E1EA0 /* MeshtasticDataModelV 23.xcdatamodel */;
|
||||
currentVersion = DDB234392B5CA9B000DA6FB1 /* MeshtasticDataModelV 24.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -215,7 +215,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
id: (peripheral.identifier.uuidString),
|
||||
title: "Radio Disconnected",
|
||||
subtitle: "\(peripheral.name ?? "unknown".localized)",
|
||||
content: e.localizedDescription
|
||||
content: e.localizedDescription,
|
||||
target: "bluetooth",
|
||||
path: "meshtastic://bluetooth"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
|
@ -233,7 +235,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
id: (peripheral.identifier.uuidString),
|
||||
title: "Radio Disconnected",
|
||||
subtitle: "\(peripheral.name ?? "unknown".localized)",
|
||||
content: e.localizedDescription
|
||||
content: e.localizedDescription,
|
||||
target: "bluetooth",
|
||||
path: "meshtastic://bluetooth"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
|
@ -548,17 +552,17 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
// Channels
|
||||
if decodedInfo.channel.isInitialized && connectedPeripheral != nil {
|
||||
nowKnown = true
|
||||
channelPacket(channel: decodedInfo.channel, fromNum: connectedPeripheral.num, context: context!)
|
||||
channelPacket(channel: decodedInfo.channel, fromNum: Int64(truncatingIfNeeded: connectedPeripheral.num), context: context!)
|
||||
}
|
||||
// Config
|
||||
if decodedInfo.config.isInitialized && !invalidVersion && connectedPeripheral != nil {
|
||||
nowKnown = true
|
||||
localConfig(config: decodedInfo.config, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
localConfig(config: decodedInfo.config, context: context!, nodeNum: Int64(truncatingIfNeeded: self.connectedPeripheral.num), nodeLongName: self.connectedPeripheral.longName)
|
||||
}
|
||||
// Module Config
|
||||
if decodedInfo.moduleConfig.isInitialized && !invalidVersion && self.connectedPeripheral?.num != 0{
|
||||
nowKnown = true
|
||||
moduleConfig(config: decodedInfo.moduleConfig, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
moduleConfig(config: decodedInfo.moduleConfig, context: context!, nodeNum: Int64(truncatingIfNeeded: self.connectedPeripheral.num), nodeLongName: self.connectedPeripheral.longName)
|
||||
if decodedInfo.moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(decodedInfo.moduleConfig.cannedMessage) {
|
||||
if decodedInfo.moduleConfig.cannedMessage.enabled {
|
||||
_ = self.getCannedMessageModuleMessages(destNum: self.connectedPeripheral.num, wantResponse: true)
|
||||
|
|
|
|||
|
|
@ -31,15 +31,19 @@ class LocalNotificationManager {
|
|||
// This function iterates over the Notification objects in the notifications array and schedules them for delivery in the future
|
||||
private func scheduleNotifications() {
|
||||
for notification in notifications {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.subtitle = notification.subtitle
|
||||
content.title = notification.title
|
||||
content.body = notification.content
|
||||
content.sound = .default
|
||||
content.interruptionLevel = .timeSensitive
|
||||
let content = UNMutableNotificationContent()
|
||||
content.subtitle = notification.subtitle
|
||||
content.title = notification.title
|
||||
content.body = notification.content
|
||||
content.sound = .default
|
||||
content.interruptionLevel = .timeSensitive
|
||||
|
||||
if notification.target != nil {
|
||||
content.userInfo["target"] = notification.target
|
||||
}
|
||||
if notification.path != nil {
|
||||
content.userInfo["path"] = notification.path
|
||||
}
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: trigger)
|
||||
|
|
@ -69,4 +73,5 @@ struct Notification {
|
|||
var subtitle: String
|
||||
var content: String
|
||||
var target: String?
|
||||
var path: String?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -660,23 +660,37 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
// Connected Device Metrics
|
||||
// ------------------------
|
||||
// Low Battery notification
|
||||
if telemetry.batteryLevel > 0 && telemetry.batteryLevel < 5 {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = "Critically Low Battery!"
|
||||
content.body = "Time to charge your radio, there is \(telemetry.batteryLevel)% battery remaining."
|
||||
content.userInfo["target"] = "node"
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let uuidString = UUID().uuidString
|
||||
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
notificationCenter.add(request) { (error) in
|
||||
if error != nil {
|
||||
// Handle any errors.
|
||||
print("Error creating local low battery notification: \(error?.localizedDescription ?? "no description")")
|
||||
} else {
|
||||
print("Created local low battery notification.")
|
||||
}
|
||||
}
|
||||
if telemetry.batteryLevel > 0 && telemetry.batteryLevel < 4 {
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(UUID().uuidString)"),
|
||||
title: "Critically Low Battery!",
|
||||
subtitle: "AKA \(telemetry.nodeTelemetry?.user?.shortName ?? "UNK")",
|
||||
content: "Time to charge your radio, there is \(telemetry.batteryLevel)% battery remaining.",
|
||||
target: "nodes",
|
||||
path: "meshtastic://nodes/\(telemetry.nodeTelemetry?.num ?? 0)/devicetelemetrylog"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
||||
// let content = UNMutableNotificationContent()
|
||||
// content.title = "Critically Low Battery!"
|
||||
// content.body = "Time to charge your radio, there is \(telemetry.batteryLevel)% battery remaining."
|
||||
// content.userInfo["target"] = "node"
|
||||
// content.userInfo["path"] = "meshtastic://node/\(telemetry.nodeTelemetry?.num ?? 0)"
|
||||
// let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
// let uuidString = UUID().uuidString
|
||||
// let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
|
||||
// let notificationCenter = UNUserNotificationCenter.current()
|
||||
// notificationCenter.add(request) { (error) in
|
||||
// if error != nil {
|
||||
// // Handle any errors.
|
||||
// print("Error creating local low battery notification: \(error?.localizedDescription ?? "no description")")
|
||||
// } else {
|
||||
// print("Created local low battery notification.")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// Update our live activity if there is one running, not available on mac iOS >= 16.2
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
|
|
@ -781,7 +795,8 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod
|
|||
title: "\(newMessage.fromUser?.longName ?? "unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText,
|
||||
target: "message"
|
||||
target: "message",
|
||||
path: "meshtastic://open-dm?userid=\(newMessage.fromUser?.num ?? 0)&id=\(newMessage.messageId)"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
|
@ -812,7 +827,8 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod
|
|||
title: "\(newMessage.fromUser?.longName ?? "unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText,
|
||||
target: "message")
|
||||
target: "message",
|
||||
path: "meshtastic://messages/channel/\(newMessage.messageId)")
|
||||
]
|
||||
manager.schedule()
|
||||
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
|
||||
|
|
@ -878,7 +894,8 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
title: "New Waypoint Received",
|
||||
subtitle: "\(icon) \(waypoint.name ?? "Dropped Pin")",
|
||||
content: "\(waypoint.longDescription ?? "\(latitude), \(longitude)")",
|
||||
target: "map"
|
||||
target: "map",
|
||||
path: "meshtastic://open-waypoint?id=\(waypoint.id)"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
|
|
|||
|
|
@ -31,8 +31,27 @@
|
|||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLIconFile</key>
|
||||
<string>alpha</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>org.meshtastic</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>meshtastic</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>INIntentsSupported</key>
|
||||
<array>
|
||||
<string>Intent</string>
|
||||
</array>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
|
|
@ -48,17 +67,17 @@
|
|||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>Bluetooth is used to connect an iPhone to a user's meshtastic device to allow text messaging and location data for the mesh network.</string>
|
||||
<string>Bluetooth is used to connect an iPhone to a user's meshtastic device to allow text messaging and location data for the mesh network.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We use the camera to share channels using a QR Code</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. Route Recording uses location in the background.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
|
||||
<key>NSLocationUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. Route Recording uses location in the background.</string>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<key>Privacy – Bluetooth Always Usage Description</key>
|
||||
|
|
@ -66,7 +85,7 @@
|
|||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
|
|
|
|||
|
|
@ -18,5 +18,7 @@
|
|||
<true/>
|
||||
<key>com.apple.security.personal-information.location</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.carplay-communication</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV 23.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV 24.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,410 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22522" systemVersion="23D56" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="green" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ledState" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="red" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="ambientLightingConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<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"/>
|
||||
<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="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
|
||||
<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="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="downlinkEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<fetchedProperty name="allPrivateMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="channel == $FETCH_SOURCE.index && toUser == nil AND isEmoji == false"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="detectionTriggeredHigh" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="disableTripleClick" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="isManaged" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" 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="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
|
||||
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hwModel" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" 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="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="oledType" 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"/>
|
||||
<attribute name="units" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wakeOnTapOrMotion" optional="YES" attributeType="Boolean" defaultValueString="NO" 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="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="useI2SAsBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="fetchedProperty" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="ExternalNotificationConfigEntity"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="routeLocation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RouteEntity" inverseName="locations" inverseEntity="RouteEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ignoreMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" 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="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="messagePayloadMarkdown" optional="YES" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" optional="YES" 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="proxyToClientEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="root" optional="YES" attributeType="String" defaultValueString="msh"/>
|
||||
<attribute name="tlsEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<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="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="toUser == nil"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ntpServer" optional="YES" attributeType="String"/>
|
||||
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="detection" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="environment" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
|
||||
<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="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
|
||||
<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="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
|
||||
<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="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
|
||||
<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="rtttlConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RTTTLConfigEntity" inverseName="rtttlConfigNode" inverseEntity="RTTTLConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="storeForwardConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreForwardConfigEntity" inverseName="storeForwardConfigNode" inverseEntity="StoreForwardConfigEntity"/>
|
||||
<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="traceRoutes" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteEntity" inverseName="node" inverseEntity="TraceRouteEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="broadcastSmartMinimumDistance" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="broadcastSmartMinimumIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="gpsEnGpio" 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="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" 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="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latest" optional="YES" attributeType="Boolean" defaultValueString="NO" 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="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" 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="RouteEntity" representedClassName="RouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="color" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="notes" optional="YES" attributeType="String"/>
|
||||
<relationship name="locations" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="LocationEntity" inverseName="routeLocation" inverseEntity="LocationEntity"/>
|
||||
</entity>
|
||||
<entity name="RTTTLConfigEntity" representedClassName="RTTTLConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ringtone" optional="YES" attributeType="String" maxValueString="228" defaultValueString=""/>
|
||||
<relationship name="rtttlConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rtttlConfig" 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="overrideConsoleSerialPort" optional="YES" attributeType="Boolean" defaultValueString="NO" 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="StoreForwardConfigEntity" representedClassName="StoreForwardConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" 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="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" 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="TraceRouteEntity" representedClassName="TraceRouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 64" 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="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="route" optional="YES" attributeType="Transformable" customClassName="[UInt32]"/>
|
||||
<attribute name="routeText" optional="YES" attributeType="String"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteHopEntity" inverseName="traceRoute" inverseEntity="TraceRouteHopEntity"/>
|
||||
<relationship name="node" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="traceRoutes" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteHopEntity" representedClassName="TraceRouteHopEntity" 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="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="traceRoute" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TraceRouteEntity" inverseName="hops" inverseEntity="TraceRouteEntity"/>
|
||||
</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="lastMessage" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<attribute name="vip" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<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="(fromUser.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 toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10 "/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="detectionSensorMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND portNum = 10"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -153,4 +153,5 @@ class AppState: ObservableObject {
|
|||
@Published var unreadChannelMessages: Int = 0
|
||||
@Published var firmwareVersion: String = "0.0.0"
|
||||
@Published var connectedNode: NodeInfoEntity?
|
||||
@Published var navigationPath: String?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ class MeshtasticAppDelegate: NSObject, UIApplicationDelegate, UNUserNotification
|
|||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
let userInfo = response.notification.request.content.userInfo
|
||||
let targetValue = userInfo["target"] as? String
|
||||
AppState.shared.navigationPath = userInfo["path"] as? String
|
||||
print("\(AppState.shared.navigationPath ?? "EMPTY")")
|
||||
if targetValue == "map" {
|
||||
AppState.shared.tabSelection = Tab.map
|
||||
} else if targetValue == "message" {
|
||||
|
|
|
|||
|
|
@ -33,24 +33,27 @@ struct ChannelSet {
|
|||
|
||||
///
|
||||
/// Channel list with settings
|
||||
var settings: [ChannelSettings] = []
|
||||
var settings: [ChannelSettings] {
|
||||
get {return _storage._settings}
|
||||
set {_uniqueStorage()._settings = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// LoRa config
|
||||
var loraConfig: Config.LoRaConfig {
|
||||
get {return _loraConfig ?? Config.LoRaConfig()}
|
||||
set {_loraConfig = newValue}
|
||||
get {return _storage._loraConfig ?? Config.LoRaConfig()}
|
||||
set {_uniqueStorage()._loraConfig = newValue}
|
||||
}
|
||||
/// Returns true if `loraConfig` has been explicitly set.
|
||||
var hasLoraConfig: Bool {return self._loraConfig != nil}
|
||||
var hasLoraConfig: Bool {return _storage._loraConfig != nil}
|
||||
/// Clears the value of `loraConfig`. Subsequent reads from it will return its default value.
|
||||
mutating func clearLoraConfig() {self._loraConfig = nil}
|
||||
mutating func clearLoraConfig() {_uniqueStorage()._loraConfig = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _loraConfig: Config.LoRaConfig? = nil
|
||||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
|
|
@ -68,36 +71,70 @@ extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
2: .standard(proto: "lora_config"),
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _settings: [ChannelSettings] = []
|
||||
var _loraConfig: Config.LoRaConfig? = nil
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
||||
private init() {}
|
||||
|
||||
init(copying source: _StorageClass) {
|
||||
_settings = source._settings
|
||||
_loraConfig = source._loraConfig
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate mutating func _uniqueStorage() -> _StorageClass {
|
||||
if !isKnownUniquelyReferenced(&_storage) {
|
||||
_storage = _StorageClass(copying: _storage)
|
||||
}
|
||||
return _storage
|
||||
}
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeRepeatedMessageField(value: &self.settings) }()
|
||||
case 2: try { try decoder.decodeSingularMessageField(value: &self._loraConfig) }()
|
||||
default: break
|
||||
_ = _uniqueStorage()
|
||||
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeRepeatedMessageField(value: &_storage._settings) }()
|
||||
case 2: try { try decoder.decodeSingularMessageField(value: &_storage._loraConfig) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
if !self.settings.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: self.settings, fieldNumber: 1)
|
||||
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
if !_storage._settings.isEmpty {
|
||||
try visitor.visitRepeatedMessageField(value: _storage._settings, fieldNumber: 1)
|
||||
}
|
||||
try { if let v = _storage._loraConfig {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
}
|
||||
try { if let v = self._loraConfig {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: ChannelSet, rhs: ChannelSet) -> Bool {
|
||||
if lhs.settings != rhs.settings {return false}
|
||||
if lhs._loraConfig != rhs._loraConfig {return false}
|
||||
if lhs._storage !== rhs._storage {
|
||||
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
|
||||
let _storage = _args.0
|
||||
let rhs_storage = _args.1
|
||||
if _storage._settings != rhs_storage._settings {return false}
|
||||
if _storage._loraConfig != rhs_storage._loraConfig {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1044,6 +1044,10 @@ struct Config {
|
|||
/// in ignore_incoming will have packets they send dropped on receive (by router.cpp)
|
||||
var ignoreIncoming: [UInt32] = []
|
||||
|
||||
///
|
||||
/// If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it.
|
||||
var ignoreMqtt: Bool = false
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
enum RegionCode: SwiftProtobuf.Enum {
|
||||
|
|
@ -2220,6 +2224,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
13: .standard(proto: "sx126x_rx_boosted_gain"),
|
||||
14: .standard(proto: "override_frequency"),
|
||||
103: .standard(proto: "ignore_incoming"),
|
||||
104: .standard(proto: "ignore_mqtt"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2243,6 +2248,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
case 13: try { try decoder.decodeSingularBoolField(value: &self.sx126XRxBoostedGain) }()
|
||||
case 14: try { try decoder.decodeSingularFloatField(value: &self.overrideFrequency) }()
|
||||
case 103: try { try decoder.decodeRepeatedUInt32Field(value: &self.ignoreIncoming) }()
|
||||
case 104: try { try decoder.decodeSingularBoolField(value: &self.ignoreMqtt) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2294,6 +2300,9 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if !self.ignoreIncoming.isEmpty {
|
||||
try visitor.visitPackedUInt32Field(value: self.ignoreIncoming, fieldNumber: 103)
|
||||
}
|
||||
if self.ignoreMqtt != false {
|
||||
try visitor.visitSingularBoolField(value: self.ignoreMqtt, fieldNumber: 104)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2313,6 +2322,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if lhs.sx126XRxBoostedGain != rhs.sx126XRxBoostedGain {return false}
|
||||
if lhs.overrideFrequency != rhs.overrideFrequency {return false}
|
||||
if lhs.ignoreIncoming != rhs.ignoreIncoming {return false}
|
||||
if lhs.ignoreMqtt != rhs.ignoreMqtt {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,16 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// EBYTE SPI LoRa module and ESP32-S3
|
||||
case ebyteEsp32S3 // = 54
|
||||
|
||||
///
|
||||
/// Waveshare ESP32-S3-PICO with PICO LoRa HAT and 2.9inch e-Ink
|
||||
case esp32S3Pico // = 55
|
||||
|
||||
///
|
||||
/// CircuitMess Chatter 2 LLCC68 Lora Module and ESP32 Wroom
|
||||
/// Lora module can be swapped out for a Heltec RA-62 which is "almost" pin compatible
|
||||
/// with one cut and one jumper Meshtastic works
|
||||
case chatter2 // = 56
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
|
|
@ -280,6 +290,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 52: self = .picomputerS3
|
||||
case 53: self = .heltecHt62
|
||||
case 54: self = .ebyteEsp32S3
|
||||
case 55: self = .esp32S3Pico
|
||||
case 56: self = .chatter2
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -334,6 +346,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .picomputerS3: return 52
|
||||
case .heltecHt62: return 53
|
||||
case .ebyteEsp32S3: return 54
|
||||
case .esp32S3Pico: return 55
|
||||
case .chatter2: return 56
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -393,6 +407,8 @@ extension HardwareModel: CaseIterable {
|
|||
.picomputerS3,
|
||||
.heltecHt62,
|
||||
.ebyteEsp32S3,
|
||||
.esp32S3Pico,
|
||||
.chatter2,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -1498,6 +1514,13 @@ struct MeshPacket {
|
|||
set {_uniqueStorage()._delayed = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// Describes whether this packet passed via MQTT somewhere along the path it currently took.
|
||||
var viaMqtt: Bool {
|
||||
get {return _storage._viaMqtt}
|
||||
set {_uniqueStorage()._viaMqtt = newValue}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
enum OneOf_PayloadVariant: Equatable {
|
||||
|
|
@ -2570,6 +2593,8 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
52: .same(proto: "PICOMPUTER_S3"),
|
||||
53: .same(proto: "HELTEC_HT62"),
|
||||
54: .same(proto: "EBYTE_ESP32_S3"),
|
||||
55: .same(proto: "ESP32_S3_PICO"),
|
||||
56: .same(proto: "CHATTER_2"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
@ -3285,6 +3310,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
11: .same(proto: "priority"),
|
||||
12: .standard(proto: "rx_rssi"),
|
||||
13: .same(proto: "delayed"),
|
||||
14: .standard(proto: "via_mqtt"),
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -3300,6 +3326,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
var _priority: MeshPacket.Priority = .unset
|
||||
var _rxRssi: Int32 = 0
|
||||
var _delayed: MeshPacket.Delayed = .noDelay
|
||||
var _viaMqtt: Bool = false
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
||||
|
|
@ -3318,6 +3345,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
_priority = source._priority
|
||||
_rxRssi = source._rxRssi
|
||||
_delayed = source._delayed
|
||||
_viaMqtt = source._viaMqtt
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3368,6 +3396,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
case 11: try { try decoder.decodeSingularEnumField(value: &_storage._priority) }()
|
||||
case 12: try { try decoder.decodeSingularInt32Field(value: &_storage._rxRssi) }()
|
||||
case 13: try { try decoder.decodeSingularEnumField(value: &_storage._delayed) }()
|
||||
case 14: try { try decoder.decodeSingularBoolField(value: &_storage._viaMqtt) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -3424,6 +3453,9 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
if _storage._delayed != .noDelay {
|
||||
try visitor.visitSingularEnumField(value: _storage._delayed, fieldNumber: 13)
|
||||
}
|
||||
if _storage._viaMqtt != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._viaMqtt, fieldNumber: 14)
|
||||
}
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
|
@ -3445,6 +3477,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
if _storage._priority != rhs_storage._priority {return false}
|
||||
if _storage._rxRssi != rhs_storage._rxRssi {return false}
|
||||
if _storage._delayed != rhs_storage._delayed {return false}
|
||||
if _storage._viaMqtt != rhs_storage._viaMqtt {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
|
|
|
|||
|
|
@ -190,6 +190,20 @@ struct ChannelMessageList: View {
|
|||
.fixedSize()
|
||||
.padding(.bottom, 1)
|
||||
}
|
||||
.onAppear {
|
||||
if !tapback.read {
|
||||
tapback.read = true
|
||||
do {
|
||||
try context.save()
|
||||
print("📖 Read message \(message.messageId) ")
|
||||
appState.unreadChannelMessages = myInfo.unreadMessages
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
context.refresh(myInfo, mergeChanges: true)
|
||||
} catch {
|
||||
print("Failed to read tapback \(tapback.messageId)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
|
|
@ -199,6 +213,7 @@ struct ChannelMessageList: View {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
if currentUser && message.receivedACK {
|
||||
// Ack Received
|
||||
|
|
|
|||
|
|
@ -167,6 +167,20 @@ struct UserMessageList: View {
|
|||
.fixedSize()
|
||||
.padding(.bottom, 1)
|
||||
}
|
||||
.onAppear {
|
||||
if !tapback.read {
|
||||
tapback.read = true
|
||||
do {
|
||||
try context.save()
|
||||
print("📖 Read tapback \(tapback.messageId) ")
|
||||
appState.unreadDirectMessages = user.unreadMessages
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
|
||||
} catch {
|
||||
print("Failed to read tapback \(tapback.messageId)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ struct DeviceMetricsLog: View {
|
|||
}
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
if UIScreen.main.bounds.size.width > 768 && (UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac) {
|
||||
// Add a table for mac and ipad
|
||||
// Table(Array(deviceMetrics),id: \.self) {
|
||||
Table(deviceMetrics) {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ struct NodeMapSwiftUI: View {
|
|||
if waypoints.count > 0 && showWaypoints {
|
||||
ForEach(Array(waypoints), id: \.id) { waypoint in
|
||||
Annotation(waypoint.name ?? "?", coordinate: waypoint.coordinate) {
|
||||
ZStack {
|
||||
LazyVStack {
|
||||
CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 35)
|
||||
.onTapGesture(coordinateSpace: .named("nodemap")) { location in
|
||||
selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint)
|
||||
|
|
@ -100,47 +100,49 @@ struct NodeMapSwiftUI: View {
|
|||
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(position.heading))
|
||||
Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) {
|
||||
ZStack {
|
||||
LazyVStack {
|
||||
if position.latest {
|
||||
Circle()
|
||||
.fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5)))
|
||||
.foregroundStyle(Color(nodeColor.lighter()).opacity(0.3))
|
||||
.frame(width: 50, height: 50)
|
||||
if pf.contains(.Heading) {
|
||||
Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon")
|
||||
.symbolEffect(.pulse.byLayer)
|
||||
.padding(5)
|
||||
.foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
.background(Color(nodeColor.darker()))
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
.onTapGesture {
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
.popover(item: $selectedPosition) { selection in
|
||||
PositionPopover(position: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
|
||||
} else {
|
||||
Image(systemName: "flipphone")
|
||||
.symbolEffect(.pulse.byLayer)
|
||||
.padding(5)
|
||||
.foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
.background(Color(UIColor(hex: UInt32(node.num)).darker()))
|
||||
.clipShape(Circle())
|
||||
.onTapGesture {
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
.popover(item: $selectedPosition) { selection in
|
||||
PositionPopover(position: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5)))
|
||||
.foregroundStyle(Color(nodeColor.lighter()).opacity(0.3))
|
||||
.frame(width: 50, height: 50)
|
||||
if pf.contains(.Heading) {
|
||||
Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "octagon")
|
||||
.symbolEffect(.pulse.byLayer)
|
||||
.padding(5)
|
||||
.foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
.background(Color(nodeColor.darker()))
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
.onTapGesture {
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
.popover(item: $selectedPosition) { selection in
|
||||
PositionPopover(position: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
|
||||
} else {
|
||||
Image(systemName: "flipphone")
|
||||
.symbolEffect(.pulse.byLayer)
|
||||
.padding(5)
|
||||
.foregroundStyle(Color(nodeColor).isLight() ? .black : .white)
|
||||
.background(Color(UIColor(hex: UInt32(node.num)).darker()))
|
||||
.clipShape(Circle())
|
||||
.onTapGesture {
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
.popover(item: $selectedPosition) { selection in
|
||||
PositionPopover(position: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if showNodeHistory {
|
||||
|
|
|
|||
|
|
@ -43,10 +43,9 @@ struct MeshMap: View {
|
|||
|
||||
var delay: Double = 0
|
||||
@State private var scale: CGFloat = 0.5
|
||||
|
||||
/// "time >= %@ && nodePosition != nil && latest == true"
|
||||
/// && time >= %@
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)],
|
||||
predicate: NSPredicate(format: "nodePosition != nil && latest == true", Calendar.current.date(byAdding: .day, value: -30, to: Date())! as NSDate), animation: .none)
|
||||
predicate: NSPredicate(format: "nodePosition != nil && latest == true", Calendar.current.date(byAdding: .day, value: -7, to: Date())! as NSDate), animation: .none)
|
||||
private var positions: FetchedResults<PositionEntity>
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
|
|
@ -68,19 +67,6 @@ struct MeshMap: View {
|
|||
ZStack {
|
||||
MapReader { reader in
|
||||
Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) {
|
||||
/// Waypoint Annotations
|
||||
if waypoints.count > 0 && showWaypoints {
|
||||
ForEach(Array(waypoints), id: \.id) { waypoint in
|
||||
Annotation(waypoint.name ?? "?", coordinate: waypoint.coordinate) {
|
||||
ZStack {
|
||||
CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 40)
|
||||
.onTapGesture(perform: { location in
|
||||
selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Convex Hull
|
||||
if showConvexHull {
|
||||
if lineCoords.count > 0 {
|
||||
|
|
@ -95,32 +81,34 @@ struct MeshMap: View {
|
|||
/// Node color from node.num
|
||||
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
|
||||
Annotation(position.nodePosition?.user?.longName ?? "?", coordinate: position.coordinate) {
|
||||
ZStack {
|
||||
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
|
||||
if position.nodePosition?.isOnline ?? false {
|
||||
Circle()
|
||||
.fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5)))
|
||||
.foregroundStyle(Color(nodeColor.lighter()).opacity(0.3))
|
||||
.scaleEffect(scale)
|
||||
.animation(
|
||||
Animation.easeInOut(duration: 0.6)
|
||||
.repeatForever().delay(delay), value: scale
|
||||
)
|
||||
.onAppear {
|
||||
self.scale = 1
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
if position.nodePosition?.hasDetectionSensorMetrics ?? false {
|
||||
Image(systemName: "sensor.fill")
|
||||
.symbolRenderingMode(.palette)
|
||||
.symbolEffect(.variableColor)
|
||||
.padding()
|
||||
.foregroundStyle(.white)
|
||||
.background(Color(nodeColor))
|
||||
.clipShape(Circle())
|
||||
} else {
|
||||
CircleText(text: position.nodePosition?.user?.shortName ?? "?", color: Color(nodeColor), circleSize: 40)
|
||||
LazyVStack {
|
||||
ZStack {
|
||||
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
|
||||
if position.nodePosition?.isOnline ?? false {
|
||||
Circle()
|
||||
.fill(Color(nodeColor.lighter()).opacity(0.4).shadow(.drop(color: Color(nodeColor).isLight() ? .black : .white, radius: 5)))
|
||||
.foregroundStyle(Color(nodeColor.lighter()).opacity(0.3))
|
||||
.scaleEffect(scale)
|
||||
.animation(
|
||||
Animation.easeInOut(duration: 0.6)
|
||||
.repeatForever().delay(delay), value: scale
|
||||
)
|
||||
.onAppear {
|
||||
self.scale = 1
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
if position.nodePosition?.hasDetectionSensorMetrics ?? false {
|
||||
Image(systemName: "sensor.fill")
|
||||
.symbolRenderingMode(.palette)
|
||||
.symbolEffect(.variableColor)
|
||||
.padding()
|
||||
.foregroundStyle(.white)
|
||||
.background(Color(nodeColor))
|
||||
.clipShape(Circle())
|
||||
} else {
|
||||
CircleText(text: position.nodePosition?.user?.shortName ?? "?", color: Color(nodeColor), circleSize: 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onTapGesture { location in
|
||||
|
|
@ -151,12 +139,12 @@ struct MeshMap: View {
|
|||
}
|
||||
}
|
||||
.annotationTitles(.automatic)
|
||||
let dashed = StrokeStyle(
|
||||
let solid = StrokeStyle(
|
||||
lineWidth: 3,
|
||||
lineCap: .round, lineJoin: .round, dash: [7, 10]
|
||||
lineCap: .round, lineJoin: .round
|
||||
)
|
||||
MapPolyline(coordinates: routeCoords)
|
||||
.stroke(Color(UIColor(hex: UInt32(route.color))), style: dashed)
|
||||
.stroke(Color(UIColor(hex: UInt32(route.color))), style: solid)
|
||||
|
||||
}
|
||||
/// Node Route Lines
|
||||
|
|
@ -179,31 +167,45 @@ struct MeshMap: View {
|
|||
/// Node History
|
||||
ForEach(Array(position.nodePosition!.positions!) as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
if showNodeHistory {
|
||||
if mappin.latest == false && mappin.nodePosition?.user?.vip ?? false {
|
||||
let pf = PositionFlags(rawValue: Int(mappin.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(mappin.heading))
|
||||
Annotation("", coordinate: mappin.coordinate) {
|
||||
ZStack {
|
||||
if pf.contains(.Heading) {
|
||||
Image(systemName: "location.north.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundStyle(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))).isLight() ? .black : .white)
|
||||
.background(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))))
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
.frame(width: 16, height: 16)
|
||||
|
||||
} else {
|
||||
Circle()
|
||||
.fill(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))))
|
||||
.strokeBorder(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))).isLight() ? .black : .white ,lineWidth: 2)
|
||||
.frame(width: 12, height: 12)
|
||||
if mappin.latest == false && mappin.nodePosition?.user?.vip ?? false {
|
||||
let pf = PositionFlags(rawValue: Int(mappin.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(mappin.heading))
|
||||
Annotation("", coordinate: mappin.coordinate) {
|
||||
LazyVStack {
|
||||
if pf.contains(.Heading) {
|
||||
Image(systemName: "location.north.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.foregroundStyle(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))).isLight() ? .black : .white)
|
||||
.background(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))))
|
||||
.clipShape(Circle())
|
||||
.rotationEffect(headingDegrees)
|
||||
.frame(width: 16, height: 16)
|
||||
|
||||
} else {
|
||||
Circle()
|
||||
.fill(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))))
|
||||
.strokeBorder(Color(UIColor(hex: UInt32(mappin.nodePosition?.num ?? 0))).isLight() ? .black : .white ,lineWidth: 2)
|
||||
.frame(width: 12, height: 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
.annotationTitles(.hidden)
|
||||
.annotationSubtitles(.hidden)
|
||||
}
|
||||
.annotationTitles(.hidden)
|
||||
.annotationSubtitles(.hidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waypoint Annotations
|
||||
if waypoints.count > 0 && showWaypoints {
|
||||
ForEach(Array(waypoints), id: \.id) { waypoint in
|
||||
Annotation(waypoint.name ?? "?", coordinate: waypoint.coordinate) {
|
||||
LazyVStack {
|
||||
CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 40)
|
||||
.onTapGesture(perform: { location in
|
||||
selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -253,6 +255,33 @@ struct MeshMap: View {
|
|||
.sheet(isPresented: $isEditingSettings) {
|
||||
MapSettingsForm(nodeHistory: $showNodeHistory, routeLines: $showRouteLines, convexHull: $showConvexHull, traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer)
|
||||
}
|
||||
.onChange(of: (appState.navigationPath)) { newPath in
|
||||
|
||||
if ((newPath?.hasPrefix("meshtastic://open-waypoint")) != nil) {
|
||||
guard let url = URL(string: appState.navigationPath ?? "NONE") else {
|
||||
print("Invalid URL")
|
||||
return
|
||||
}
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
|
||||
print("Invalid URL Components")
|
||||
return
|
||||
}
|
||||
guard let action = components.host, action == "open-waypoint" else {
|
||||
print("Unknown waypoint URL action")
|
||||
return
|
||||
}
|
||||
guard let waypointId = components.queryItems?.first(where: { $0.name == "id" })?.value else {
|
||||
print("Waypoint id not found")
|
||||
return
|
||||
}
|
||||
guard let waypoint = waypoints.first(where: { $0.id == Int64(waypointId) }) else {
|
||||
print("Waypoint not found")
|
||||
return
|
||||
}
|
||||
showWaypoints = true
|
||||
position = .camera(MapCamera(centerCoordinate: waypoint.coordinate, distance: 1000, heading: 0, pitch: 60))
|
||||
}
|
||||
}
|
||||
.onChange(of: (selectedMapLayer)) { newMapLayer in
|
||||
switch selectedMapLayer {
|
||||
case .standard:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ struct Channels: View {
|
|||
var node: NodeInfoEntity?
|
||||
|
||||
@State var hasChanges = false
|
||||
@State var hasValidKey = false
|
||||
@State private var isPresentingEditView = false
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State private var channelIndex: Int32 = 0
|
||||
|
|
@ -167,16 +168,34 @@ struct Channels: View {
|
|||
HStack(alignment: .top) {
|
||||
Text("Key")
|
||||
Spacer()
|
||||
Text(channelKey)
|
||||
.foregroundColor(Color.gray)
|
||||
.textSelection(.enabled)
|
||||
// TextField(
|
||||
// "",
|
||||
// text: $channelKey,
|
||||
// axis: .vertical
|
||||
// )
|
||||
// .foregroundColor(Color.gray)
|
||||
// .disabled(true)
|
||||
TextField(
|
||||
"Key",
|
||||
text: $channelKey
|
||||
)
|
||||
.padding(4)
|
||||
.disableAutocorrection(true)
|
||||
.keyboardType(.alphabet)
|
||||
.foregroundColor(Color.gray)
|
||||
.textSelection(.enabled)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 25.0)
|
||||
.stroke(
|
||||
hasValidKey ?
|
||||
Color.green :
|
||||
Color.red
|
||||
, lineWidth: 2.0)
|
||||
)
|
||||
.onChange(of: channelKey, perform: { _ in
|
||||
let tempKey = Data(base64Encoded: channelKey) ?? Data()
|
||||
if tempKey.count == channelKeySize || channelKeySize == -1{
|
||||
hasValidKey = true
|
||||
}
|
||||
else {
|
||||
hasValidKey = false
|
||||
}
|
||||
hasChanges = true
|
||||
})
|
||||
.disabled(channelKeySize <= 0)
|
||||
}
|
||||
Picker("Channel Role", selection: $channelRole) {
|
||||
if channelRole == 1 {
|
||||
|
|
@ -256,7 +275,7 @@ struct Channels: View {
|
|||
} label: {
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges || !hasValidKey)
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ struct DeviceConfig: View {
|
|||
}
|
||||
Section(header: Text("GPIO")) {
|
||||
Picker("Button GPIO", selection: $buttonGPIO) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -113,7 +113,7 @@ struct DeviceConfig: View {
|
|||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("Buzzer GPIO", selection: $buzzerGPIO) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ struct LoRaConfig: View {
|
|||
@State var codingRate = 0
|
||||
@State var rxBoostedGain = false
|
||||
@State var overrideFrequency: Float = 0.0
|
||||
@State var ignoreMqtt = false
|
||||
|
||||
let floatFormatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
|
|
@ -111,6 +112,11 @@ struct LoRaConfig: View {
|
|||
}
|
||||
}
|
||||
Section(header: Text("Advanced")) {
|
||||
|
||||
Toggle(isOn: $ignoreMqtt) {
|
||||
Label("Ignore MQTT", systemImage: "server.rack")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $txEnabled) {
|
||||
Label("Transmit Enabled", systemImage: "waveform.path")
|
||||
|
|
@ -227,6 +233,7 @@ struct LoRaConfig: View {
|
|||
lc.spreadFactor = UInt32(spreadFactor)
|
||||
lc.sx126XRxBoostedGain = rxBoostedGain
|
||||
lc.overrideFrequency = overrideFrequency
|
||||
lc.ignoreMqtt = ignoreMqtt
|
||||
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -319,6 +326,11 @@ struct LoRaConfig: View {
|
|||
if newTxEnabled != node!.loRaConfig!.txEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: ignoreMqtt) { newIgnoreMqtt in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newIgnoreMqtt != node!.loRaConfig!.ignoreMqtt { hasChanges = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
func setLoRaValues() {
|
||||
self.hopLimit = Int(node?.loRaConfig?.hopLimit ?? 3)
|
||||
|
|
@ -333,6 +345,7 @@ struct LoRaConfig: View {
|
|||
self.spreadFactor = Int(node?.loRaConfig?.spreadFactor ?? 0)
|
||||
self.rxBoostedGain = node?.loRaConfig?.sx126xRxBoostedGain ?? false
|
||||
self.overrideFrequency = node?.loRaConfig?.overrideFrequency ?? 0.0
|
||||
self.ignoreMqtt = node?.loRaConfig?.ignoreMqtt ?? false
|
||||
self.hasChanges = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ struct CannedMessagesConfig: View {
|
|||
.disabled(configPreset > 0)
|
||||
Section(header: Text("Inputs")) {
|
||||
Picker("Pin A", selection: $inputbrokerPinA) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -136,7 +136,7 @@ struct CannedMessagesConfig: View {
|
|||
Text("GPIO pin for rotary encoder A port.")
|
||||
.font(.caption)
|
||||
Picker("Pin B", selection: $inputbrokerPinB) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -148,7 +148,7 @@ struct CannedMessagesConfig: View {
|
|||
Text("GPIO pin for rotary encoder B port.")
|
||||
.font(.caption)
|
||||
Picker("Press Pin", selection: $inputbrokerPinPress) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ struct DetectionSensorConfig: View {
|
|||
.listRowSeparator(.visible)
|
||||
.offset(y: -10)
|
||||
Picker("GPIO Pin to monitor", selection: $monitorPin) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ struct ExternalNotificationConfig: View {
|
|||
Text("If enabled, the 'output' Pin will be pulled active high, disabled means active low.")
|
||||
.font(.caption)
|
||||
Picker("Output pin GPIO", selection: $output) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -147,7 +147,7 @@ struct ExternalNotificationConfig: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Picker("Output pin buzzer GPIO ", selection: $outputBuzzer) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -157,7 +157,7 @@ struct ExternalNotificationConfig: View {
|
|||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("Output pin vibra GPIO", selection: $outputVibra) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ struct MQTTConfig: View {
|
|||
@State var jsonEnabled = false
|
||||
@State var tlsEnabled = true
|
||||
@State var root = "msh"
|
||||
@State var mqttConnected: Bool = false
|
||||
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
|
@ -54,6 +57,7 @@ struct MQTTConfig: View {
|
|||
.foregroundColor(.orange)
|
||||
}
|
||||
Section(header: Text("options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
|
||||
Label("enabled", systemImage: "dot.radiowaves.right")
|
||||
|
|
@ -64,7 +68,13 @@ struct MQTTConfig: View {
|
|||
Label("mqtt.clientproxy", systemImage: "iphone.radiowaves.left.and.right")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("If both MQTT and the client proxy are enabled your mobile device will utalize an available network connection to connect to the specified MQTT server.")
|
||||
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.")
|
||||
.font(.caption2)
|
||||
|
||||
Toggle(isOn: $encryptionEnabled) {
|
||||
|
|
@ -242,7 +252,7 @@ struct MQTTConfig: View {
|
|||
.navigationTitle("mqtt.config")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyConnected: bleManager.mqttProxyConnected)
|
||||
})
|
||||
.onAppear {
|
||||
if self.bleManager.context == nil {
|
||||
|
|
@ -314,6 +324,17 @@ struct MQTTConfig: View {
|
|||
if newTlsEnabled != node!.mqttConfig!.tlsEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.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!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func setMqttValues() {
|
||||
self.enabled = (node?.mqttConfig?.enabled ?? false)
|
||||
|
|
@ -325,6 +346,7 @@ struct MQTTConfig: View {
|
|||
self.encryptionEnabled = (node?.mqttConfig?.encryptionEnabled ?? false)
|
||||
self.jsonEnabled = (node?.mqttConfig?.jsonEnabled ?? false)
|
||||
self.tlsEnabled = (node?.mqttConfig?.tlsEnabled ?? false)
|
||||
self.mqttConnected = bleManager.mqttProxyConnected
|
||||
self.hasChanges = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ struct RtttlConfig: View {
|
|||
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil {
|
||||
let adminMessageId = bleManager.saveRtttlConfig(ringtone: ringtone, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveRtttlConfig(ringtone: ringtone.trimmingCharacters(in: .whitespacesAndNewlines), fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ struct SerialConfig: View {
|
|||
Section(header: Text("GPIO")) {
|
||||
|
||||
Picker("Receive data (rxd) GPIO pin", selection: $rxd) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -110,7 +110,7 @@ struct SerialConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
Picker("Transmit data (txd) GPIO pin", selection: $txd) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -154,17 +154,6 @@ struct PositionConfig: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
if includeAltitude {
|
||||
Toggle(isOn: $includeAltitudeMsl) {
|
||||
Label("Altitude is Mean Sea Level", systemImage: "arrow.up.to.line.compact")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Toggle(isOn: $includeGeoidalSeparation) {
|
||||
Label("Altitude Geoidal Separation", systemImage: "globe.americas")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
|
||||
Toggle(isOn: $includeSatsinview) {
|
||||
Label("Number of satellites", systemImage: "skew")
|
||||
}
|
||||
|
|
@ -192,6 +181,17 @@ struct PositionConfig: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
Section(header: Text("Advanced Position Flags")) {
|
||||
|
||||
if includeAltitude {
|
||||
Toggle(isOn: $includeAltitudeMsl) {
|
||||
Label("Altitude is Mean Sea Level", systemImage: "arrow.up.to.line.compact")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Toggle(isOn: $includeGeoidalSeparation) {
|
||||
Label("Altitude Geoidal Separation", systemImage: "globe.americas")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
|
||||
Toggle(isOn: $includeDop) {
|
||||
Text("Dilution of precision (DOP) PDOP used by default")
|
||||
|
|
@ -213,7 +213,7 @@ struct PositionConfig: View {
|
|||
if deviceGpsEnabled {
|
||||
|
||||
Picker("GPS Receive GPIO", selection: $rxGpio) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -223,7 +223,7 @@ struct PositionConfig: View {
|
|||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("GPS Transmit GPIO", selection: $txGpio) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
@ -233,7 +233,7 @@ struct PositionConfig: View {
|
|||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Picker("GPS EN GPIO", selection: $gpsEnGpio) {
|
||||
ForEach(0..<48) {
|
||||
ForEach(0..<49) {
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -112,8 +112,7 @@ struct Firmware: View {
|
|||
|
||||
if bleManager.sendEnterDfuMode(fromUser: connectedNode!.user!, toUser: node!.user!) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
bleManager.automaticallyReconnect = false
|
||||
bleManager.disconnectPeripheral()
|
||||
bleManager.disconnectPeripheral(reconnect: false)
|
||||
}
|
||||
} else {
|
||||
print("Enter DFU Failed")
|
||||
|
|
|
|||
|
|
@ -257,6 +257,12 @@
|
|||
// .presentationDetents([.fraction(0.30), .fraction(0.65)])
|
||||
// .presentationDragIndicator(.hidden)
|
||||
// .interactiveDismissDisabled(false)
|
||||
// .onAppear {
|
||||
// UIApplication.shared.isIdleTimerDisabled = true
|
||||
// }
|
||||
// .onDisappear(perform: {
|
||||
// UIApplication.shared.isIdleTimerDisabled = false
|
||||
// })
|
||||
// .onChange(of: locationsHandler.locationsArray.last) { newLoc in
|
||||
// if locationsHandler.isRecording {
|
||||
// if let loc = newLoc {
|
||||
|
|
|
|||
|
|
@ -181,12 +181,12 @@ struct Routes: View {
|
|||
}
|
||||
}
|
||||
.annotationTitles(.automatic)
|
||||
let dashed = StrokeStyle(
|
||||
let solid = StrokeStyle(
|
||||
lineWidth: 3,
|
||||
lineCap: .round, lineJoin: .round, dash: [7, 10]
|
||||
lineCap: .round, lineJoin: .round
|
||||
)
|
||||
MapPolyline(coordinates: lineCoords)
|
||||
.stroke(Color(UIColor(hex: UInt32(selectedRoute?.color ?? 0))), style: dashed)
|
||||
.stroke(Color(UIColor(hex: UInt32(selectedRoute?.color ?? 0))), style: solid)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.safeAreaInset(edge: .bottom, alignment: UIDevice.current.userInterfaceIdiom == .phone ? .leading : .trailing) {
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ struct ShareChannels: View {
|
|||
loRaConfig.usePreset = node?.loRaConfig?.usePreset ?? true
|
||||
loRaConfig.channelNum = UInt32(node?.loRaConfig?.channelNum ?? 0)
|
||||
loRaConfig.sx126XRxBoostedGain = node?.loRaConfig?.sx126xRxBoostedGain ?? false
|
||||
loRaConfig.ignoreMqtt = node?.loRaConfig?.ignoreMqtt ?? false
|
||||
channelSet.loraConfig = loRaConfig
|
||||
if node?.myInfo?.channels != nil && node?.myInfo?.channels?.count ?? 0 > 0 {
|
||||
for ch in node?.myInfo?.channels?.array as? [ChannelEntity] ?? [] {
|
||||
|
|
|
|||
|
|
@ -198,8 +198,10 @@
|
|||
"mode"="Modus";
|
||||
"module.configuration"="Modul Konfiguration";
|
||||
"mqtt"="MQTT";
|
||||
"mqtt.connect"="Connect to MQTT";
|
||||
"mqtt.config"="MQTT Config";
|
||||
"mqtt.clientproxy"="MQTT Client Proxy";
|
||||
"mqtt.disconnect"="Disconnect from MQTT";
|
||||
"mqtt.username"="Benutzername";
|
||||
"name"="Name";
|
||||
"network"="Netzwerk";
|
||||
|
|
|
|||
|
|
@ -202,8 +202,10 @@
|
|||
"mode"="Mode";
|
||||
"module.configuration"="Module Configuration";
|
||||
"mqtt"="MQTT";
|
||||
"mqtt.connect"="Connect to MQTT";
|
||||
"mqtt.config"="MQTT Config";
|
||||
"mqtt.clientproxy"="MQTT Client Proxy";
|
||||
"mqtt.disconnect"="Disconnect from MQTT";
|
||||
"mqtt.username"="Username";
|
||||
"name"="Name";
|
||||
"network"="Network";
|
||||
|
|
|
|||
|
|
@ -200,8 +200,10 @@
|
|||
"mode"="Tryb";
|
||||
"module.configuration"="Konfiguracja modułu";
|
||||
"mqtt"="MQTT";
|
||||
"mqtt.connect"="Connect to MQTT";
|
||||
"mqtt.config"="Konfiguracja MQTT";
|
||||
"mqtt.clientproxy"="Klient Proxy MQTT";
|
||||
"mqtt.disconnect"="Disconnect from MQTT";
|
||||
"mqtt.username"="Nazwa użytkownika";
|
||||
"name"="Nazwa";
|
||||
"network"="Sieć";
|
||||
|
|
|
|||
|
|
@ -198,8 +198,10 @@
|
|||
"mode"="模式";
|
||||
"module.configuration"="模块配置";
|
||||
"mqtt"="MQTT";
|
||||
"mqtt.connect"="Connect to MQTT";
|
||||
"mqtt.config"="MQTT 配置";
|
||||
"mqtt.clientproxy"="MQTT 客户端代理";
|
||||
"mqtt.disconnect"="Disconnect from MQTT";
|
||||
"mqtt.username"="用户名称";
|
||||
"name"="名称";
|
||||
"network"="网络";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue