mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge branch 'center-meshmap' into powersjcb/nodes-map-deep-links
This commit is contained in:
commit
39702d45f2
29 changed files with 979 additions and 221 deletions
|
|
@ -21,21 +21,6 @@
|
|||
},
|
||||
": %d" : {
|
||||
|
||||
},
|
||||
".dot" : {
|
||||
|
||||
},
|
||||
".gauge" : {
|
||||
|
||||
},
|
||||
".gradient" : {
|
||||
|
||||
},
|
||||
".pill" : {
|
||||
|
||||
},
|
||||
".text" : {
|
||||
|
||||
},
|
||||
"(Re)define PIN_GPS_EN for your board." : {
|
||||
|
||||
|
|
@ -1429,8 +1414,15 @@
|
|||
"Bad" : {
|
||||
|
||||
},
|
||||
"Bad Packets: %d" : {
|
||||
|
||||
"Bad Packets: %d %@%%" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Bad Packets: %1$d %2$@%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bandwidth" : {
|
||||
|
||||
|
|
@ -5061,9 +5053,6 @@
|
|||
},
|
||||
"Debug" : {
|
||||
|
||||
},
|
||||
"Debug Log" : {
|
||||
|
||||
},
|
||||
"Debug Logs" : {
|
||||
|
||||
|
|
@ -15907,6 +15896,9 @@
|
|||
},
|
||||
"OK" : {
|
||||
|
||||
},
|
||||
"Ok to MQTT" : {
|
||||
|
||||
},
|
||||
"OLED Type" : {
|
||||
|
||||
|
|
@ -16079,11 +16071,15 @@
|
|||
"Override automatic OLED screen detection." : {
|
||||
|
||||
},
|
||||
"Packets Received: %d" : {
|
||||
|
||||
},
|
||||
"Packets Sent: %d" : {
|
||||
|
||||
"Packets: Sent: %d Received: %d" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Packets: Sent: %1$d Received: %2$d"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"password" : {
|
||||
"localizations" : {
|
||||
|
|
@ -19158,6 +19154,9 @@
|
|||
},
|
||||
"Send" : {
|
||||
|
||||
},
|
||||
"Send ${messageContent} to ${channelNumber}" : {
|
||||
|
||||
},
|
||||
"Send a Group Message" : {
|
||||
|
||||
|
|
@ -22314,6 +22313,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Uptime: %@" : {
|
||||
|
||||
},
|
||||
"Use a PWM output (like the RAK Buzzer) for tunes instead of an on/off output. This will ignore the output, output duration and active settings and use the device config buzzer GPIO option instead." : {
|
||||
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@
|
|||
DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelTips.swift; sourceTree = "<group>"; };
|
||||
DD77093E2AA1B146007A8BF0 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = "<group>"; };
|
||||
DD798B062915928D005217CD /* ChannelMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelMessageList.swift; sourceTree = "<group>"; };
|
||||
DD7CF8DA2C93663C008BD10E /* MeshtasticDataModelV 44.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 44.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD7E235F2C7AA3E50078ACDF /* MeshtasticDataModelV 43.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 43.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
|
||||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -1686,7 +1687,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.5.3;
|
||||
MARKETING_VERSION = 2.5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1721,7 +1722,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.5.3;
|
||||
MARKETING_VERSION = 2.5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1753,7 +1754,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.5.3;
|
||||
MARKETING_VERSION = 2.5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1786,7 +1787,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.5.3;
|
||||
MARKETING_VERSION = 2.5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1898,6 +1899,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD7CF8DA2C93663C008BD10E /* MeshtasticDataModelV 44.xcdatamodel */,
|
||||
DD7E235F2C7AA3E50078ACDF /* MeshtasticDataModelV 43.xcdatamodel */,
|
||||
DD1BD0F12C61D3AD008C0C70 /* MeshtasticDataModelV 42.xcdatamodel */,
|
||||
DD2984A82C5AEF7500B1268D /* MeshtasticDataModelV 41.xcdatamodel */,
|
||||
|
|
@ -1942,7 +1944,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD7E235F2C7AA3E50078ACDF /* MeshtasticDataModelV 43.xcdatamodel */;
|
||||
currentVersion = DD7CF8DA2C93663C008BD10E /* MeshtasticDataModelV 44.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "d57a5aecf24a25b32ec4a74be2f5d0a995a47c4b",
|
||||
"version" : "1.27.0"
|
||||
"revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5",
|
||||
"version" : "1.28.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -5,6 +5,30 @@
|
|||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "logo-dark.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"filename" : "logo-tinted.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
|
|||
BIN
Meshtastic/Assets.xcassets/AppIcon.appiconset/logo-dark.png
Normal file
BIN
Meshtastic/Assets.xcassets/AppIcon.appiconset/logo-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
Meshtastic/Assets.xcassets/AppIcon.appiconset/logo-tinted.png
Normal file
BIN
Meshtastic/Assets.xcassets/AppIcon.appiconset/logo-tinted.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -3302,7 +3302,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
// MARK: - CB Central Manager implmentation
|
||||
extension BLEManager: CBCentralManagerDelegate {
|
||||
|
||||
|
||||
// MARK: Bluetooth enabled/disabled
|
||||
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||
if central.state == CBManagerState.poweredOn {
|
||||
|
|
@ -3312,9 +3312,9 @@ extension BLEManager: CBCentralManagerDelegate {
|
|||
} else {
|
||||
isSwitchedOn = false
|
||||
}
|
||||
|
||||
|
||||
var status = ""
|
||||
|
||||
|
||||
switch central.state {
|
||||
case .poweredOff:
|
||||
status = "BLE is powered off"
|
||||
|
|
@ -3333,10 +3333,10 @@ extension BLEManager: CBCentralManagerDelegate {
|
|||
}
|
||||
Logger.services.info("📜 [BLE] Bluetooth status: \(status)")
|
||||
}
|
||||
|
||||
|
||||
// Called each time a peripheral is discovered
|
||||
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
|
||||
|
||||
|
||||
if self.automaticallyReconnect && peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
|
||||
self.connectTo(peripheral: peripheral)
|
||||
Logger.services.info("✅ [BLE] Reconnecting to prefered peripheral: \(peripheral.name ?? "Unknown", privacy: .public)")
|
||||
|
|
@ -3344,7 +3344,7 @@ extension BLEManager: CBCentralManagerDelegate {
|
|||
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
|
||||
let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "?", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral)
|
||||
let index = peripherals.map { $0.peripheral }.firstIndex(of: peripheral)
|
||||
|
||||
|
||||
if let peripheralIndex = index {
|
||||
peripherals[peripheralIndex] = device
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@ import OSLog
|
|||
class LocalNotificationManager {
|
||||
|
||||
var notifications = [Notification]()
|
||||
|
||||
let thumbsUpAction = UNNotificationAction(identifier: "messageNotification.thumbsUpAction", title:
|
||||
"👍 \(Tapbacks.thumbsUp.description)", options: [])
|
||||
let thumbsDownAction = UNNotificationAction(identifier: "messageNotification.thumbsDownAction", title:
|
||||
"👎 \(Tapbacks.thumbsDown.description)", options: [])
|
||||
let replyInputAction = UNTextInputNotificationAction(
|
||||
identifier: "messageNotification.replyInputAction",
|
||||
title: "reply".localized,
|
||||
options: [])
|
||||
|
||||
|
||||
// Step 1 Request Permissions for notifications
|
||||
private func requestAuthorization() {
|
||||
|
|
@ -31,6 +41,15 @@ class LocalNotificationManager {
|
|||
|
||||
// This function iterates over the Notification objects in the notifications array and schedules them for delivery in the future
|
||||
private func scheduleNotifications() {
|
||||
let messageNotificationCategory = UNNotificationCategory(
|
||||
identifier: "messageNotificationCategory",
|
||||
actions: [thumbsUpAction, thumbsDownAction,replyInputAction],
|
||||
intentIdentifiers: [],
|
||||
options: .customDismissAction
|
||||
)
|
||||
|
||||
UNUserNotificationCenter.current().setNotificationCategories([messageNotificationCategory])
|
||||
|
||||
for notification in notifications {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.subtitle = notification.subtitle
|
||||
|
|
@ -45,6 +64,17 @@ class LocalNotificationManager {
|
|||
if notification.path != nil {
|
||||
content.userInfo["path"] = notification.path
|
||||
}
|
||||
if notification.messageId != nil {
|
||||
content.categoryIdentifier = "messageNotificationCategory"
|
||||
content.userInfo["messageId"] = notification.messageId
|
||||
}
|
||||
if notification.channel != nil {
|
||||
content.userInfo["channel"] = notification.channel
|
||||
}
|
||||
if notification.userNum != nil {
|
||||
content.userInfo["userNum"] = notification.userNum
|
||||
}
|
||||
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: trigger)
|
||||
|
|
@ -76,4 +106,7 @@ struct Notification {
|
|||
var content: String
|
||||
var target: String?
|
||||
var path: String?
|
||||
var messageId: Int64?
|
||||
var channel: Int32?
|
||||
var userNum: Int64?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -816,12 +816,10 @@ func textMessageAppPacket(
|
|||
context: NSManagedObjectContext,
|
||||
appState: AppState
|
||||
) {
|
||||
|
||||
var messageText = String(bytes: packet.decoded.payload, encoding: .utf8)
|
||||
let rangeRef = Reference(Int.self)
|
||||
let rangeTestRegex = Regex {
|
||||
"seq "
|
||||
|
||||
TryCapture(as: rangeRef) {
|
||||
OneOrMore(.digit)
|
||||
} transform: { match in
|
||||
|
|
@ -829,7 +827,7 @@ func textMessageAppPacket(
|
|||
}
|
||||
}
|
||||
let rangeTest = messageText?.contains(rangeTestRegex) ?? false && messageText?.starts(with: "seq ") ?? false
|
||||
|
||||
|
||||
if !wantRangeTestPackets && rangeTest {
|
||||
return
|
||||
}
|
||||
|
|
@ -842,15 +840,16 @@ func textMessageAppPacket(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if messageText?.count ?? 0 > 0 {
|
||||
|
||||
MeshLogger.log("💬 \("mesh.log.textmessage.received".localized)")
|
||||
|
||||
|
||||
let messageUsers = UserEntity.fetchRequest()
|
||||
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
|
||||
|
||||
do {
|
||||
let fetchedUsers = try context.fetch(messageUsers)
|
||||
|
||||
let newMessage = MessageEntity(context: context)
|
||||
newMessage.messageId = Int64(packet.id)
|
||||
if packet.rxTime > 0 {
|
||||
|
|
@ -864,56 +863,66 @@ func textMessageAppPacket(
|
|||
newMessage.isEmoji = packet.decoded.emoji == 1
|
||||
newMessage.channel = Int32(packet.channel)
|
||||
newMessage.portNum = Int32(packet.decoded.portnum.rawValue)
|
||||
newMessage.publicKey = packet.publicKey
|
||||
newMessage.pkiEncrypted = packet.pkiEncrypted
|
||||
if newMessage.toUser?.pkiEncrypted ?? false {
|
||||
newMessage.pkiEncrypted = true
|
||||
newMessage.publicKey = packet.publicKey
|
||||
}
|
||||
if packet.decoded.portnum == PortNum.detectionSensorApp {
|
||||
if !UserDefaults.enableDetectionNotifications {
|
||||
newMessage.read = true
|
||||
}
|
||||
}
|
||||
|
||||
if packet.decoded.replyID > 0 {
|
||||
newMessage.replyID = Int64(packet.decoded.replyID)
|
||||
}
|
||||
|
||||
|
||||
if fetchedUsers.first(where: { $0.num == packet.to }) != nil && packet.to != Constants.maximumNodeNum {
|
||||
if !storeForwardBroadcast {
|
||||
newMessage.toUser = fetchedUsers.first(where: { $0.num == packet.to })
|
||||
}
|
||||
}
|
||||
|
||||
if fetchedUsers.first(where: { $0.num == packet.from }) != nil {
|
||||
newMessage.fromUser = fetchedUsers.first(where: { $0.num == packet.from })
|
||||
if !(newMessage.fromUser?.publicKey?.isEmpty ?? true) {
|
||||
/// We have a key, check if it matches
|
||||
|
||||
if !(newMessage.fromUser?.publicKey?.isEmpty ?? true) && newMessage.toUser != nil && packet.pkiEncrypted {
|
||||
// We have a key and it is a PKC encrypted DM, check if it matches
|
||||
if newMessage.fromUser?.publicKey != newMessage.publicKey {
|
||||
newMessage.fromUser?.keyMatch = false
|
||||
newMessage.fromUser?.newPublicKey = newMessage.publicKey
|
||||
Logger.data.error("🔑 Key Mismatch origninal key: \(newMessage.fromUser?.publicKey?.base64EncodedString() ?? "No Key") new key: \(newMessage.fromUser?.newPublicKey?.base64EncodedString() ?? "No Key") ")
|
||||
}
|
||||
} else {
|
||||
/// We have no key, set it
|
||||
newMessage.fromUser?.publicKey = packet.publicKey
|
||||
newMessage.fromUser?.pkiEncrypted = packet.pkiEncrypted
|
||||
/// We have no key, set it if it is not empty
|
||||
if !packet.publicKey.isEmpty {
|
||||
newMessage.fromUser?.pkiEncrypted = true
|
||||
newMessage.fromUser?.publicKey = packet.publicKey
|
||||
}
|
||||
}
|
||||
|
||||
if packet.rxTime > 0 {
|
||||
newMessage.fromUser?.userNode?.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
} else {
|
||||
newMessage.fromUser?.userNode?.lastHeard = Date()
|
||||
}
|
||||
}
|
||||
|
||||
newMessage.messagePayload = messageText
|
||||
newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: messageText!)
|
||||
|
||||
if packet.to != Constants.maximumNodeNum && newMessage.fromUser != nil {
|
||||
newMessage.fromUser?.lastMessage = Date()
|
||||
}
|
||||
|
||||
var messageSaved = false
|
||||
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
Logger.data.info("💾 Saved a new message for \(newMessage.messageId)")
|
||||
messageSaved = true
|
||||
|
||||
if messageSaved {
|
||||
|
||||
if packet.decoded.portnum == PortNum.detectionSensorApp && !UserDefaults.enableDetectionNotifications {
|
||||
return
|
||||
}
|
||||
|
|
@ -932,14 +941,16 @@ func textMessageAppPacket(
|
|||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.messageId)"
|
||||
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(packet.from)
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
|
||||
}
|
||||
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
|
||||
|
||||
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode))
|
||||
|
||||
|
|
@ -962,7 +973,11 @@ func textMessageAppPacket(
|
|||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)")
|
||||
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(newMessage.fromUser?.userId ?? "0")
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
|
||||
|
|
@ -970,7 +985,7 @@ func textMessageAppPacket(
|
|||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
// Handle error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -985,6 +1000,7 @@ func textMessageAppPacket(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat("mesh.log.waypoint.received %@".localized, String(packet.from))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV 43.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV 44.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,476 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22758" systemVersion="23G5075b" 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="deviceLoggingEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<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" attributeType="Boolean" defaultValueString="NO" 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="positionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<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="ledHeartbeatEnabled" optional="YES" attributeType="Boolean" defaultValueString="YES" 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"/>
|
||||
<attribute name="tzdef" optional="YES" attributeType="String"/>
|
||||
<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"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<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"/>
|
||||
</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="okToMqtt" attributeType="Boolean" defaultValueString="NO" 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="pkiEncrypted" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<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="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"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String"/>
|
||||
<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="mapPositionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="13" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPublishIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mapReportingEnabled" 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"/>
|
||||
<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="favorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="firstHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hopsAway" optional="YES" attributeType="Integer 32" defaultValueString="0" 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="sessionExpiration" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="sessionPasskey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="viaMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Cascade" 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="pax" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PaxCounterEntity" inverseName="paxNode" inverseEntity="PaxCounterEntity"/>
|
||||
<relationship name="paxCounterConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PaxCounterConfigEntity" inverseName="paxCounterConfigNode" inverseEntity="PaxCounterConfigEntity"/>
|
||||
<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="powerConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PowerConfigEntity" inverseName="powerConfigNode" inverseEntity="PowerConfigEntity"/>
|
||||
<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="securityConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecurityConfigEntity" inverseName="securityConfigNode" inverseEntity="SecurityConfigEntity"/>
|
||||
<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="PaxCounterConfigEntity" representedClassName="PaxCounterConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleThreshold" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiThreshold" optional="YES" attributeType="Integer 32" defaultValueString="-80" usesScalarValueType="YES"/>
|
||||
<relationship name="paxCounterConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="paxCounterConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PaxCounterEntity" representedClassName="PaxCounterEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ble" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="paxNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="pax" inverseEntity="NodeInfoEntity"/>
|
||||
</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="gpsMode" 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="precisionBits" optional="YES" attributeType="Integer 32" defaultValueString="32" 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="PowerConfigEntity" representedClassName="PowerConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adcMultiplierOverride" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceBatteryInaAddress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isPowerSaving" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="lsSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minWakeSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="onBatteryShutdownAfterSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="waitBluetoothSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="powerConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="powerConfig" 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="distance" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="elevationGain" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="endDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<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="SecurityConfigEntity" representedClassName="SecurityConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adminChannelEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="bluetoothLoggingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogApiEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="isManaged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="privateKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="serialEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="securityConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="securityConfig" 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="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" 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"/>
|
||||
<attribute name="powerMeasurementEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="powerScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="powerUpdateInterval" 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="iaq" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numOnlineNodes" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numPacketsRx" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numPacketsRxBad" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numPacketsTx" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTotalNodes" 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="uptimeSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="weight" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windDirection" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="windGust" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windLull" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windSpeed" 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="hwDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="hwModelId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="keyMatch" attributeType="Boolean" defaultValueString="YES" 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="newPublicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numString" optional="YES" attributeType="String"/>
|
||||
<attribute name="pkiEncrypted" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
</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"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="id"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -9,9 +9,9 @@ import SwiftUI
|
|||
import OSLog
|
||||
|
||||
class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
|
||||
|
||||
var router: Router?
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||
Logger.services.info("🚀 [App] Meshtstic Apple App launched!")
|
||||
// Default User Default Values
|
||||
|
|
@ -22,7 +22,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
|
|||
if #available(iOS 17.0, macOS 14.0, *) {
|
||||
let locationsHandler = LocationsHandler.shared
|
||||
locationsHandler.startLocationUpdates()
|
||||
|
||||
|
||||
// If a background activity session was previously active, reinstantiate it after the background launch.
|
||||
if locationsHandler.backgroundActivity {
|
||||
locationsHandler.backgroundActivity = true
|
||||
|
|
@ -38,7 +38,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
|
|||
) {
|
||||
completionHandler([.list, .banner, .sound])
|
||||
}
|
||||
|
||||
|
||||
// This method is called when a user clicks on the notification
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
|
|
@ -46,6 +46,64 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
|
|||
withCompletionHandler completionHandler: @escaping () -> Void
|
||||
) {
|
||||
let userInfo = response.notification.request.content.userInfo
|
||||
|
||||
switch response.actionIdentifier {
|
||||
case UNNotificationDefaultActionIdentifier:
|
||||
break
|
||||
|
||||
case "messageNotification.thumbsUpAction":
|
||||
if let channel = userInfo["channel"] as? Int32,
|
||||
let replyID = userInfo["messageId"] as? Int64 {
|
||||
let tapbackResponse = !BLEManager.shared.sendMessage(
|
||||
message: Tapbacks.thumbsUp.emojiString,
|
||||
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
|
||||
channel: channel,
|
||||
isEmoji: true,
|
||||
replyID: replyID
|
||||
)
|
||||
Logger.services.info("Tapback response sent")
|
||||
} else {
|
||||
Logger.services.error("Failed to retrieve channel or messageId from userInfo")
|
||||
}
|
||||
break
|
||||
|
||||
case "messageNotification.thumbsDownAction":
|
||||
if let channel = userInfo["channel"] as? Int32,
|
||||
let replyID = userInfo["messageId"] as? Int64 {
|
||||
let tapbackResponse = !BLEManager.shared.sendMessage(
|
||||
message: Tapbacks.thumbsDown.emojiString,
|
||||
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
|
||||
channel: channel,
|
||||
isEmoji: true,
|
||||
replyID: replyID
|
||||
)
|
||||
Logger.services.info("Tapback response sent")
|
||||
} else {
|
||||
Logger.services.error("Failed to retrieve channel or messageId from userInfo")
|
||||
}
|
||||
break
|
||||
|
||||
case "messageNotification.replyInputAction":
|
||||
if let userInput = (response as? UNTextInputNotificationResponse)?.userText,
|
||||
let channel = userInfo["channel"] as? Int32,
|
||||
let replyID = userInfo["messageId"] as? Int64 {
|
||||
let tapbackResponse = !BLEManager.shared.sendMessage(
|
||||
message: userInput,
|
||||
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
|
||||
channel: channel,
|
||||
isEmoji: false,
|
||||
replyID: replyID
|
||||
)
|
||||
Logger.services.info("Actionable notification reply sent")
|
||||
} else {
|
||||
Logger.services.error("Failed to retrieve user input, channel, or messageId from userInfo")
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let targetValue = userInfo["target"] as? String,
|
||||
let deepLink = userInfo["path"] as? String,
|
||||
let url = URL(string: deepLink) {
|
||||
|
|
@ -54,7 +112,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
|
|||
} else {
|
||||
Logger.services.error("Failed to handle notification response: \(userInfo)")
|
||||
}
|
||||
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,8 +181,11 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
newUser.role = Int32(newUserMessage.role.rawValue)
|
||||
newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased()
|
||||
newUser.hwModelId = Int32(newUserMessage.hwModel.rawValue)
|
||||
newUser.pkiEncrypted = packet.pkiEncrypted
|
||||
newUser.publicKey = packet.publicKey
|
||||
if !newUserMessage.publicKey.isEmpty {
|
||||
newUser.pkiEncrypted = true
|
||||
newUser.publicKey = newUserMessage.publicKey
|
||||
}
|
||||
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == newUser.hwModelId })
|
||||
|
|
@ -209,8 +212,10 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
} else {
|
||||
if packet.from > Constants.minimumNodeNum {
|
||||
let newUser = createUser(num: Int64(packet.from), context: context)
|
||||
newNode.user?.pkiEncrypted = packet.pkiEncrypted
|
||||
newNode.user?.publicKey = packet.publicKey
|
||||
if !packet.publicKey.isEmpty {
|
||||
newNode.user?.pkiEncrypted = true
|
||||
newNode.user?.publicKey = packet.publicKey
|
||||
}
|
||||
newNode.user = newUser
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +229,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
myInfoEntity.rebootCount = 0
|
||||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 [NodeInfo] Saved a NodeInfo for node number: \(packet.from.toHex(), privacy: .public)")
|
||||
Logger.data.info("💾 [MyInfoEntity] Saved a new myInfo for node number: \(packet.from.toHex(), privacy: .public)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
|
|
@ -271,10 +277,9 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
fetchedNode[0].user!.role = Int32(nodeInfoMessage.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfoMessage.user.hwModel.rawValue)
|
||||
|
||||
if !packet.publicKey.isEmpty {
|
||||
fetchedNode[0].user!.pkiEncrypted = packet.pkiEncrypted
|
||||
fetchedNode[0].user!.publicKey = packet.publicKey
|
||||
if !nodeInfoMessage.user.publicKey.isEmpty {
|
||||
fetchedNode[0].user!.pkiEncrypted = true
|
||||
fetchedNode[0].user!.publicKey = nodeInfoMessage.user.publicKey
|
||||
}
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
|
|
@ -458,7 +463,6 @@ func upsertDeviceConfigPacket(config: Config.DeviceConfig, nodeNum: Int64, sessi
|
|||
let newDeviceConfig = DeviceConfigEntity(context: context)
|
||||
newDeviceConfig.role = Int32(config.role.rawValue)
|
||||
newDeviceConfig.serialEnabled = config.serialEnabled
|
||||
newDeviceConfig.debugLogEnabled = config.debugLogEnabled
|
||||
newDeviceConfig.buttonGpio = Int32(config.buttonGpio)
|
||||
newDeviceConfig.buzzerGpio = Int32(config.buzzerGpio)
|
||||
newDeviceConfig.rebroadcastMode = Int32(config.rebroadcastMode.rawValue)
|
||||
|
|
@ -471,7 +475,6 @@ func upsertDeviceConfigPacket(config: Config.DeviceConfig, nodeNum: Int64, sessi
|
|||
} else {
|
||||
fetchedNode[0].deviceConfig?.role = Int32(config.role.rawValue)
|
||||
fetchedNode[0].deviceConfig?.serialEnabled = config.serialEnabled
|
||||
fetchedNode[0].deviceConfig?.debugLogEnabled = config.debugLogEnabled
|
||||
fetchedNode[0].deviceConfig?.buttonGpio = Int32(config.buttonGpio)
|
||||
fetchedNode[0].deviceConfig?.buzzerGpio = Int32(config.buzzerGpio)
|
||||
fetchedNode[0].deviceConfig?.rebroadcastMode = Int32(config.rebroadcastMode.rawValue)
|
||||
|
|
@ -595,6 +598,7 @@ func upsertLoRaConfigPacket(config: Config.LoRaConfig, nodeNum: Int64, sessionPa
|
|||
newLoRaConfig.channelNum = Int32(config.channelNum)
|
||||
newLoRaConfig.sx126xRxBoostedGain = config.sx126XRxBoostedGain
|
||||
newLoRaConfig.ignoreMqtt = config.ignoreMqtt
|
||||
newLoRaConfig.okToMqtt = config.configOkToMqtt
|
||||
fetchedNode[0].loRaConfig = newLoRaConfig
|
||||
} else {
|
||||
fetchedNode[0].loRaConfig?.regionCode = Int32(config.region.rawValue)
|
||||
|
|
@ -612,6 +616,7 @@ func upsertLoRaConfigPacket(config: Config.LoRaConfig, nodeNum: Int64, sessionPa
|
|||
fetchedNode[0].loRaConfig?.channelNum = Int32(config.channelNum)
|
||||
fetchedNode[0].loRaConfig?.sx126xRxBoostedGain = config.sx126XRxBoostedGain
|
||||
fetchedNode[0].loRaConfig?.ignoreMqtt = config.ignoreMqtt
|
||||
fetchedNode[0].loRaConfig?.okToMqtt = config.configOkToMqtt
|
||||
fetchedNode[0].loRaConfig?.sx126xRxBoostedGain = config.sx126XRxBoostedGain
|
||||
}
|
||||
if sessionPasskey != nil {
|
||||
|
|
@ -813,21 +818,23 @@ func upsertSecurityConfigPacket(config: Config.SecurityConfig, nodeNum: Int64, s
|
|||
let newSecurityConfig = SecurityConfigEntity(context: context)
|
||||
newSecurityConfig.publicKey = config.publicKey
|
||||
newSecurityConfig.privateKey = config.privateKey
|
||||
newSecurityConfig.adminKey = config.adminKey
|
||||
if config.adminKey.count > 0 {
|
||||
newSecurityConfig.adminKey = config.adminKey[0]
|
||||
}
|
||||
newSecurityConfig.isManaged = config.isManaged
|
||||
newSecurityConfig.serialEnabled = config.serialEnabled
|
||||
newSecurityConfig.debugLogApiEnabled = config.debugLogApiEnabled
|
||||
newSecurityConfig.bluetoothLoggingEnabled = config.bluetoothLoggingEnabled
|
||||
newSecurityConfig.adminChannelEnabled = config.adminChannelEnabled
|
||||
fetchedNode[0].securityConfig = newSecurityConfig
|
||||
} else {
|
||||
fetchedNode[0].securityConfig?.publicKey = config.publicKey
|
||||
fetchedNode[0].securityConfig?.privateKey = config.privateKey
|
||||
fetchedNode[0].securityConfig?.adminKey = config.adminKey
|
||||
if config.adminKey.count > 0 {
|
||||
fetchedNode[0].securityConfig?.adminKey = config.adminKey[0]
|
||||
}
|
||||
fetchedNode[0].securityConfig?.isManaged = config.isManaged
|
||||
fetchedNode[0].securityConfig?.serialEnabled = config.serialEnabled
|
||||
fetchedNode[0].securityConfig?.debugLogApiEnabled = config.debugLogApiEnabled
|
||||
fetchedNode[0].securityConfig?.bluetoothLoggingEnabled = config.bluetoothLoggingEnabled
|
||||
fetchedNode[0].securityConfig?.adminChannelEnabled = config.adminChannelEnabled
|
||||
}
|
||||
if sessionPasskey?.count != 0 {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ struct Connect: View {
|
|||
}
|
||||
VStack {
|
||||
let localStats = node?.telemetries?.filtered(using: NSPredicate(format: "metricsType == 6")).lastObject as? TelemetryEntity
|
||||
|
||||
if localStats != nil {
|
||||
Divider()
|
||||
if localStats?.numTotalNodes ?? 0 >= 100 {
|
||||
|
|
@ -111,25 +112,29 @@ struct Connect: View {
|
|||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Packets Sent: \(localStats?.numPacketsTx ?? 0)")
|
||||
Text("Packets: Sent: \(localStats?.numPacketsTx ?? 0) Received: \(localStats?.numPacketsRx ?? 0)")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Packets Received: \(localStats?.numPacketsRx ?? 0)")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Bad Packets: \(localStats?.numPacketsRxBad ?? 0)")
|
||||
let errorRate = (Double(localStats?.numPacketsRxBad ?? -1) / Double(localStats?.numPacketsRx ?? -1)) * 100
|
||||
Text("Bad Packets: \(localStats?.numPacketsRxBad ?? 0) \(String(format: "Error Rate: %.2f", errorRate))%")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize()
|
||||
let now = Date.now
|
||||
let later = now + TimeInterval(Double(localStats?.numPacketsRxBad ?? 0))
|
||||
let uptime = (now..<later).formatted(.components(style: .narrow))
|
||||
Text("Uptime: \(uptime)")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.gray)
|
||||
.padding([.top, .bottom])
|
||||
.padding([.top])
|
||||
.swipeActions {
|
||||
Button(role: .destructive) {
|
||||
if let connectedPeripheral = bleManager.connectedPeripheral,
|
||||
|
|
|
|||
|
|
@ -72,6 +72,20 @@ private func getColor(signalStrength: LoRaSignalStrength) -> Color {
|
|||
}
|
||||
|
||||
func getLoRaSignalStrength(snr: Float, rssi: Int32, preset: ModemPresets) -> LoRaSignalStrength {
|
||||
// rssi is 0 when not available
|
||||
if rssi == 0 {
|
||||
if snr > (preset.snrLimit()) {
|
||||
return .good
|
||||
}
|
||||
if snr < (preset.snrLimit() - 7.5) {
|
||||
return .none
|
||||
}
|
||||
if snr <= (preset.snrLimit() - 5.5) {
|
||||
return .bad
|
||||
}
|
||||
return .fair
|
||||
}
|
||||
|
||||
if rssi > -115 && snr > (preset.snrLimit()) {
|
||||
return .good
|
||||
} else if rssi < -126 && snr < (preset.snrLimit() - 7.5) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ struct UserList: View {
|
|||
var boolFilters: [Bool] {[
|
||||
isFavorite,
|
||||
isOnline,
|
||||
isPkiEncrypted,
|
||||
isEnvironment,
|
||||
distanceFilter,
|
||||
roleFilter
|
||||
|
|
@ -45,11 +44,12 @@ struct UserList: View {
|
|||
sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false),
|
||||
NSSortDescriptor(key: "userNode.favorite", ascending: false),
|
||||
NSSortDescriptor(key: "pkiEncrypted", ascending: false),
|
||||
NSSortDescriptor(key: "userNode.lastHeard", ascending: false),
|
||||
NSSortDescriptor(key: "longName", ascending: true)],
|
||||
predicate: NSPredicate(format: "hwModelId != nil"),
|
||||
predicate: NSPredicate(format: "longName != ''"),
|
||||
animation: .default
|
||||
)
|
||||
private var users: FetchedResults<UserEntity>
|
||||
var users: FetchedResults<UserEntity>
|
||||
|
||||
@Binding var node: NodeInfoEntity?
|
||||
@Binding var userSelection: UserEntity?
|
||||
|
|
@ -202,34 +202,55 @@ struct UserList: View {
|
|||
DirectMessagesHelp()
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: viaLora) { _ in
|
||||
if !viaLora && !viaMqtt {
|
||||
viaMqtt = true
|
||||
}
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: viaMqtt) { _ in
|
||||
if !viaLora && !viaMqtt {
|
||||
viaLora = true
|
||||
}
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: [deviceRoles]) { _ in
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: hopsAway) { _ in
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: [boolFilters]) { _ in
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: maxDistance) { _ in
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onChange(of: isPkiEncrypted) { _ in
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
searchUserList()
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .bottom, alignment: .leading) {
|
||||
HStack {
|
||||
|
|
@ -267,8 +288,7 @@ struct UserList: View {
|
|||
.scrollDismissesKeyboard(.immediately)
|
||||
}
|
||||
}
|
||||
|
||||
private func searchUserList() {
|
||||
private func searchUserList() async {
|
||||
|
||||
/// Case Insensitive Search Text Predicates
|
||||
let searchPredicates = ["userId", "numString", "hwModel", "hwDisplayName", "longName", "shortName"].map { property in
|
||||
|
|
|
|||
|
|
@ -130,7 +130,9 @@ struct NodeMapSwiftUI: View {
|
|||
if node.positions?.count ?? 0 > 1 {
|
||||
position = .automatic
|
||||
} else {
|
||||
position = .camera(MapCamera(centerCoordinate: mostRecent!.coordinate, distance: 8000, heading: 0, pitch: 60))
|
||||
if let mrCoord = mostRecent?.coordinate {
|
||||
position = .camera(MapCamera(centerCoordinate: mrCoord, distance: 8000, heading: 0, pitch: 60))
|
||||
}
|
||||
}
|
||||
if self.scene == nil {
|
||||
Task {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ struct Channels: View {
|
|||
preciseLocation = true
|
||||
positionsEnabled = true
|
||||
if channelKey == "AQ==" {
|
||||
positionPrecision = 13
|
||||
positionPrecision = 14
|
||||
preciseLocation = false
|
||||
}
|
||||
} else if !supportedVersion && channelRole == 2 {
|
||||
|
|
@ -103,8 +103,8 @@ struct Channels: View {
|
|||
} else {
|
||||
if channelKey == "AQ==" {
|
||||
preciseLocation = false
|
||||
if (positionPrecision > 0 && positionPrecision < 10) || positionPrecision > 16 {
|
||||
positionPrecision = 13
|
||||
if (positionPrecision > 0 && positionPrecision < 11) || positionPrecision > 14 {
|
||||
positionPrecision = 14
|
||||
}
|
||||
} else if positionPrecision == 32 {
|
||||
preciseLocation = true
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ struct ChannelForm: View {
|
|||
.listRowSeparator(.visible)
|
||||
.onChange(of: preciseLocation) { pl in
|
||||
if pl == false {
|
||||
positionPrecision = 13
|
||||
positionPrecision = 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ struct ChannelForm: View {
|
|||
VStack(alignment: .leading) {
|
||||
Label("Approximate Location", systemImage: "location.slash.circle.fill")
|
||||
|
||||
Slider(value: $positionPrecision, in: 10...16, step: 1) {
|
||||
Slider(value: $positionPrecision, in: 11...14, step: 1) {
|
||||
} minimumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
} maximumValueLabel: {
|
||||
|
|
@ -220,7 +220,7 @@ struct ChannelForm: View {
|
|||
}
|
||||
positionPrecision = 32
|
||||
} else {
|
||||
positionPrecision = 13
|
||||
positionPrecision = 14
|
||||
}
|
||||
hasChanges = true
|
||||
}
|
||||
|
|
@ -230,7 +230,7 @@ struct ChannelForm: View {
|
|||
.onChange(of: positionsEnabled) { pe in
|
||||
if pe {
|
||||
if positionPrecision == 0 {
|
||||
positionPrecision = 13
|
||||
positionPrecision = 14
|
||||
}
|
||||
} else {
|
||||
positionPrecision = 0
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ struct DeviceConfig: View {
|
|||
@State var buzzerGPIO = 0
|
||||
@State var buttonGPIO = 0
|
||||
@State var serialEnabled = true
|
||||
@State var debugLogEnabled = false
|
||||
@State var rebroadcastMode = 0
|
||||
@State var nodeInfoBroadcastSecs = 10800
|
||||
@State var doubleTapAsButtonPress = false
|
||||
|
|
@ -89,10 +88,6 @@ struct DeviceConfig: View {
|
|||
Label("Serial Console", systemImage: "terminal")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Toggle(isOn: $debugLogEnabled) {
|
||||
Label("Debug Log", systemImage: "ant.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Label("Time Zone", systemImage: "clock.badge.exclamationmark")
|
||||
|
|
@ -200,7 +195,6 @@ struct DeviceConfig: View {
|
|||
var dc = Config.DeviceConfig()
|
||||
dc.role = DeviceRoles(rawValue: deviceRole)!.protoEnumValue()
|
||||
dc.serialEnabled = serialEnabled
|
||||
dc.debugLogEnabled = debugLogEnabled
|
||||
dc.buttonGpio = UInt32(buttonGPIO)
|
||||
dc.buzzerGpio = UInt32(buzzerGPIO)
|
||||
dc.rebroadcastMode = RebroadcastModes(rawValue: rebroadcastMode)?.protoEnumValue() ?? RebroadcastModes.all.protoEnumValue()
|
||||
|
|
@ -259,9 +253,6 @@ struct DeviceConfig: View {
|
|||
.onChange(of: serialEnabled) {
|
||||
if $0 != node?.deviceConfig?.serialEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: debugLogEnabled) {
|
||||
if $0 != node?.deviceConfig?.debugLogEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: buttonGPIO) { newButtonGPIO in
|
||||
if newButtonGPIO != node?.deviceConfig?.buttonGpio ?? -1 { hasChanges = true }
|
||||
}
|
||||
|
|
@ -289,7 +280,6 @@ struct DeviceConfig: View {
|
|||
func setDeviceValues() {
|
||||
self.deviceRole = Int(node?.deviceConfig?.role ?? 0)
|
||||
self.serialEnabled = (node?.deviceConfig?.serialEnabled ?? true)
|
||||
self.debugLogEnabled = node?.deviceConfig?.debugLogEnabled ?? false
|
||||
self.buttonGPIO = Int(node?.deviceConfig?.buttonGpio ?? 0)
|
||||
self.buzzerGPIO = Int(node?.deviceConfig?.buzzerGpio ?? 0)
|
||||
self.rebroadcastMode = Int(node?.deviceConfig?.rebroadcastMode ?? 0)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ struct LoRaConfig: View {
|
|||
@State var rxBoostedGain = false
|
||||
@State var overrideFrequency: Float = 0.0
|
||||
@State var ignoreMqtt = false
|
||||
@State var okToMqtt = false
|
||||
|
||||
let floatFormatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
|
|
@ -100,6 +101,10 @@ struct LoRaConfig: View {
|
|||
Label("Ignore MQTT", systemImage: "server.rack")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Toggle(isOn: $okToMqtt) {
|
||||
Label("Ok to MQTT", systemImage: "network")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $txEnabled) {
|
||||
Label("Transmit Enabled", systemImage: "waveform.path")
|
||||
|
|
@ -209,6 +214,7 @@ struct LoRaConfig: View {
|
|||
lc.sx126XRxBoostedGain = rxBoostedGain
|
||||
lc.overrideFrequency = overrideFrequency
|
||||
lc.ignoreMqtt = ignoreMqtt
|
||||
lc.configOkToMqtt = okToMqtt
|
||||
if connectedNode?.num ?? -1 == node?.user?.num ?? 0 {
|
||||
UserDefaults.modemPreset = modemPreset
|
||||
}
|
||||
|
|
@ -292,6 +298,9 @@ struct LoRaConfig: View {
|
|||
.onChange(of: ignoreMqtt) {
|
||||
if $0 != node?.loRaConfig?.ignoreMqtt { hasChanges = true }
|
||||
}
|
||||
.onChange(of: okToMqtt) {
|
||||
if $0 != node?.loRaConfig?.okToMqtt { hasChanges = true }
|
||||
}
|
||||
}
|
||||
func setLoRaValues() {
|
||||
self.hopLimit = Int(node?.loRaConfig?.hopLimit ?? 3)
|
||||
|
|
@ -307,6 +316,7 @@ struct LoRaConfig: View {
|
|||
self.rxBoostedGain = node?.loRaConfig?.sx126xRxBoostedGain ?? false
|
||||
self.overrideFrequency = node?.loRaConfig?.overrideFrequency ?? 0.0
|
||||
self.ignoreMqtt = node?.loRaConfig?.ignoreMqtt ?? false
|
||||
self.okToMqtt = node?.loRaConfig?.okToMqtt ?? false
|
||||
self.hasChanges = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ struct MQTTConfig: View {
|
|||
@State var nearbyTopics = [String]()
|
||||
@State var mapReportingEnabled = false
|
||||
@State var mapPublishIntervalSecs = 3600
|
||||
@State var preciseLocation: Bool = false
|
||||
@State var mapPositionPrecision: Double = 13.0
|
||||
@State var mapPositionPrecision: Double = 14.0
|
||||
|
||||
let locale = Locale.current
|
||||
|
||||
|
|
@ -105,35 +104,17 @@ struct MQTTConfig: View {
|
|||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Toggle(isOn: $preciseLocation) {
|
||||
Label("Precise Location", systemImage: "scope")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.listRowSeparator(.visible)
|
||||
.onChange(of: preciseLocation) { pl in
|
||||
if pl == false {
|
||||
mapPositionPrecision = 12
|
||||
} else {
|
||||
mapPositionPrecision = 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !preciseLocation {
|
||||
VStack(alignment: .leading) {
|
||||
Label("Approximate Location", systemImage: "location.slash.circle.fill")
|
||||
Slider(value: $mapPositionPrecision, in: 11...16, step: 1) {
|
||||
} minimumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
} maximumValueLabel: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
Text(PositionPrecision(rawValue: Int(mapPositionPrecision))?.description ?? "")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
Label("Approximate Location", systemImage: "location.slash.circle.fill")
|
||||
Slider(value: $mapPositionPrecision, in: 11...14, step: 1) {
|
||||
} minimumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
} maximumValueLabel: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
Text(PositionPrecision(rawValue: Int(mapPositionPrecision))?.description ?? "")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -429,11 +410,12 @@ struct MQTTConfig: View {
|
|||
self.mqttConnected = bleManager.mqttProxyConnected
|
||||
self.mapReportingEnabled = node?.mqttConfig?.mapReportingEnabled ?? false
|
||||
self.mapPublishIntervalSecs = Int(node?.mqttConfig?.mapPublishIntervalSecs ?? 3600)
|
||||
self.mapPositionPrecision = Double(node?.mqttConfig?.mapPositionPrecision ?? 12)
|
||||
if mapPositionPrecision == 0.0 {
|
||||
self.mapPositionPrecision = 12
|
||||
self.mapPositionPrecision = Double(node?.mqttConfig?.mapPositionPrecision ?? 14)
|
||||
if mapPositionPrecision < 11 || mapPositionPrecision > 14 {
|
||||
self.mapPositionPrecision = 14
|
||||
self.hasChanges = true
|
||||
} else {
|
||||
self.hasChanges = false
|
||||
}
|
||||
self.preciseLocation = mapPositionPrecision == 32
|
||||
self.hasChanges = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ struct SecurityConfig: View {
|
|||
var config = Config.SecurityConfig()
|
||||
config.publicKey = Data(base64Encoded: publicKey) ?? Data()
|
||||
config.privateKey = Data(base64Encoded: privateKey) ?? Data()
|
||||
config.adminKey = Data(base64Encoded: adminKey) ?? Data()
|
||||
config.adminKey = [Data(base64Encoded: adminKey) ?? Data()]
|
||||
config.isManaged = isManaged
|
||||
config.serialEnabled = serialEnabled
|
||||
config.debugLogApiEnabled = debugLogApiEnabled
|
||||
|
|
|
|||
|
|
@ -844,6 +844,7 @@ public struct AdminMessage {
|
|||
///
|
||||
/// TODO: REPLACE
|
||||
case securityConfig // = 7
|
||||
case sessionkeyConfig // = 8
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
|
|
@ -860,6 +861,7 @@ public struct AdminMessage {
|
|||
case 5: self = .loraConfig
|
||||
case 6: self = .bluetoothConfig
|
||||
case 7: self = .securityConfig
|
||||
case 8: self = .sessionkeyConfig
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -874,6 +876,7 @@ public struct AdminMessage {
|
|||
case .loraConfig: return 5
|
||||
case .bluetoothConfig: return 6
|
||||
case .securityConfig: return 7
|
||||
case .sessionkeyConfig: return 8
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -998,6 +1001,7 @@ extension AdminMessage.ConfigType: CaseIterable {
|
|||
.loraConfig,
|
||||
.bluetoothConfig,
|
||||
.securityConfig,
|
||||
.sessionkeyConfig,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1755,6 +1759,7 @@ extension AdminMessage.ConfigType: SwiftProtobuf._ProtoNameProviding {
|
|||
5: .same(proto: "LORA_CONFIG"),
|
||||
6: .same(proto: "BLUETOOTH_CONFIG"),
|
||||
7: .same(proto: "SECURITY_CONFIG"),
|
||||
8: .same(proto: "SESSIONKEY_CONFIG"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,14 @@ public struct Config {
|
|||
set {payloadVariant = .security(newValue)}
|
||||
}
|
||||
|
||||
public var sessionkey: Config.SessionkeyConfig {
|
||||
get {
|
||||
if case .sessionkey(let v)? = payloadVariant {return v}
|
||||
return Config.SessionkeyConfig()
|
||||
}
|
||||
set {payloadVariant = .sessionkey(newValue)}
|
||||
}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -106,6 +114,7 @@ public struct Config {
|
|||
case lora(Config.LoRaConfig)
|
||||
case bluetooth(Config.BluetoothConfig)
|
||||
case security(Config.SecurityConfig)
|
||||
case sessionkey(Config.SessionkeyConfig)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool {
|
||||
|
|
@ -145,6 +154,10 @@ public struct Config {
|
|||
guard case .security(let l) = lhs, case .security(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.sessionkey, .sessionkey): return {
|
||||
guard case .sessionkey(let l) = lhs, case .sessionkey(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
|
@ -167,12 +180,6 @@ public struct Config {
|
|||
/// Moved to SecurityConfig
|
||||
public var serialEnabled: Bool = false
|
||||
|
||||
///
|
||||
/// By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
|
||||
/// Set this to true to leave the debug log outputting even when API is active.
|
||||
/// Moved to SecurityConfig
|
||||
public var debugLogEnabled: Bool = false
|
||||
|
||||
///
|
||||
/// For boards without a hard wired button, this is the pin number that will be used
|
||||
/// Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined.
|
||||
|
|
@ -1250,6 +1257,13 @@ public struct Config {
|
|||
set {_uniqueStorage()._ignoreMqtt = newValue}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the ok_to_mqtt bit on outgoing packets
|
||||
public var configOkToMqtt: Bool {
|
||||
get {return _storage._configOkToMqtt}
|
||||
set {_uniqueStorage()._configOkToMqtt = newValue}
|
||||
}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum RegionCode: SwiftProtobuf.Enum {
|
||||
|
|
@ -1492,11 +1506,6 @@ public struct Config {
|
|||
/// Specified PIN for PairingMode.FixedPin
|
||||
public var fixedPin: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Enables device (serial style logs) over Bluetooth
|
||||
/// Moved to SecurityConfig
|
||||
public var deviceLoggingEnabled: Bool = false
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum PairingMode: SwiftProtobuf.Enum {
|
||||
|
|
@ -1559,7 +1568,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// The public key authorized to send admin messages to this node.
|
||||
public var adminKey: Data = Data()
|
||||
public var adminKey: [Data] = []
|
||||
|
||||
///
|
||||
/// If true, device is considered to be "managed" by a mesh administrator via admin messages
|
||||
|
|
@ -1572,13 +1581,9 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
|
||||
/// Output live debug logging over serial.
|
||||
/// Output live debug logging over serial or bluetooth is set to true.
|
||||
public var debugLogApiEnabled: Bool = false
|
||||
|
||||
///
|
||||
/// Enables device (serial style logs) over Bluetooth
|
||||
public var bluetoothLoggingEnabled: Bool = false
|
||||
|
||||
///
|
||||
/// Allow incoming device control over the insecure legacy admin channel.
|
||||
public var adminChannelEnabled: Bool = false
|
||||
|
|
@ -1588,6 +1593,18 @@ public struct Config {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// Blank config request, strictly for getting the session key
|
||||
public struct SessionkeyConfig {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
|
|
@ -1784,6 +1801,7 @@ extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {}
|
|||
extension Config.BluetoothConfig: @unchecked Sendable {}
|
||||
extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {}
|
||||
extension Config.SecurityConfig: @unchecked Sendable {}
|
||||
extension Config.SessionkeyConfig: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
|
@ -1801,6 +1819,7 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
|
|||
6: .same(proto: "lora"),
|
||||
7: .same(proto: "bluetooth"),
|
||||
8: .same(proto: "security"),
|
||||
9: .same(proto: "sessionkey"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1913,6 +1932,19 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
|
|||
self.payloadVariant = .security(v)
|
||||
}
|
||||
}()
|
||||
case 9: try {
|
||||
var v: Config.SessionkeyConfig?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .sessionkey(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .sessionkey(v)
|
||||
}
|
||||
}()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1956,6 +1988,10 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
|
|||
guard case .security(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 8)
|
||||
}()
|
||||
case .sessionkey?: try {
|
||||
guard case .sessionkey(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 9)
|
||||
}()
|
||||
case nil: break
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
|
|
@ -1973,7 +2009,6 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "role"),
|
||||
2: .standard(proto: "serial_enabled"),
|
||||
3: .standard(proto: "debug_log_enabled"),
|
||||
4: .standard(proto: "button_gpio"),
|
||||
5: .standard(proto: "buzzer_gpio"),
|
||||
6: .standard(proto: "rebroadcast_mode"),
|
||||
|
|
@ -1993,7 +2028,6 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularEnumField(value: &self.role) }()
|
||||
case 2: try { try decoder.decodeSingularBoolField(value: &self.serialEnabled) }()
|
||||
case 3: try { try decoder.decodeSingularBoolField(value: &self.debugLogEnabled) }()
|
||||
case 4: try { try decoder.decodeSingularUInt32Field(value: &self.buttonGpio) }()
|
||||
case 5: try { try decoder.decodeSingularUInt32Field(value: &self.buzzerGpio) }()
|
||||
case 6: try { try decoder.decodeSingularEnumField(value: &self.rebroadcastMode) }()
|
||||
|
|
@ -2015,9 +2049,6 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
if self.serialEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.serialEnabled, fieldNumber: 2)
|
||||
}
|
||||
if self.debugLogEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.debugLogEnabled, fieldNumber: 3)
|
||||
}
|
||||
if self.buttonGpio != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.buttonGpio, fieldNumber: 4)
|
||||
}
|
||||
|
|
@ -2051,7 +2082,6 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
public static func ==(lhs: Config.DeviceConfig, rhs: Config.DeviceConfig) -> Bool {
|
||||
if lhs.role != rhs.role {return false}
|
||||
if lhs.serialEnabled != rhs.serialEnabled {return false}
|
||||
if lhs.debugLogEnabled != rhs.debugLogEnabled {return false}
|
||||
if lhs.buttonGpio != rhs.buttonGpio {return false}
|
||||
if lhs.buzzerGpio != rhs.buzzerGpio {return false}
|
||||
if lhs.rebroadcastMode != rhs.rebroadcastMode {return false}
|
||||
|
|
@ -2595,6 +2625,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
15: .standard(proto: "pa_fan_disabled"),
|
||||
103: .standard(proto: "ignore_incoming"),
|
||||
104: .standard(proto: "ignore_mqtt"),
|
||||
105: .standard(proto: "config_ok_to_mqtt"),
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -2615,6 +2646,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
var _paFanDisabled: Bool = false
|
||||
var _ignoreIncoming: [UInt32] = []
|
||||
var _ignoreMqtt: Bool = false
|
||||
var _configOkToMqtt: Bool = false
|
||||
|
||||
#if swift(>=5.10)
|
||||
// This property is used as the initial default value for new instances of the type.
|
||||
|
|
@ -2646,6 +2678,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
_paFanDisabled = source._paFanDisabled
|
||||
_ignoreIncoming = source._ignoreIncoming
|
||||
_ignoreMqtt = source._ignoreMqtt
|
||||
_configOkToMqtt = source._configOkToMqtt
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2681,6 +2714,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
case 15: try { try decoder.decodeSingularBoolField(value: &_storage._paFanDisabled) }()
|
||||
case 103: try { try decoder.decodeRepeatedUInt32Field(value: &_storage._ignoreIncoming) }()
|
||||
case 104: try { try decoder.decodeSingularBoolField(value: &_storage._ignoreMqtt) }()
|
||||
case 105: try { try decoder.decodeSingularBoolField(value: &_storage._configOkToMqtt) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2740,6 +2774,9 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if _storage._ignoreMqtt != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._ignoreMqtt, fieldNumber: 104)
|
||||
}
|
||||
if _storage._configOkToMqtt != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._configOkToMqtt, fieldNumber: 105)
|
||||
}
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
|
@ -2766,6 +2803,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if _storage._paFanDisabled != rhs_storage._paFanDisabled {return false}
|
||||
if _storage._ignoreIncoming != rhs_storage._ignoreIncoming {return false}
|
||||
if _storage._ignoreMqtt != rhs_storage._ignoreMqtt {return false}
|
||||
if _storage._configOkToMqtt != rhs_storage._configOkToMqtt {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
|
|
@ -2819,7 +2857,6 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
1: .same(proto: "enabled"),
|
||||
2: .same(proto: "mode"),
|
||||
3: .standard(proto: "fixed_pin"),
|
||||
4: .standard(proto: "device_logging_enabled"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2831,7 +2868,6 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
|
||||
case 2: try { try decoder.decodeSingularEnumField(value: &self.mode) }()
|
||||
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.fixedPin) }()
|
||||
case 4: try { try decoder.decodeSingularBoolField(value: &self.deviceLoggingEnabled) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2847,9 +2883,6 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
if self.fixedPin != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.fixedPin, fieldNumber: 3)
|
||||
}
|
||||
if self.deviceLoggingEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.deviceLoggingEnabled, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2857,7 +2890,6 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
if lhs.enabled != rhs.enabled {return false}
|
||||
if lhs.mode != rhs.mode {return false}
|
||||
if lhs.fixedPin != rhs.fixedPin {return false}
|
||||
if lhs.deviceLoggingEnabled != rhs.deviceLoggingEnabled {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -2880,7 +2912,6 @@ extension Config.SecurityConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
4: .standard(proto: "is_managed"),
|
||||
5: .standard(proto: "serial_enabled"),
|
||||
6: .standard(proto: "debug_log_api_enabled"),
|
||||
7: .standard(proto: "bluetooth_logging_enabled"),
|
||||
8: .standard(proto: "admin_channel_enabled"),
|
||||
]
|
||||
|
||||
|
|
@ -2892,11 +2923,10 @@ extension Config.SecurityConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularBytesField(value: &self.publicKey) }()
|
||||
case 2: try { try decoder.decodeSingularBytesField(value: &self.privateKey) }()
|
||||
case 3: try { try decoder.decodeSingularBytesField(value: &self.adminKey) }()
|
||||
case 3: try { try decoder.decodeRepeatedBytesField(value: &self.adminKey) }()
|
||||
case 4: try { try decoder.decodeSingularBoolField(value: &self.isManaged) }()
|
||||
case 5: try { try decoder.decodeSingularBoolField(value: &self.serialEnabled) }()
|
||||
case 6: try { try decoder.decodeSingularBoolField(value: &self.debugLogApiEnabled) }()
|
||||
case 7: try { try decoder.decodeSingularBoolField(value: &self.bluetoothLoggingEnabled) }()
|
||||
case 8: try { try decoder.decodeSingularBoolField(value: &self.adminChannelEnabled) }()
|
||||
default: break
|
||||
}
|
||||
|
|
@ -2911,7 +2941,7 @@ extension Config.SecurityConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
try visitor.visitSingularBytesField(value: self.privateKey, fieldNumber: 2)
|
||||
}
|
||||
if !self.adminKey.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.adminKey, fieldNumber: 3)
|
||||
try visitor.visitRepeatedBytesField(value: self.adminKey, fieldNumber: 3)
|
||||
}
|
||||
if self.isManaged != false {
|
||||
try visitor.visitSingularBoolField(value: self.isManaged, fieldNumber: 4)
|
||||
|
|
@ -2922,9 +2952,6 @@ extension Config.SecurityConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
if self.debugLogApiEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.debugLogApiEnabled, fieldNumber: 6)
|
||||
}
|
||||
if self.bluetoothLoggingEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.bluetoothLoggingEnabled, fieldNumber: 7)
|
||||
}
|
||||
if self.adminChannelEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.adminChannelEnabled, fieldNumber: 8)
|
||||
}
|
||||
|
|
@ -2938,9 +2965,27 @@ extension Config.SecurityConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
if lhs.isManaged != rhs.isManaged {return false}
|
||||
if lhs.serialEnabled != rhs.serialEnabled {return false}
|
||||
if lhs.debugLogApiEnabled != rhs.debugLogApiEnabled {return false}
|
||||
if lhs.bluetoothLoggingEnabled != rhs.bluetoothLoggingEnabled {return false}
|
||||
if lhs.adminChannelEnabled != rhs.adminChannelEnabled {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension Config.SessionkeyConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = Config.protoMessageName + ".SessionkeyConfig"
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let _ = try decoder.nextFieldNumber() {
|
||||
}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: Config.SessionkeyConfig, rhs: Config.SessionkeyConfig) -> Bool {
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,6 +346,19 @@ public enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins.
|
||||
case me25Ls014Y10Td // = 75
|
||||
|
||||
///
|
||||
/// RP2040_FEATHER_RFM95
|
||||
/// Adafruit Feather RP2040 with RFM95 LoRa Radio RFM95 with SX1272, SSD1306 OLED
|
||||
/// https://www.adafruit.com/product/5714
|
||||
/// https://www.adafruit.com/product/326
|
||||
/// https://www.adafruit.com/product/938
|
||||
/// ^^^ short A0 to switch to I2C address 0x3C
|
||||
case rp2040FeatherRfm95 // = 76
|
||||
|
||||
/// M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
|
||||
case m5StackCorebasic // = 77
|
||||
case m5StackCore2 // = 78
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// 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.
|
||||
|
|
@ -434,6 +447,9 @@ public enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 73: self = .wioE5
|
||||
case 74: self = .radiomaster900Bandit
|
||||
case 75: self = .me25Ls014Y10Td
|
||||
case 76: self = .rp2040FeatherRfm95
|
||||
case 77: self = .m5StackCorebasic
|
||||
case 78: self = .m5StackCore2
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -516,6 +532,9 @@ public enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .wioE5: return 73
|
||||
case .radiomaster900Bandit: return 74
|
||||
case .me25Ls014Y10Td: return 75
|
||||
case .rp2040FeatherRfm95: return 76
|
||||
case .m5StackCorebasic: return 77
|
||||
case .m5StackCore2: return 78
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -603,6 +622,9 @@ extension HardwareModel: CaseIterable {
|
|||
.wioE5,
|
||||
.radiomaster900Bandit,
|
||||
.me25Ls014Y10Td,
|
||||
.rp2040FeatherRfm95,
|
||||
.m5StackCorebasic,
|
||||
.m5StackCore2,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -1520,9 +1542,22 @@ public struct DataMessage {
|
|||
/// a message a heart or poop emoji.
|
||||
public var emoji: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT.
|
||||
public var bitfield: UInt32 {
|
||||
get {return _bitfield ?? 0}
|
||||
set {_bitfield = newValue}
|
||||
}
|
||||
/// Returns true if `bitfield` has been explicitly set.
|
||||
public var hasBitfield: Bool {return self._bitfield != nil}
|
||||
/// Clears the value of `bitfield`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearBitfield() {self._bitfield = nil}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
||||
fileprivate var _bitfield: UInt32? = nil
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -1907,6 +1942,15 @@ public struct MeshPacket {
|
|||
/// assume it is important and use a slightly higher priority
|
||||
case reliable // = 70
|
||||
|
||||
///
|
||||
/// If priority is unset but the packet is a response to a request, we want it to get there relatively quickly.
|
||||
/// Furthermore, responses stop relaying packets directed to a node early.
|
||||
case response // = 80
|
||||
|
||||
///
|
||||
/// Higher priority for specific message types (portnums) to distinguish between other reliable packets.
|
||||
case high // = 100
|
||||
|
||||
///
|
||||
/// Ack/naks are sent with very high priority to ensure that retransmission
|
||||
/// stops as soon as possible
|
||||
|
|
@ -1928,6 +1972,8 @@ public struct MeshPacket {
|
|||
case 10: self = .background
|
||||
case 64: self = .default
|
||||
case 70: self = .reliable
|
||||
case 80: self = .response
|
||||
case 100: self = .high
|
||||
case 120: self = .ack
|
||||
case 127: self = .max
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
|
|
@ -1941,6 +1987,8 @@ public struct MeshPacket {
|
|||
case .background: return 10
|
||||
case .default: return 64
|
||||
case .reliable: return 70
|
||||
case .response: return 80
|
||||
case .high: return 100
|
||||
case .ack: return 120
|
||||
case .max: return 127
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
|
|
@ -2006,6 +2054,8 @@ extension MeshPacket.Priority: CaseIterable {
|
|||
.background,
|
||||
.default,
|
||||
.reliable,
|
||||
.response,
|
||||
.high,
|
||||
.ack,
|
||||
.max,
|
||||
]
|
||||
|
|
@ -3241,6 +3291,9 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
73: .same(proto: "WIO_E5"),
|
||||
74: .same(proto: "RADIOMASTER_900_BANDIT"),
|
||||
75: .same(proto: "ME25LS01_4Y10TD"),
|
||||
76: .same(proto: "RP2040_FEATHER_RFM95"),
|
||||
77: .same(proto: "M5STACK_COREBASIC"),
|
||||
78: .same(proto: "M5STACK_CORE2"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
@ -3779,6 +3832,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
6: .standard(proto: "request_id"),
|
||||
7: .standard(proto: "reply_id"),
|
||||
8: .same(proto: "emoji"),
|
||||
9: .same(proto: "bitfield"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3795,12 +3849,17 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
case 6: try { try decoder.decodeSingularFixed32Field(value: &self.requestID) }()
|
||||
case 7: try { try decoder.decodeSingularFixed32Field(value: &self.replyID) }()
|
||||
case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }()
|
||||
case 9: try { try decoder.decodeSingularUInt32Field(value: &self._bitfield) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public 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.portnum != .unknownApp {
|
||||
try visitor.visitSingularEnumField(value: self.portnum, fieldNumber: 1)
|
||||
}
|
||||
|
|
@ -3825,6 +3884,9 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
if self.emoji != 0 {
|
||||
try visitor.visitSingularFixed32Field(value: self.emoji, fieldNumber: 8)
|
||||
}
|
||||
try { if let v = self._bitfield {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 9)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -3837,6 +3899,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
if lhs.requestID != rhs.requestID {return false}
|
||||
if lhs.replyID != rhs.replyID {return false}
|
||||
if lhs.emoji != rhs.emoji {return false}
|
||||
if lhs._bitfield != rhs._bitfield {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -4224,6 +4287,8 @@ extension MeshPacket.Priority: SwiftProtobuf._ProtoNameProviding {
|
|||
10: .same(proto: "BACKGROUND"),
|
||||
64: .same(proto: "DEFAULT"),
|
||||
70: .same(proto: "RELIABLE"),
|
||||
80: .same(proto: "RESPONSE"),
|
||||
100: .same(proto: "HIGH"),
|
||||
120: .same(proto: "ACK"),
|
||||
127: .same(proto: "MAX"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
///
|
||||
/// MAX17048 1S lipo battery sensor (voltage, state of charge, time to go)
|
||||
case max17048 // = 28
|
||||
|
||||
///
|
||||
/// Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor
|
||||
case customSensor // = 29
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
|
|
@ -177,6 +181,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case 26: self = .bmp3Xx
|
||||
case 27: self = .icm20948
|
||||
case 28: self = .max17048
|
||||
case 29: self = .customSensor
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -212,6 +217,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case .bmp3Xx: return 26
|
||||
case .icm20948: return 27
|
||||
case .max17048: return 28
|
||||
case .customSensor: return 29
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -252,6 +258,7 @@ extension TelemetrySensorType: CaseIterable {
|
|||
.bmp3Xx,
|
||||
.icm20948,
|
||||
.max17048,
|
||||
.customSensor,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1003,6 +1010,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
|
|||
26: .same(proto: "BMP3XX"),
|
||||
27: .same(proto: "ICM20948"),
|
||||
28: .same(proto: "MAX17048"),
|
||||
29: .same(proto: "CUSTOM_SENSOR"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ struct WidgetsLiveActivity: Widget {
|
|||
.fixedSize()
|
||||
Spacer()
|
||||
}
|
||||
if context.state.nodesOnline >= 100 {
|
||||
if context.state.totalNodes >= 100 {
|
||||
Text("100+ online")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
|
|
@ -81,7 +81,7 @@ struct WidgetsLiveActivity: Widget {
|
|||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize()
|
||||
Text("Bad \(context.state.badReceivedPackets)")
|
||||
Text("Bad: \(context.state.badReceivedPackets)")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize()
|
||||
|
|
@ -97,7 +97,7 @@ struct WidgetsLiveActivity: Widget {
|
|||
} compactLeading: {
|
||||
Image("m-logo-black")
|
||||
.resizable()
|
||||
.frame(width: 30.0)
|
||||
.frame(width: 25)
|
||||
.padding(4)
|
||||
.background(.green.gradient, in: ContainerRelativeShape())
|
||||
} compactTrailing: {
|
||||
|
|
@ -120,27 +120,26 @@ struct WidgetsLiveActivity: Widget {
|
|||
}
|
||||
}
|
||||
|
||||
// struct WidgetsLiveActivity_Previews: PreviewProvider {
|
||||
// static let attributes = MeshActivityAttributes(nodeNum: 123456789, name: "RAK Compact Rotary Handset Gray 8E6G")
|
||||
// static let state = MeshActivityAttributes.ContentState(
|
||||
// timerRange: Date.now...Date(timeIntervalSinceNow: 60), connected: true, channelUtilization: 25.84, airtime: 10.01, batteryLevel: 39, nodes: 17, nodesOnline: 9)
|
||||
//
|
||||
// static var previews: some View {
|
||||
// attributes
|
||||
// .previewContext(state, viewKind: .dynamicIsland(.compact))
|
||||
// .previewDisplayName("Compact")
|
||||
// attributes
|
||||
// .previewContext(state, viewKind: .dynamicIsland(.minimal))
|
||||
// .previewDisplayName("Minimal")
|
||||
// attributes
|
||||
// .previewContext(state, viewKind: .dynamicIsland(.expanded))
|
||||
// .previewDisplayName("Expanded")
|
||||
// attributes
|
||||
// .previewContext(state, viewKind: .content)
|
||||
// .previewDisplayName("Notification")
|
||||
// }
|
||||
// }
|
||||
|
||||
struct WidgetsLiveActivity_Previews: PreviewProvider {
|
||||
static let attributes = MeshActivityAttributes(nodeNum: 123456789, name: "RAK Compact Rotary Handset Gray 8E6G")
|
||||
static let state = MeshActivityAttributes.ContentState(uptimeSeconds: 600, channelUtilization: 1.2, airtime: 3.5, sentPackets: 12587, receivedPackets: 12555, badReceivedPackets: 800, nodesOnline: 99, totalNodes: 100, timerRange: Date.now...Date(timeIntervalSinceNow: 300))
|
||||
|
||||
static var previews: some View {
|
||||
attributes
|
||||
.previewContext(state, viewKind: .dynamicIsland(.compact))
|
||||
.previewDisplayName("Compact")
|
||||
attributes
|
||||
.previewContext(state, viewKind: .dynamicIsland(.minimal))
|
||||
.previewDisplayName("Minimal")
|
||||
attributes
|
||||
.previewContext(state, viewKind: .dynamicIsland(.expanded))
|
||||
.previewDisplayName("Expanded")
|
||||
attributes
|
||||
.previewContext(state, viewKind: .content)
|
||||
.previewDisplayName("Notification")
|
||||
}
|
||||
}
|
||||
struct LiveActivityView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@Environment(\.isLuminanceReduced) var isLuminanceReduced
|
||||
|
|
@ -192,6 +191,7 @@ struct NodeInfoView: View {
|
|||
var timerRange: ClosedRange<Date>
|
||||
|
||||
var body: some View {
|
||||
let errorRate = (Double(badReceivedPackets) / Double(receivedPackets)) * 100
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(nodeName)
|
||||
.font(nodeName.count > 14 ? .callout : .title3)
|
||||
|
|
@ -203,19 +203,13 @@ struct NodeInfoView: View {
|
|||
.foregroundStyle(.secondary)
|
||||
.opacity(isLuminanceReduced ? 0.8 : 1.0)
|
||||
.fixedSize()
|
||||
Text("Packets Sent: \(sentPackets)")
|
||||
Text("Packets: Sent \(sentPackets) Rec. \(receivedPackets)")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
.opacity(isLuminanceReduced ? 0.8 : 1.0)
|
||||
.fixedSize()
|
||||
Text("Packets Received: \(receivedPackets)")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
.opacity(isLuminanceReduced ? 0.8 : 1.0)
|
||||
.fixedSize()
|
||||
Text("Bad Packets: \(badReceivedPackets)")
|
||||
Text("Bad: \(badReceivedPackets) \(String(format: "Error Rate: %.2f", errorRate))%")
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.secondary)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit b623762940ebdb1887a3b31b86f4d9cdaa7e6ecf
|
||||
Subproject commit 0acaec6eff00e748beeae89148093221f131cd9c
|
||||
Loading…
Add table
Add a link
Reference in a new issue