mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
commit
ab2e70d2dd
51 changed files with 1831 additions and 514 deletions
|
|
@ -1512,6 +1512,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"12 Hour Clock" : {
|
||||
|
||||
},
|
||||
"25" : {
|
||||
"localizations" : {
|
||||
|
|
@ -2809,34 +2812,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Allow incoming device control over the insecure legacy admin channel." : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Erlaubt die eingehende Gerätesteuerung über den unsicheren Legacy-Admin-Kanal."
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Consentire il controllo del dispositivo in entrata attraverso il canale di amministrazione legacy non sicuro."
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Дозволите контролу долазног уређаја над небезбедним старим администраторским каналом."
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "允許經由不安全的傳統管理通道接收裝置控制指令。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Allow Position Requests" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -11316,6 +11291,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Expiration" : {
|
||||
|
||||
},
|
||||
"Expire" : {
|
||||
"localizations" : {
|
||||
|
|
@ -13243,6 +13221,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"GitHub Repository" : {
|
||||
|
||||
},
|
||||
"Good" : {
|
||||
"localizations" : {
|
||||
|
|
@ -13816,34 +13797,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Help with App Development" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Aiuto per lo sviluppo di app"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Помози при развоју апликације"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "帮助开发应用程序"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "幫助App開發"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Hide alerts" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -15650,6 +15603,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Latitude in degrees (e.g., 37.7749)" : {
|
||||
|
||||
},
|
||||
"Latitude must be between -90 and 90 degrees" : {
|
||||
|
||||
},
|
||||
"LED Heartbeat" : {
|
||||
"localizations" : {
|
||||
|
|
@ -15765,34 +15724,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Legacy Administration" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Alte Administrationsart"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Amministrazione del patrimonio"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Стари начин администрације"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "舊版遠端管理"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Level" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -15957,40 +15888,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Location" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Standort"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Posizione"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Локација:"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "位置"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "位置"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Location:" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -16396,6 +16293,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Longitude in degrees (e.g., -122.4194)" : {
|
||||
|
||||
},
|
||||
"Longitude must be between -180 and 180 degrees" : {
|
||||
|
||||
},
|
||||
"LoRa" : {
|
||||
"localizations" : {
|
||||
|
|
@ -23974,6 +23877,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Regenerate Private Key" : {
|
||||
|
||||
},
|
||||
"Region" : {
|
||||
"localizations" : {
|
||||
|
|
@ -24353,17 +24259,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Replying to a message" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在回覆訊息"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Request Legacy Admin: %@" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -27776,6 +27671,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Set to current location" : {
|
||||
|
||||
},
|
||||
"Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. O hop broadcast messages will not get ACKs." : {
|
||||
"localizations" : {
|
||||
|
|
@ -27798,6 +27696,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sets the screen clock format to 12-hour." : {
|
||||
|
||||
},
|
||||
"Settings" : {
|
||||
"localizations" : {
|
||||
|
|
@ -28970,6 +28871,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sponsor App Development" : {
|
||||
|
||||
},
|
||||
"Spread Factor" : {
|
||||
"localizations" : {
|
||||
|
|
@ -33330,6 +33234,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Use my Location" : {
|
||||
|
||||
},
|
||||
"Use Preset" : {
|
||||
"localizations" : {
|
||||
|
|
@ -34157,6 +34064,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Waypoint Failed to Send" : {
|
||||
|
||||
},
|
||||
"Waypoint Options" : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -371,6 +371,7 @@
|
|||
DD1BD0ED2C603C91008C0C70 /* CustomFormatters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFormatters.swift; sourceTree = "<group>"; };
|
||||
DD1BD0F12C61D3AD008C0C70 /* MeshtasticDataModelV 42.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 42.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD1BD0F22C63C65E008C0C70 /* SecurityConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityConfig.swift; sourceTree = "<group>"; };
|
||||
DD1BEF462DFF284C0090CE24 /* MeshtasticDataModelV 53.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 53.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
|
||||
DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = "<group>"; };
|
||||
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -1802,12 +1803,12 @@
|
|||
INFOPLIST_FILE = Meshtastic/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Meshtastic;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.5;
|
||||
MARKETING_VERSION = 2.6.6;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1835,12 +1836,12 @@
|
|||
INFOPLIST_FILE = Meshtastic/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Meshtastic;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.5;
|
||||
MARKETING_VERSION = 2.6.6;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1865,13 +1866,13 @@
|
|||
INFOPLIST_FILE = Widgets/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widgets;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.5;
|
||||
MARKETING_VERSION = 2.6.6;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1897,13 +1898,13 @@
|
|||
INFOPLIST_FILE = Widgets/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Widgets;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.5;
|
||||
MARKETING_VERSION = 2.6.6;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -2002,6 +2003,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD1BEF462DFF284C0090CE24 /* MeshtasticDataModelV 53.xcdatamodel */,
|
||||
DD0836AB2DE7C7CB00A3A973 /* MeshtasticDataModelV 52.xcdatamodel */,
|
||||
DD63CB4E2DD4FBEA00AFCAE2 /* MeshtasticDataModelV 51.xcdatamodel */,
|
||||
233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */,
|
||||
|
|
@ -2055,7 +2057,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD0836AB2DE7C7CB00A3A973 /* MeshtasticDataModelV 52.xcdatamodel */;
|
||||
currentVersion = DD1BEF462DFF284C0090CE24 /* MeshtasticDataModelV 53.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@ struct RestartNodeIntent: AppIntent {
|
|||
if let connectedPeripheralNum = BLEManager.shared.connectedPeripheral?.num,
|
||||
let connectedNode = getNodeInfo(id: connectedPeripheralNum, context: PersistenceController.shared.container.viewContext),
|
||||
let fromUser = connectedNode.user,
|
||||
let toUser = connectedNode.user,
|
||||
let adminIndex = connectedNode.myInfo?.adminIndex {
|
||||
let toUser = connectedNode.user {
|
||||
|
||||
// Attempt to send shutdown, throw an error if it fails
|
||||
if !BLEManager.shared.sendReboot(fromUser: fromUser, toUser: toUser, adminIndex: adminIndex) {
|
||||
if !BLEManager.shared.sendReboot(fromUser: fromUser, toUser: toUser) {
|
||||
throw AppIntentErrors.AppIntentError.message("Failed to restart")
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import AppIntents
|
|||
import MeshtasticProtobufs
|
||||
|
||||
struct SendWaypointIntent: AppIntent {
|
||||
|
||||
var defaultDate = Date.now.addingTimeInterval(60 * 480)
|
||||
|
||||
static var title = LocalizedStringResource("Send a Waypoint")
|
||||
|
||||
|
|
@ -23,13 +25,24 @@ struct SendWaypointIntent: AppIntent {
|
|||
@Parameter(title: "Emoji", default: "📍")
|
||||
var emojiParameter: String?
|
||||
|
||||
@Parameter(title: "Location")
|
||||
var locationParameter: CLPlacemark
|
||||
// Replace CLPlacemark with latitude and longitude parameters
|
||||
@Parameter(title: "Latitude", description: "Latitude in degrees (e.g., 37.7749)")
|
||||
var latitudeParameter: Double
|
||||
|
||||
@Parameter(title: "Longitude", description: "Longitude in degrees (e.g., -122.4194)")
|
||||
var longitudeParameter: Double
|
||||
|
||||
@Parameter(title: "Locked", default: false)
|
||||
var isLocked: Bool
|
||||
|
||||
@Parameter(title: "Expiration")
|
||||
var expiration: Date?
|
||||
|
||||
func perform() async throws -> some IntentResult {
|
||||
if !BLEManager.shared.isConnected {
|
||||
throw AppIntentErrors.AppIntentError.notConnected
|
||||
}
|
||||
|
||||
// Provide default values if parameters are nil
|
||||
let name = nameParameter ?? "Dropped Pin"
|
||||
let description = descriptionParameter ?? ""
|
||||
|
|
@ -50,24 +63,39 @@ struct SendWaypointIntent: AppIntent {
|
|||
throw $emojiParameter.needsValueError("Must be a single emoji")
|
||||
}
|
||||
|
||||
// Validate latitude and longitude
|
||||
guard abs(latitudeParameter) <= 90 else {
|
||||
throw $latitudeParameter.needsValueError("Latitude must be between -90 and 90 degrees")
|
||||
}
|
||||
guard abs(longitudeParameter) <= 180 else {
|
||||
throw $longitudeParameter.needsValueError("Longitude must be between -180 and 180 degrees")
|
||||
}
|
||||
|
||||
var newWaypoint = Waypoint()
|
||||
|
||||
if let latitude = locationParameter.location?.coordinate.latitude {
|
||||
newWaypoint.latitudeI = Int32(latitude * 10_000_000)
|
||||
}
|
||||
|
||||
if let longitude = locationParameter.location?.coordinate.longitude {
|
||||
newWaypoint.longitudeI = Int32(longitude * 10_000_000)
|
||||
}
|
||||
// Set latitude and longitude directly
|
||||
newWaypoint.latitudeI = Int32(latitudeParameter * 10_000_000)
|
||||
newWaypoint.longitudeI = Int32(longitudeParameter * 10_000_000)
|
||||
|
||||
newWaypoint.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
// Unicode scalar value for the icon emoji string
|
||||
let unicodeScalers = emoji.unicodeScalars
|
||||
// First element as an UInt32
|
||||
let unicode = unicodeScalers[unicodeScalers.startIndex].value
|
||||
newWaypoint.icon = unicode
|
||||
newWaypoint.name = name
|
||||
newWaypoint.description_p = description
|
||||
|
||||
if let expirationDate = expiration {
|
||||
newWaypoint.expire = UInt32(expirationDate.timeIntervalSince1970)
|
||||
}
|
||||
|
||||
if isLocked {
|
||||
if let connectedPeripheral = BLEManager.shared.connectedPeripheral {
|
||||
newWaypoint.lockedTo = UInt32(connectedPeripheral.num)
|
||||
} else {
|
||||
throw AppIntentErrors.AppIntentError.notConnected
|
||||
}
|
||||
}
|
||||
|
||||
if !BLEManager.shared.sendWaypoint(waypoint: newWaypoint) {
|
||||
throw AppIntentErrors.AppIntentError.message("Failed to Send Waypoint")
|
||||
}
|
||||
|
|
@ -75,11 +103,9 @@ struct SendWaypointIntent: AppIntent {
|
|||
}
|
||||
|
||||
private func isValidSingleEmoji(_ emoji: String) -> Bool {
|
||||
// This regex pattern is for matching a single emoji
|
||||
let emojiPattern = "^([\\p{So}\\p{Cn}])$"
|
||||
let regex = try? NSRegularExpression(pattern: emojiPattern, options: [])
|
||||
let matches = regex?.matches(in: emoji, options: [], range: NSRange(location: 0, length: emoji.utf16.count))
|
||||
|
||||
return matches?.count == 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@ struct ShutDownNodeIntent: AppIntent {
|
|||
if let connectedPeripheralNum = BLEManager.shared.connectedPeripheral?.num,
|
||||
let connectedNode = getNodeInfo(id: connectedPeripheralNum, context: PersistenceController.shared.container.viewContext),
|
||||
let fromUser = connectedNode.user,
|
||||
let toUser = connectedNode.user,
|
||||
let adminIndex = connectedNode.myInfo?.adminIndex {
|
||||
let toUser = connectedNode.user {
|
||||
|
||||
// Attempt to send shutdown, throw an error if it fails
|
||||
if !BLEManager.shared.sendShutdown(fromUser: fromUser, toUser: toUser, adminIndex: adminIndex) {
|
||||
if !BLEManager.shared.sendShutdown(fromUser: fromUser, toUser: toUser) {
|
||||
throw AppIntentErrors.AppIntentError.message("Failed to shut down")
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
12
Meshtastic/Assets.xcassets/HELTECMESHPOCKET.imageset/Contents.json
vendored
Normal file
12
Meshtastic/Assets.xcassets/HELTECMESHPOCKET.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "heltec_mesh_pocket.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
196
Meshtastic/Assets.xcassets/HELTECMESHPOCKET.imageset/heltec_mesh_pocket.svg
vendored
Normal file
196
Meshtastic/Assets.xcassets/HELTECMESHPOCKET.imageset/heltec_mesh_pocket.svg
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="379.51 218.53 478.45 660.45"
|
||||
version="1.1"
|
||||
id="svg72"
|
||||
sodipodi:docname="heltec_mesh_pocket.svg"
|
||||
inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview72"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.78391112"
|
||||
inkscape:cx="4.4647919"
|
||||
inkscape:cy="146.06248"
|
||||
inkscape:window-width="1472"
|
||||
inkscape:window-height="890"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="38"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_2" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<style
|
||||
id="style1">.cls-1{fill:#d5bd0a;}.cls-2{fill:#eceded;}.cls-3{fill:#e3e1e0;}.cls-4{fill:#1d1d1b;}.cls-12,.cls-5,.cls-7{fill:none;}.cls-5,.cls-7{stroke:#1d1d1b;stroke-miterlimit:10;}.cls-5{stroke-width:0.87px;}.cls-6{fill:#fff;}.cls-7{stroke-width:3.49px;}.cls-8{fill:#2b293d;}.cls-9{fill:#2ea358;}.cls-10{fill:#efefef;}.cls-11{fill:#acd087;}.cls-12{stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.31px;}.cls-13{fill:#2590d0;}.cls-14{fill:#4e4d4e;}</style>
|
||||
</defs>
|
||||
<g
|
||||
id="Layer_2"
|
||||
data-name="Layer 2">
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M784.42,219.19H419.9a39.73,39.73,0,0,0-39.74,39.73V838.6a39.74,39.74,0,0,0,39.74,39.73H784.42a39.73,39.73,0,0,0,39.73-39.73V258.92A39.73,39.73,0,0,0,784.42,219.19Zm31,619.41a31,31,0,0,1-31,31H419.9a31,31,0,0,1-31-31V258.92a31,31,0,0,1,31-31H784.42a31,31,0,0,1,31,31Z"
|
||||
id="path1" />
|
||||
<rect
|
||||
class="cls-2"
|
||||
x="388.9"
|
||||
y="227.92"
|
||||
width="426.51"
|
||||
height="641.67"
|
||||
rx="31"
|
||||
id="rect1"
|
||||
style="fill:#fcfcfc;fill-opacity:1" />
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M857.3,302.32V633.56a7.53,7.53,0,0,1-1.75,4.84l-31.4,37.77V260a67.74,67.74,0,0,1,32.06,36.41A16.78,16.78,0,0,1,857.3,302.32Z"
|
||||
id="path2" />
|
||||
<rect
|
||||
class="cls-3"
|
||||
x="439.49"
|
||||
y="398.62"
|
||||
width="324.87"
|
||||
height="163.57"
|
||||
id="rect2"
|
||||
style="fill:#bfbfbf;fill-opacity:1" />
|
||||
<rect
|
||||
class="cls-8"
|
||||
x="513.04"
|
||||
y="724.36"
|
||||
width="166.81"
|
||||
height="33.44"
|
||||
rx="3.87"
|
||||
id="rect48" />
|
||||
<path
|
||||
class="cls-9"
|
||||
d="M558.92,724.36V757.8h-42a3.87,3.87,0,0,1-3.87-3.87V728.24a3.88,3.88,0,0,1,3.87-3.88Z"
|
||||
id="path48" />
|
||||
<rect
|
||||
class="cls-10"
|
||||
x="514.95"
|
||||
y="740.28"
|
||||
width="20.46"
|
||||
height="2.8"
|
||||
transform="translate(-383.39 761.4) rotate(-55.94)"
|
||||
id="rect49" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M551.65,750.93,541.54,736l-10.12,14.93-2.31-1.56L540,733.29a1.8,1.8,0,0,1,1.54-.82h0a1.85,1.85,0,0,1,1.57.87l10.85,16Zm-10.88-16.07s0,0,0,0Zm1.57,0,0,0Z"
|
||||
id="path49" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M566.72,731.61a31.88,31.88,0,0,1,5.15-.39c2.66,0,4.6.6,5.84,1.68a5.43,5.43,0,0,1,1.82,4.25,5.93,5.93,0,0,1-1.61,4.35,8.72,8.72,0,0,1-6.36,2.23,9.52,9.52,0,0,1-2.16-.18v8.14h-2.68Zm2.68,9.8a9.32,9.32,0,0,0,2.23.21c3.24,0,5.21-1.54,5.21-4.34s-1.94-4-4.9-4a11.68,11.68,0,0,0-2.54.21Z"
|
||||
id="path50" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M595.8,744.27c0,5.4-3.83,7.75-7.44,7.75-4,0-7.16-2.89-7.16-7.51,0-4.88,3.27-7.75,7.41-7.75S595.8,739.81,595.8,744.27Zm-11.85.15c0,3.2,1.88,5.61,4.54,5.61s4.53-2.38,4.53-5.67c0-2.47-1.26-5.61-4.47-5.61S584,741.65,584,744.42Z"
|
||||
id="path51" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M600.1,737.1l2,7.41c.43,1.63.84,3.14,1.11,4.65h.1c.34-1.48.83-3,1.32-4.62L607,737.1h2.29l2.31,7.29c.56,1.75,1,3.29,1.33,4.77h.09a44.65,44.65,0,0,1,1.14-4.74l2.13-7.32H619l-4.82,14.59h-2.47l-2.28-7a48.12,48.12,0,0,1-1.33-4.79h-.06a40.69,40.69,0,0,1-1.36,4.82l-2.41,6.94h-2.47l-4.5-14.59Z"
|
||||
id="path52" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M623,744.87c.06,3.59,2.41,5.07,5.12,5.07a10.08,10.08,0,0,0,4.14-.75l.46,1.9a12.41,12.41,0,0,1-5,.9c-4.6,0-7.35-3-7.35-7.36s2.66-7.87,7-7.87c4.88,0,6.18,4.19,6.18,6.88a9.56,9.56,0,0,1-.1,1.23Zm8-1.89c0-1.69-.71-4.32-3.76-4.32-2.75,0-4,2.48-4.17,4.32Z"
|
||||
id="path53" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M636.9,741.65c0-1.72,0-3.2-.12-4.55h2.38l.09,2.86h.12a4.48,4.48,0,0,1,4.14-3.2,3,3,0,0,1,.77.09v2.51a4.19,4.19,0,0,0-.93-.09,3.77,3.77,0,0,0-3.64,3.4,7.56,7.56,0,0,0-.12,1.24v7.78H636.9Z"
|
||||
id="path54" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M648.2,744.87c.07,3.59,2.41,5.07,5.13,5.07a10.11,10.11,0,0,0,4.14-.75l.46,1.9a12.44,12.44,0,0,1-5,.9c-4.6,0-7.35-3-7.35-7.36s2.66-7.87,7-7.87c4.88,0,6.17,4.19,6.17,6.88a10,10,0,0,1-.09,1.23Zm8-1.89c0-1.69-.71-4.32-3.77-4.32-2.75,0-3.95,2.48-4.17,4.32Z"
|
||||
id="path55" />
|
||||
<path
|
||||
class="cls-10"
|
||||
d="M675.07,730.28v17.64c0,1.3,0,2.77.12,3.77h-2.44l-.12-2.53h-.07a5.55,5.55,0,0,1-5.09,2.86c-3.61,0-6.39-3-6.39-7.42,0-4.85,3.06-7.84,6.7-7.84a5.05,5.05,0,0,1,4.51,2.23h.06v-8.71ZM672.35,743a4.21,4.21,0,0,0-.13-1.11,4,4,0,0,0-3.91-3.08c-2.81,0-4.48,2.42-4.48,5.64,0,3,1.48,5.4,4.41,5.4a4.06,4.06,0,0,0,4-3.17,4.15,4.15,0,0,0,.13-1.14Z"
|
||||
id="path56" />
|
||||
<rect
|
||||
class="cls-11"
|
||||
x="561.8"
|
||||
y="768.06"
|
||||
width="65.15"
|
||||
height="6.66"
|
||||
rx="3.33"
|
||||
id="rect56" />
|
||||
<path
|
||||
class="cls-12"
|
||||
d="M784.42,219.19H419.9a39.73,39.73,0,0,0-39.74,39.73V838.6a39.74,39.74,0,0,0,39.74,39.73H784.42a39.73,39.73,0,0,0,39.73-39.73V258.92A39.73,39.73,0,0,0,784.42,219.19Zm31,619.41a31,31,0,0,1-31,31H419.9a31,31,0,0,1-31-31V258.92a31,31,0,0,1,31-31H784.42a31,31,0,0,1,31,31Z"
|
||||
id="path57" />
|
||||
<path
|
||||
class="cls-12"
|
||||
d="M824.15,260a67.74,67.74,0,0,1,32.06,36.41,16.78,16.78,0,0,1,1.09,5.87V633.56a7.53,7.53,0,0,1-1.75,4.84l-31.4,37.77"
|
||||
id="path58" />
|
||||
<path
|
||||
class="cls-13"
|
||||
d="M714.73,253.2s0,0,0,0l-.24,1-7.2,29a4.47,4.47,0,0,0,.7,3.69l.06.07a15.25,15.25,0,0,1-1.59-.42,9.75,9.75,0,0,1-3.94-2.24,6.35,6.35,0,0,1-1.63-6.06l.67-2.7L705,261.64h-4.77l-1.67,6.73c-.52,2.1-.6,3,.25,5a1.18,1.18,0,0,1,0,.25l-2.48-1.32a5.56,5.56,0,0,1-2.95-6.41l4.11-16.56a.09.09,0,0,0,0,0,4.73,4.73,0,0,0-.86-4.11l1.69.75.76.34a5.79,5.79,0,0,1,3.27,6.67l-1,4h4.76l2.07-8.35a4.52,4.52,0,0,0,0-2,6.32,6.32,0,0,0-1.17-2.68,13,13,0,0,1,2.1.71C711.81,245.69,715.73,248.21,714.73,253.2Z"
|
||||
id="path59" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M724.83,258.46l-.59,2.36h5l-1.12,4.54h-5l-.76,3.06h5L726.25,273h-9.88l1.13-4.54.76-3.06,1.12-4.54.59-2.36.71-2.86a2.23,2.23,0,0,1,1.27-1.5h0a2.22,2.22,0,0,1,.87-.18H731l-1.13,4.54Z"
|
||||
id="path60" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M765.52,258.46l-.59,2.36h5l-1.13,4.54h-5l-.76,3.06h5L766.94,273h-9.88l1.13-4.54.76-3.06,1.13-4.54.58-2.36.71-2.86a2.22,2.22,0,0,1,1.28-1.5h0a2.15,2.15,0,0,1,.87-.18h8.14l-1.13,4.54Z"
|
||||
id="path61" />
|
||||
<polygon
|
||||
class="cls-14"
|
||||
points="758.02 253.92 757.07 257.81 753.56 257.81 753.39 258.46 752.81 260.82 749.8 272.96 744.93 272.96 746.07 268.42 746.82 265.36 747.95 260.82 748.53 258.46 748.7 257.81 745.1 257.81 745.91 254.53 746.06 253.92 758.02 253.92"
|
||||
id="polygon61" />
|
||||
<polygon
|
||||
class="cls-14"
|
||||
points="741.13 268.42 740 272.96 730.13 272.96 731.26 268.42 732.01 265.36 733.14 260.82 733.73 258.46 734.7 254.53 734.85 253.92 739.71 253.92 738.59 258.46 738 260.82 736.12 268.42 741.13 268.42"
|
||||
id="polygon62" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M788.73,255l-1.5,6.26a3.48,3.48,0,0,0-1.3-2.36c-1.86-1.4-4.88-.52-6.78,2s-1.92,5.66-.07,7.05,4.88.52,6.77-2c.15-.2.28-.4.41-.61l-1.73,7.24a7.65,7.65,0,0,1-6.77.91c-4.31-1.62-6.17-7.27-4.16-12.64C775.55,255.69,781.94,250.72,788.73,255Z"
|
||||
id="path62" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M718.84,282.11h-2.13l-.55,1h-1.92l3.81-6.16h2.05l.76,6.16h-2Zm-.06-1.33-.12-2.22-1.21,2.22Z"
|
||||
id="path63" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M726.65,277h1.88l-.91,3.67a3.57,3.57,0,0,1-.43,1,3.2,3.2,0,0,1-1.61,1.36,4.44,4.44,0,0,1-1.37.2,8.21,8.21,0,0,1-1-.06,2,2,0,0,1-.82-.25,1.53,1.53,0,0,1-.51-.53,1.47,1.47,0,0,1-.22-.71,3.41,3.41,0,0,1,.08-1l.91-3.67h1.87l-.93,3.75a1,1,0,0,0,.08.79.81.81,0,0,0,.7.29,1.32,1.32,0,0,0,.83-.28,1.4,1.4,0,0,0,.47-.8Z"
|
||||
id="path64" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M729.34,277h5.72l-.38,1.52h-1.92l-1.15,4.64h-1.88l1.15-4.64H729Z"
|
||||
id="path65" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M734.62,280.05A4.25,4.25,0,0,1,736,277.7a3.87,3.87,0,0,1,2.52-.84,2.49,2.49,0,0,1,2.13.83,2.69,2.69,0,0,1,.25,2.31,4.83,4.83,0,0,1-.8,1.77,3.77,3.77,0,0,1-1.3,1.08,3.93,3.93,0,0,1-1.79.38,3.32,3.32,0,0,1-1.62-.33,1.81,1.81,0,0,1-.83-1A3.25,3.25,0,0,1,734.62,280.05Zm1.87,0a1.91,1.91,0,0,0,0,1.34.89.89,0,0,0,.83.41,1.46,1.46,0,0,0,1-.4,3,3,0,0,0,.69-1.43,1.78,1.78,0,0,0,0-1.28.94.94,0,0,0-.85-.4,1.48,1.48,0,0,0-1,.41A2.67,2.67,0,0,0,736.49,280.06Z"
|
||||
id="path66" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M742.67,277h2.47l0,3.75L747,277h2.47L748,283.13h-1.54l1.17-4.7-2.35,4.7h-1.39l0-4.7-1.17,4.7h-1.54Z"
|
||||
id="path67" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M753.17,282.11H751l-.55,1h-1.92l3.82-6.16h2.05l.75,6.16h-2Zm-.06-1.33-.12-2.22-1.21,2.22Z"
|
||||
id="path68" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M756.31,277H762l-.38,1.52h-1.92l-1.15,4.64h-1.88l1.16-4.64h-1.92Z"
|
||||
id="path69" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M762.94,277h1.89l-1.53,6.16h-1.89Z"
|
||||
id="path70" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M765.17,280.05a4.21,4.21,0,0,1,1.42-2.35,3.86,3.86,0,0,1,2.51-.84,2.5,2.5,0,0,1,2.14.83,2.69,2.69,0,0,1,.25,2.31,4.83,4.83,0,0,1-.8,1.77,3.8,3.8,0,0,1-1.31,1.08,3.89,3.89,0,0,1-1.78.38,3.32,3.32,0,0,1-1.62-.33,1.77,1.77,0,0,1-.83-1A3.23,3.23,0,0,1,765.17,280.05Zm1.88,0a1.91,1.91,0,0,0,0,1.34.89.89,0,0,0,.83.41,1.47,1.47,0,0,0,1-.4,3,3,0,0,0,.68-1.43,1.68,1.68,0,0,0,0-1.28.91.91,0,0,0-.84-.4,1.47,1.47,0,0,0-1,.41A2.67,2.67,0,0,0,767.05,280.06Z"
|
||||
id="path71" />
|
||||
<path
|
||||
class="cls-14"
|
||||
d="M773.26,277H775l1.44,3.41.85-3.41h1.77l-1.53,6.16h-1.77l-1.44-3.39-.84,3.39h-1.77Z"
|
||||
id="path72" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
12
Meshtastic/Assets.xcassets/SEEEDSOLARNODE.imageset/Contents.json
vendored
Normal file
12
Meshtastic/Assets.xcassets/SEEEDSOLARNODE.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "seeed_solar.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
1
Meshtastic/Assets.xcassets/SEEEDSOLARNODE.imageset/seeed_solar.svg
vendored
Normal file
1
Meshtastic/Assets.xcassets/SEEEDSOLARNODE.imageset/seeed_solar.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 98 KiB |
|
|
@ -106,14 +106,44 @@ extension UserEntity {
|
|||
}
|
||||
}
|
||||
}
|
||||
public func createUser(num: Int64, context: NSManagedObjectContext) -> UserEntity {
|
||||
let newUser = UserEntity(context: context)
|
||||
newUser.num = Int64(num)
|
||||
let userId = String(format: "%2X", num)
|
||||
newUser.userId = "!\(userId)"
|
||||
let last4 = String(userId.suffix(4))
|
||||
newUser.longName = "Meshtastic \(last4)"
|
||||
newUser.shortName = last4
|
||||
newUser.hwModel = "UNSET"
|
||||
|
||||
public func createUser(num: Int64, context: NSManagedObjectContext) throws -> UserEntity {
|
||||
// Validate Input
|
||||
guard num >= 0 else {
|
||||
throw CoreDataError.invalidInput(message: "User number cannot be negative.")
|
||||
}
|
||||
|
||||
var newUser: UserEntity! // Use an implicitly unwrapped optional, but ensure it's assigned
|
||||
|
||||
context.performAndWait {
|
||||
newUser = UserEntity(context: context)
|
||||
newUser.num = num
|
||||
|
||||
let userId = String(format: "%016llX", num)
|
||||
newUser.userId = "!\(userId)"
|
||||
|
||||
let last4 = String(userId.suffix(4))
|
||||
newUser.longName = "Meshtastic \(last4)"
|
||||
newUser.shortName = last4
|
||||
newUser.hwModel = "UNSET"
|
||||
}
|
||||
|
||||
return newUser
|
||||
}
|
||||
|
||||
enum CoreDataError: Error, LocalizedError {
|
||||
case invalidInput(message: String)
|
||||
case saveFailed(message: String)
|
||||
case entityCreationFailed(message: String) // In case UserEntity(context:) fails for some reason
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidInput(let message):
|
||||
return "Core Data Input Error: \(message)"
|
||||
case .saveFailed(let message):
|
||||
return "Core Data Save Error: \(message)"
|
||||
case .entityCreationFailed(let message):
|
||||
return "Core Data Entity Creation Error: \(message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ extension WaypointEntity {
|
|||
static func allWaypointssFetchRequest() -> NSFetchRequest<WaypointEntity> {
|
||||
let request: NSFetchRequest<WaypointEntity> = WaypointEntity.fetchRequest()
|
||||
request.fetchLimit = 50
|
||||
// request.fetchBatchSize = 1
|
||||
// request.returnsObjectsAsFaults = false
|
||||
// request.includesSubentities = true
|
||||
request.returnsDistinctResults = true
|
||||
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: false)]
|
||||
request.predicate = NSPredicate(format: "expire == nil || expire >= %@", Date() as NSDate)
|
||||
|
|
@ -24,7 +21,6 @@ extension WaypointEntity {
|
|||
}
|
||||
|
||||
var latitude: Double? {
|
||||
|
||||
let d = Double(latitudeI)
|
||||
if d == 0 {
|
||||
return 0
|
||||
|
|
@ -33,7 +29,6 @@ extension WaypointEntity {
|
|||
}
|
||||
|
||||
var longitude: Double? {
|
||||
|
||||
let d = Double(longitudeI)
|
||||
if d == 0 {
|
||||
return 0
|
||||
|
|
@ -46,7 +41,7 @@ extension WaypointEntity {
|
|||
let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
|
||||
return coord
|
||||
} else {
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,16 +55,29 @@ extension WaypointEntity {
|
|||
}
|
||||
|
||||
extension WaypointEntity: MKAnnotation {
|
||||
public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationsHandler.DefaultLocation }
|
||||
public var title: String? { name ?? "Dropped Pin" }
|
||||
@MainActor
|
||||
public var coordinate: CLLocationCoordinate2D {
|
||||
get {
|
||||
waypointCoordinate ?? LocationsHandler.DefaultLocation
|
||||
}
|
||||
set {
|
||||
latitudeI = Int32(newValue.latitude * 1e7)
|
||||
longitudeI = Int32(newValue.longitude * 1e7)
|
||||
}
|
||||
}
|
||||
|
||||
public var title: String? {
|
||||
name ?? "Dropped Pin"
|
||||
}
|
||||
|
||||
public var subtitle: String? {
|
||||
(longDescription ?? "") +
|
||||
String(expire != nil ? "\n⌛ Expires \(String(describing: expire?.formatted()))" : "") +
|
||||
String(locked > 0 ? "\n🔒 Locked" : "") }
|
||||
String(locked > 0 ? "\n🔒 Locked" : "")
|
||||
}
|
||||
}
|
||||
|
||||
struct WaypointCoordinate: Identifiable {
|
||||
|
||||
let id: UUID
|
||||
let coordinate: CLLocationCoordinate2D?
|
||||
let waypointId: Int64
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -185,9 +185,6 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
|
|||
mutableChannels.add(newChannel)
|
||||
}
|
||||
fetchedMyInfo[0].channels = mutableChannels.copy() as? NSOrderedSet
|
||||
if newChannel.name?.lowercased() == "admin" {
|
||||
fetchedMyInfo[0].adminIndex = newChannel.index
|
||||
}
|
||||
context.refresh(newChannel, mergeChanges: true)
|
||||
do {
|
||||
try context.save()
|
||||
|
|
@ -330,8 +327,14 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
}}
|
||||
newNode.user = newUser
|
||||
} else if nodeInfo.num > Constants.minimumNodeNum {
|
||||
let newUser = createUser(num: Int64(nodeInfo.num), context: context)
|
||||
newNode.user = newUser
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(nodeInfo.num), context: context)
|
||||
newNode.user = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity (Invalid Input) from node number: \(nodeInfo.num, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity from node number: \(nodeInfo.num, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeInfo.position.longitudeI != 0 && nodeInfo.position.latitudeI != 0) && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) {
|
||||
|
|
@ -420,9 +423,14 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
}
|
||||
} else {
|
||||
if fetchedNode[0].user == nil && nodeInfo.num > Constants.minimumNodeNum {
|
||||
|
||||
let newUser = createUser(num: Int64(nodeInfo.num), context: context)
|
||||
fetchedNode[0].user = newUser
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(nodeInfo.num), context: context)
|
||||
fetchedNode[0].user = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity on an existing node (Invalid Input) from node number: \(nodeInfo.num, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity on an existing node from node number: \(nodeInfo.num, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -711,7 +719,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
if let telemetryMessage = try? Telemetry(serializedBytes: packet.decoded.payload) {
|
||||
let logString = String.localizedStringWithFormat("Telemetry received for: %@".localized, String(packet.from))
|
||||
Logger.mesh.info("📈 \(logString, privacy: .public)")
|
||||
if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) && telemetryMessage.variant != Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) {
|
||||
if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) && telemetryMessage.variant != Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) {
|
||||
/// Other unhandled telemetry packets
|
||||
return
|
||||
}
|
||||
|
|
@ -932,7 +940,14 @@ func textMessageAppPacket(
|
|||
// For S&F broadcast messages, treat as a channel message (not a DM)
|
||||
newMessage.toUser = nil
|
||||
} else {
|
||||
newMessage.toUser = createUser(num: Int64(truncatingIfNeeded: packet.to), context: context)
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(truncatingIfNeeded: packet.to), context: context)
|
||||
newMessage.toUser = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity (Invalid Input) from node number: \(packet.to, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity from node number: \(packet.to, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
if fetchedUsers.first(where: { $0.num == packet.from }) != nil {
|
||||
|
|
@ -962,7 +977,14 @@ func textMessageAppPacket(
|
|||
}
|
||||
} else {
|
||||
/// Make a new from user if they are unknown
|
||||
newMessage.fromUser = createUser(num: Int64(truncatingIfNeeded: packet.from), context: context)
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(truncatingIfNeeded: packet.from), context: context)
|
||||
newMessage.fromUser = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity (Invalid Input) from node number: \(packet.from, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity from node number: \(packet.from, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
if packet.rxTime > 0 {
|
||||
newMessage.fromUser?.userNode?.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
|
|
@ -979,79 +1001,79 @@ func textMessageAppPacket(
|
|||
try context.save()
|
||||
Logger.data.info("💾 Saved a new message for \(newMessage.messageId, privacy: .public)")
|
||||
messageSaved = true
|
||||
|
||||
if messageSaved {
|
||||
if packet.decoded.portnum == PortNum.detectionSensorApp && !UserDefaults.enableDetectionNotifications {
|
||||
return
|
||||
}
|
||||
if newMessage.fromUser != nil && newMessage.toUser != nil {
|
||||
// Set Unread Message Indicators
|
||||
if packet.to == connectedNode {
|
||||
appState.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0
|
||||
}
|
||||
if !(newMessage.fromUser?.mute ?? false) {
|
||||
// Create an iOS Notification for the received DM message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(packet.from),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
}
|
||||
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
|
||||
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode))
|
||||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest)
|
||||
if !fetchedMyInfo.isEmpty {
|
||||
appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages
|
||||
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
|
||||
if channel.index == newMessage.channel {
|
||||
context.refresh(channel, mergeChanges: true)
|
||||
}
|
||||
if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications {
|
||||
// Create an iOS Notification for the received channel message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(newMessage.fromUser?.userId ?? "0"),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Handle error
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Failed to save new MessageEntity \(nsError, privacy: .public)")
|
||||
}
|
||||
// Send notifications if the message saved properly to core data
|
||||
if messageSaved {
|
||||
if packet.decoded.portnum == PortNum.detectionSensorApp && !UserDefaults.enableDetectionNotifications {
|
||||
return
|
||||
}
|
||||
if newMessage.fromUser != nil && newMessage.toUser != nil {
|
||||
// Set Unread Message Indicators
|
||||
if packet.to == connectedNode {
|
||||
appState.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0
|
||||
}
|
||||
if !(newMessage.fromUser?.mute ?? false) {
|
||||
// Create an iOS Notification for the received DM message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(packet.from),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
}
|
||||
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
|
||||
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode))
|
||||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest)
|
||||
if !fetchedMyInfo.isEmpty {
|
||||
appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages
|
||||
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
|
||||
if channel.index == newMessage.channel {
|
||||
context.refresh(channel, mergeChanges: true)
|
||||
}
|
||||
if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications {
|
||||
// Create an iOS Notification for the received channel message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(newMessage.fromUser?.userId ?? "0"),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Handle error
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Logger.data.error("Fetch Message To and From Users Error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV 52.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV 53.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,506 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23788.4" systemVersion="24D81" 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="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="triggerType" optional="YES" attributeType="Integer 32" 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="tripleClickAsAdHocPing" optional="YES" attributeType="Boolean" defaultValueString="YES" 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="excludedModules" 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="use12HClock" attributeType="Boolean" defaultValueString="NO" 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="mapReportingShouldReportLocation" optional="YES" attributeType="Boolean" 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="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="deviceId" optional="YES" attributeType="Binary"/>
|
||||
<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"/>
|
||||
<attribute name="registered" attributeType="Boolean" defaultValueString="NO" 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="enabledProtocols" 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="ignored" attributeType="Boolean" defaultValueString="NO" 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="adminKey2" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="adminKey3" 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">
|
||||
<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="irLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="lux" optional="YES" attributeType="Float" defaultValueString="0.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="numRxDupe" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTotalNodes" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTxRelay" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTxRelayCanceled" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh1Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh1Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh2Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh2Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh3Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh3Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="radiation" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rainfall1H" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rainfall24H" optional="YES" attributeType="Float" defaultValueString="0.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="soilMoisture" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="soilTemperature" 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="uvLux" optional="YES" attributeType="Float" defaultValueString="0.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="whiteLux" 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="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="hopsBack" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopsTowards" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="routeBackText" optional="YES" attributeType="String"/>
|
||||
<attribute name="routeText" optional="YES" attributeType="String"/>
|
||||
<attribute name="sent" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Cascade" 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="back" 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="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.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="unmessagable" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<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>
|
||||
|
|
@ -170,8 +170,14 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
|
||||
if newUserMessage.id.isEmpty {
|
||||
if packet.from > Constants.minimumNodeNum {
|
||||
let newUser = createUser(num: Int64(packet.from), context: context)
|
||||
newNode.user = newUser
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(truncatingIfNeeded: packet.from), context: context)
|
||||
newNode.user = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity (Invalid Input) from node number: \(packet.from, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity from node number: \(packet.from, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
|
|
@ -225,17 +231,34 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
}
|
||||
} else {
|
||||
if packet.from > Constants.minimumNodeNum {
|
||||
let newUser = createUser(num: Int64(packet.from), context: context)
|
||||
if !packet.publicKey.isEmpty {
|
||||
newNode.user?.pkiEncrypted = true
|
||||
newNode.user?.publicKey = packet.publicKey
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(truncatingIfNeeded: packet.from), context: context)
|
||||
if !packet.publicKey.isEmpty {
|
||||
newNode.user?.pkiEncrypted = true
|
||||
newNode.user?.publicKey = packet.publicKey
|
||||
}
|
||||
newNode.user = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity (Invalid Input) from node number: \(packet.from, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity from node number: \(packet.from, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
newNode.user = newUser
|
||||
}
|
||||
}
|
||||
|
||||
// User is messed up and has failed to create at least once, if this fails bail out
|
||||
if newNode.user == nil && packet.from > Constants.minimumNodeNum {
|
||||
newNode.user = createUser(num: Int64(packet.from), context: context)
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(packet.from), context: context)
|
||||
newNode.user = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity (Invalid Input) from node number: \(packet.from, privacy: .public) Error: \(message, privacy: .public)")
|
||||
context.rollback()
|
||||
return
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity from node number: \(packet.from, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
context.rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let myInfoEntity = MyInfoEntity(context: context)
|
||||
|
|
@ -317,9 +340,14 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
fetchedNode[0].hopsAway = Int32(packet.hopStart - packet.hopLimit)
|
||||
}
|
||||
if fetchedNode[0].user == nil {
|
||||
let newUser = createUser(num: Int64(truncatingIfNeeded: packet.from), context: context)
|
||||
fetchedNode[0].user? = newUser
|
||||
|
||||
do {
|
||||
let newUser = try createUser(num: Int64(truncatingIfNeeded: packet.from), context: context)
|
||||
fetchedNode[0].user = newUser
|
||||
} catch CoreDataError.invalidInput(let message) {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity on an existing node (Invalid Input) from node number: \(packet.from, privacy: .public) Error: \(message, privacy: .public)")
|
||||
} catch {
|
||||
Logger.data.error("Error Creating a new Core Data UserEntity on an existing node from node number: \(packet.from, privacy: .public) Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
|
|
@ -553,6 +581,7 @@ func upsertDisplayConfigPacket(config: Config.DisplayConfig, nodeNum: Int64, ses
|
|||
newDisplayConfig.displayMode = Int32(config.displaymode.rawValue)
|
||||
newDisplayConfig.units = Int32(config.units.rawValue)
|
||||
newDisplayConfig.headingBold = config.headingBold
|
||||
newDisplayConfig.use12HClock = config.use12HClock
|
||||
fetchedNode[0].displayConfig = newDisplayConfig
|
||||
} else {
|
||||
|
||||
|
|
@ -564,6 +593,7 @@ func upsertDisplayConfigPacket(config: Config.DisplayConfig, nodeNum: Int64, ses
|
|||
fetchedNode[0].displayConfig?.oledType = Int32(config.oled.rawValue)
|
||||
fetchedNode[0].displayConfig?.displayMode = Int32(config.displaymode.rawValue)
|
||||
fetchedNode[0].displayConfig?.units = Int32(config.units.rawValue)
|
||||
fetchedNode[0].displayConfig?.use12HClock = config.use12HClock
|
||||
fetchedNode[0].displayConfig?.headingBold = config.headingBold
|
||||
}
|
||||
if sessionPasskey != nil {
|
||||
|
|
|
|||
|
|
@ -229,7 +229,8 @@
|
|||
"images": [
|
||||
"tlora-t3s3-epaper.svg"
|
||||
],
|
||||
"requiresDfu": true
|
||||
"requiresDfu": true,
|
||||
"hasInkHud": true
|
||||
},
|
||||
{
|
||||
"hwModel": 17,
|
||||
|
|
@ -604,7 +605,7 @@
|
|||
"hwModelSlug": "HELTEC_WIRELESS_TRACKER",
|
||||
"platformioTarget": "tracksenger",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": false,
|
||||
"activelySupported": true,
|
||||
"supportLevel": 3,
|
||||
"displayName": "TrackSenger (small TFT)",
|
||||
"requiresDfu": true,
|
||||
|
|
@ -626,7 +627,7 @@
|
|||
"hwModelSlug": "HELTEC_WIRELESS_TRACKER",
|
||||
"platformioTarget": "tracksenger-oled",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": false,
|
||||
"activelySupported": true,
|
||||
"supportLevel": 3,
|
||||
"displayName": "TrackSenger (big OLED)",
|
||||
"partitionScheme": "8MB"
|
||||
|
|
@ -872,5 +873,110 @@
|
|||
"images": [
|
||||
"thinknode_m2.svg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"hwModel": 94,
|
||||
"hwModelSlug": "HELTEC_MESH_POCKET",
|
||||
"platformioTarget": "heltec-mesh-pocket-10000",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"supportLevel": 1,
|
||||
"displayName": "Heltec MeshPocket",
|
||||
"tags": [
|
||||
"Heltec"
|
||||
],
|
||||
"images": [
|
||||
"heltec_mesh_pocket.svg"
|
||||
],
|
||||
"requiresDfu": true,
|
||||
"hasInkHud": true
|
||||
},
|
||||
{
|
||||
"hwModel": 95,
|
||||
"hwModelSlug": "SEEED_SOLAR_NODE",
|
||||
"platformioTarget": "seeed_solar_node",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"supportLevel": 1,
|
||||
"displayName": "Seeed SenseCAP Solar Node",
|
||||
"tags": [
|
||||
"Seeed"
|
||||
],
|
||||
"images": [
|
||||
"seeed_solar.svg"
|
||||
],
|
||||
"requiresDfu": true
|
||||
},
|
||||
{
|
||||
"hwModel": 99,
|
||||
"hwModelSlug": "SEEED_WIO_TRACKER_L1",
|
||||
"platformioTarget": "seeed_wio_tracker_L1",
|
||||
"architecture": "nrf52840",
|
||||
"activelySupported": true,
|
||||
"supportLevel": 1,
|
||||
"displayName": "Seeed Wio Tracker L1",
|
||||
"tags": [
|
||||
"Seeed"
|
||||
],
|
||||
"images": [
|
||||
"wio_tracker_l1_case.svg"
|
||||
],
|
||||
"requiresDfu": true
|
||||
},
|
||||
{
|
||||
"hwModel": 97,
|
||||
"hwModelSlug": "CROWPANEL",
|
||||
"platformioTarget": "elecrow-adv1-43-50-70-tft",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"supportLevel": 1,
|
||||
"displayName": "Crowpanel Adv 4.3/5.0/7.0 TFT",
|
||||
"tags": [
|
||||
"Elecrow"
|
||||
],
|
||||
"requiresDfu": true,
|
||||
"images": [
|
||||
"crowpanel_5_0.svg",
|
||||
"crowpanel_7_0.svg"
|
||||
],
|
||||
"partitionScheme": "16MB",
|
||||
"hasMui": true
|
||||
},
|
||||
{
|
||||
"hwModel": 97,
|
||||
"hwModelSlug": "CROWPANEL",
|
||||
"platformioTarget": "elecrow-adv-24-28-tft",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"supportLevel": 1,
|
||||
"displayName": "Crowpanel Adv 2.4/2.8 TFT",
|
||||
"tags": [
|
||||
"Elecrow"
|
||||
],
|
||||
"requiresDfu": true,
|
||||
"images": [
|
||||
"crowpanel_2_4.svg",
|
||||
"crowpanel_2_8.svg"
|
||||
],
|
||||
"partitionScheme": "16MB",
|
||||
"hasMui": true
|
||||
},
|
||||
{
|
||||
"hwModel": 97,
|
||||
"hwModelSlug": "CROWPANEL",
|
||||
"platformioTarget": "elecrow-adv-35-tft",
|
||||
"architecture": "esp32-s3",
|
||||
"activelySupported": true,
|
||||
"supportLevel": 1,
|
||||
"displayName": "Crowpanel Adv 3.5 TFT",
|
||||
"tags": [
|
||||
"Elecrow"
|
||||
],
|
||||
"requiresDfu": true,
|
||||
"images": [
|
||||
"crowpanel_3_5.svg"
|
||||
],
|
||||
"partitionScheme": "16MB",
|
||||
"hasMui": true
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ struct Connect: View {
|
|||
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
||||
}
|
||||
Button {
|
||||
if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!, adminIndex: node!.myInfo!.adminIndex) {
|
||||
if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!) {
|
||||
Logger.mesh.error("Shutdown Failed")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ struct ChannelList: View {
|
|||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
if channel.name?.isEmpty ?? false {
|
||||
if channel.role == 1 {
|
||||
Text(String("PrimaryChannel").camelCaseToWords())
|
||||
|
|
|
|||
|
|
@ -180,24 +180,24 @@ struct ChannelMessageList: View {
|
|||
}
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.onFirstAppear {
|
||||
// Find first unread message
|
||||
if let firstUnreadMessageId = channel.allPrivateMessages.first(where: { !$0.read })?.messageId {
|
||||
if channel.unreadMessages == 0 {
|
||||
withAnimation {
|
||||
scrollView.scrollTo(firstUnreadMessageId, anchor: .top)
|
||||
showScrollToBottomButton = true
|
||||
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
||||
hasReachedBottom = true
|
||||
}
|
||||
} else {
|
||||
// If no unread messages, scroll to bottom
|
||||
withAnimation {
|
||||
scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom)
|
||||
hasReachedBottom = true
|
||||
if let firstUnreadMessageId = channel.allPrivateMessages.first(where: { !$0.read })?.messageId {
|
||||
withAnimation {
|
||||
scrollView.scrollTo(firstUnreadMessageId, anchor: .top)
|
||||
showScrollToBottomButton = true
|
||||
}
|
||||
}
|
||||
}
|
||||
gotFirstUnreadMessage = true
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { _ in
|
||||
withAnimation {
|
||||
scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom)
|
||||
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
||||
hasReachedBottom = true
|
||||
showScrollToBottomButton = false
|
||||
}
|
||||
|
|
@ -205,7 +205,7 @@ struct ChannelMessageList: View {
|
|||
.onChange(of: channel.allPrivateMessages) {
|
||||
if hasReachedBottom {
|
||||
withAnimation {
|
||||
scrollView.scrollTo(channel.allPrivateMessages.last?.messageId ?? 0, anchor: .bottom)
|
||||
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
||||
}
|
||||
} else {
|
||||
showScrollToBottomButton = true
|
||||
|
|
|
|||
|
|
@ -167,24 +167,24 @@ struct UserMessageList: View {
|
|||
}
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.onFirstAppear {
|
||||
// Find first unread message
|
||||
if let firstUnreadMessageId = user.messageList.first(where: { !$0.read })?.messageId {
|
||||
if user.unreadMessages == 0 {
|
||||
withAnimation {
|
||||
scrollView.scrollTo(firstUnreadMessageId, anchor: .top)
|
||||
showScrollToBottomButton = true
|
||||
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
||||
hasReachedBottom = true
|
||||
}
|
||||
} else {
|
||||
// If no unread messages, scroll to bottom
|
||||
withAnimation {
|
||||
scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom)
|
||||
hasReachedBottom = true
|
||||
if let firstUnreadMessageId = user.messageList.first(where: { !$0.read })?.messageId {
|
||||
withAnimation {
|
||||
scrollView.scrollTo(firstUnreadMessageId, anchor: .top)
|
||||
showScrollToBottomButton = true
|
||||
}
|
||||
}
|
||||
}
|
||||
gotFirstUnreadMessage = true
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { _ in
|
||||
withAnimation {
|
||||
scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom)
|
||||
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
||||
hasReachedBottom = true
|
||||
showScrollToBottomButton = false
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ struct UserMessageList: View {
|
|||
.onChange(of: user.messageList) {
|
||||
if hasReachedBottom {
|
||||
withAnimation {
|
||||
scrollView.scrollTo(user.messageList.last?.messageId ?? 0, anchor: .bottom)
|
||||
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
||||
}
|
||||
} else {
|
||||
showScrollToBottomButton = true
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ struct WaypointForm: View {
|
|||
@State private var lockedTo: Int64 = 0
|
||||
@State private var detents: Set<PresentationDetent> = [.medium, .fraction(0.85)]
|
||||
@State private var selectedDetent: PresentationDetent = .medium
|
||||
@State private var waypointFailedAlert: Bool = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
|
|
@ -47,7 +48,19 @@ struct WaypointForm: View {
|
|||
.textSelection(.enabled)
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
|
||||
}
|
||||
Button {
|
||||
let currentLoc = LocationsHandler.currentLocation
|
||||
waypoint.coordinate.longitude = currentLoc.longitude
|
||||
waypoint.coordinate.latitude = currentLoc.latitude
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Use my Location")
|
||||
Image(systemName: "location")
|
||||
}
|
||||
}
|
||||
.accessibilityLabel("Set to current location")
|
||||
HStack {
|
||||
if waypoint.coordinate.latitude != 0 && waypoint.coordinate.longitude != 0 {
|
||||
DistanceText(meters: distance)
|
||||
|
|
@ -72,6 +85,7 @@ struct WaypointForm: View {
|
|||
name = String(name.dropLast())
|
||||
totalBytes = name.utf8.count
|
||||
}
|
||||
waypoint.name = name.count > 0 ? name : "Dropped Pin"
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
|
|
@ -167,8 +181,8 @@ struct WaypointForm: View {
|
|||
if bleManager.sendWaypoint(waypoint: newWaypoint) {
|
||||
dismiss()
|
||||
} else {
|
||||
dismiss()
|
||||
Logger.mesh.warning("Send waypoint failed")
|
||||
waypointFailedAlert = true
|
||||
}
|
||||
} else {
|
||||
Logger.mesh.warning("Send waypoint failed, node not connected")
|
||||
|
|
@ -233,8 +247,8 @@ struct WaypointForm: View {
|
|||
}
|
||||
dismiss()
|
||||
} else {
|
||||
dismiss()
|
||||
Logger.mesh.warning("Send waypoint failed")
|
||||
waypointFailedAlert = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -256,8 +270,8 @@ struct WaypointForm: View {
|
|||
Text(waypoint.name ?? "?")
|
||||
.font(.largeTitle)
|
||||
Spacer()
|
||||
if waypoint.locked > 0 {
|
||||
Image(systemName: "lock.fill" )
|
||||
if waypoint.locked > 0 && waypoint.locked != UInt32(BLEManager.shared.connectedPeripheral?.num ?? 0) {
|
||||
Image(systemName: "lock.fill")
|
||||
.font(.largeTitle)
|
||||
} else {
|
||||
Button {
|
||||
|
|
@ -368,6 +382,17 @@ struct WaypointForm: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.alert("Waypoint Failed to Send", isPresented: $waypointFailedAlert) {
|
||||
Button("OK", role: .cancel) {
|
||||
bleManager.context.delete(waypoint)
|
||||
do {
|
||||
try bleManager.context.save()
|
||||
} catch {
|
||||
bleManager.context.rollback()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if waypoint.id == 0 {
|
||||
// New, unsent waypoint created by the user: delete it
|
||||
|
|
|
|||
|
|
@ -516,7 +516,6 @@ struct NodeDetail: View {
|
|||
let adminMessageId = bleManager.requestDeviceMetadata(
|
||||
fromUser: connectedNode.user!,
|
||||
toUser: node.user!,
|
||||
adminIndex: connectedNode.myInfo!.adminIndex,
|
||||
context: context
|
||||
)
|
||||
if adminMessageId > 0 {
|
||||
|
|
@ -543,8 +542,7 @@ struct NodeDetail: View {
|
|||
Button("Shutdown Node?", role: .destructive) {
|
||||
if !bleManager.sendShutdown(
|
||||
fromUser: connectedNode.user!,
|
||||
toUser: node.user!,
|
||||
adminIndex: connectedNode.myInfo!.adminIndex
|
||||
toUser: node.user!
|
||||
) {
|
||||
Logger.mesh.warning("Shutdown Failed")
|
||||
}
|
||||
|
|
@ -566,8 +564,7 @@ struct NodeDetail: View {
|
|||
Button("Reboot node?", role: .destructive) {
|
||||
if !bleManager.sendReboot(
|
||||
fromUser: connectedNode.user!,
|
||||
toUser: node.user!,
|
||||
adminIndex: connectedNode.myInfo!.adminIndex
|
||||
toUser: node.user!
|
||||
) {
|
||||
Logger.mesh.warning("Reboot Failed")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ struct AboutMeshtastic: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
Link("Help with App Development", destination: URL(string: "https://github.com/meshtastic/Meshtastic-Apple")!)
|
||||
Link("Sponsor App Development", destination: URL(string: "https://github.com/sponsors/garthvh")!)
|
||||
.font(.title2)
|
||||
Link("GitHub Repository", destination: URL(string: "https://github.com/meshtastic/Meshtastic-Apple")!)
|
||||
.font(.title2)
|
||||
Button("Review the app") {
|
||||
if let scene = UIApplication.shared.connectedScenes
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ struct BluetoothConfig: View {
|
|||
bc.enabled = enabled
|
||||
bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin
|
||||
bc.fixedPin = UInt32(fixedPin) ?? 123456
|
||||
let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -111,7 +111,7 @@ struct BluetoothConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.bluetoothConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired bluetooth config requesting via PKI admin")
|
||||
_ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ struct DeviceConfig: View {
|
|||
dc.disableTripleClick = !tripleClickAsAdHocPing
|
||||
dc.tzdef = tzdef
|
||||
dc.ledHeartbeatDisabled = !ledHeartbeatEnabled
|
||||
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -278,7 +278,7 @@ struct DeviceConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.deviceConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired device config requesting via PKI admin")
|
||||
_ = bleManager.requestDeviceConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestDeviceConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
if node.deviceConfig == nil {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ struct DisplayConfig: View {
|
|||
@State var oledType = 0
|
||||
@State var displayMode = 0
|
||||
@State var units = 0
|
||||
@State var use12HourClock = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
|
|
@ -74,6 +75,11 @@ struct DisplayConfig: View {
|
|||
.font(.callout)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Toggle(isOn: $use12HourClock) {
|
||||
Label("12 Hour Clock", systemImage: "clock")
|
||||
Text("Sets the screen clock format to 12-hour.")
|
||||
}
|
||||
.tint(Color.accentColor)
|
||||
}
|
||||
Section(header: Text("Timing & Format")) {
|
||||
VStack(alignment: .leading) {
|
||||
|
|
@ -141,8 +147,9 @@ struct DisplayConfig: View {
|
|||
dc.oled = OledTypes(rawValue: oledType)!.protoEnumValue()
|
||||
dc.displaymode = DisplayModes(rawValue: displayMode)!.protoEnumValue()
|
||||
dc.units = Units(rawValue: units)!.protoEnumValue()
|
||||
dc.use12HClock = use12HourClock
|
||||
|
||||
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -174,7 +181,7 @@ struct DisplayConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.displayConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired display config requesting via PKI admin")
|
||||
_ = bleManager.requestDisplayConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestDisplayConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
@ -211,6 +218,9 @@ struct DisplayConfig: View {
|
|||
.onChange(of: units) { oldUnits, newUnits in
|
||||
if oldUnits != newUnits && newUnits != node?.displayConfig?.units ?? -1 { hasChanges = true }
|
||||
}
|
||||
.onChange(of: use12HourClock) { oldUse12HourClock, newUse12HourClock in
|
||||
if oldUse12HourClock != newUse12HourClock && newUse12HourClock != node?.displayConfig?.use12HClock { hasChanges = true }
|
||||
}
|
||||
}
|
||||
func setDisplayValues() {
|
||||
self.gpsFormat = Int(node?.displayConfig?.gpsFormat ?? 0)
|
||||
|
|
@ -222,6 +232,7 @@ struct DisplayConfig: View {
|
|||
self.oledType = Int(node?.displayConfig?.oledType ?? 0)
|
||||
self.displayMode = Int(node?.displayConfig?.displayMode ?? 0)
|
||||
self.units = Int(node?.displayConfig?.units ?? 0)
|
||||
self.hasChanges = false
|
||||
self.use12HourClock = node?.displayConfig?.use12HClock ?? false
|
||||
self.hasChanges = node?.displayConfig?.use12HClock ?? false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ struct LoRaConfig: View {
|
|||
if connectedNode?.num ?? -1 == node?.user?.num ?? 0 {
|
||||
UserDefaults.modemPreset = modemPreset
|
||||
}
|
||||
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -250,7 +250,7 @@ struct LoRaConfig: View {
|
|||
if expiration < Date() || node.loRaConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired lora config requesting via PKI admin")
|
||||
if connectedNode.user != nil && node.user != nil {
|
||||
_ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ struct AmbientLightingConfig: View {
|
|||
al.blue = UInt32(components.blue * 255)
|
||||
}
|
||||
|
||||
let adminMessageId = bleManager.saveAmbientLightingModuleConfig(config: al, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveAmbientLightingModuleConfig(config: al, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -96,7 +96,7 @@ struct AmbientLightingConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.ambientLightingConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired ambient lighting module config requesting via PKI admin")
|
||||
_ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ struct CannedMessagesConfig: View {
|
|||
cmc.inputbrokerEventCw = InputEventChars(rawValue: inputbrokerEventCw)!.protoEnumValue()
|
||||
cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue()
|
||||
cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue()
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -211,7 +211,7 @@ struct CannedMessagesConfig: View {
|
|||
}
|
||||
}
|
||||
if hasMessagesChanges {
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messages, fromUser: node!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveCannedMessageModuleMessages(messages: messages, fromUser: node!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -244,7 +244,7 @@ struct CannedMessagesConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.cannedMessageConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired canned messages module config requesting via PKI admin")
|
||||
_ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ struct DetectionSensorConfig: View {
|
|||
dsc.usePullup = self.usePullup
|
||||
dsc.minimumBroadcastSecs = UInt32(self.minimumBroadcastSecs)
|
||||
dsc.stateBroadcastSecs = UInt32(self.stateBroadcastSecs)
|
||||
let adminMessageId = bleManager.saveDetectionSensorModuleConfig(config: dsc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveDetectionSensorModuleConfig(config: dsc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -202,7 +202,7 @@ struct DetectionSensorConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.detectionSensorConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired detection sensor module config requesting via PKI admin")
|
||||
_ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ struct ExternalNotificationConfig: View {
|
|||
enc.outputMs = UInt32(outputMilliseconds)
|
||||
enc.usePwm = usePWM
|
||||
enc.useI2SAsBuzzer = useI2SAsBuzzer
|
||||
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -210,7 +210,7 @@ struct ExternalNotificationConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.externalNotificationConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired external notificaiton module config requesting via PKI admin")
|
||||
_ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ struct MQTTConfig: View {
|
|||
mqtt.mapReportingEnabled = self.mapReportingEnabled
|
||||
mqtt.mapReportSettings.positionPrecision = UInt32(self.mapPositionPrecision)
|
||||
mqtt.mapReportSettings.publishIntervalSecs = UInt32(self.mapPublishIntervalSecs)
|
||||
let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -360,7 +360,7 @@ struct MQTTConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.mqttConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired mqtt module config requesting via PKI admin")
|
||||
_ = bleManager.requestMqttModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestMqttModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ struct PaxCounterConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.paxCounterConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired pax counter module config requesting via PKI admin")
|
||||
_ = bleManager.requestPaxCounterModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestPaxCounterModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
@ -100,8 +100,7 @@ struct PaxCounterConfig: View {
|
|||
let adminMessageId = bleManager.savePaxcounterModuleConfig(
|
||||
config: config,
|
||||
fromUser: fromUser,
|
||||
toUser: toUser,
|
||||
adminIndex: connectedNode.myInfo?.adminIndex ?? 0
|
||||
toUser: toUser
|
||||
)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ struct RangeTestConfig: View {
|
|||
rtc.enabled = enabled
|
||||
rtc.save = save
|
||||
rtc.sender = UInt32(sender)
|
||||
let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -92,7 +92,7 @@ struct RangeTestConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.rangeTestConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired range test module config requesting via PKI admin")
|
||||
_ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ struct RtttlConfig: View {
|
|||
SaveConfigButton(node: node, hasChanges: $hasChanges) {
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil {
|
||||
let adminMessageId = bleManager.saveRtttlConfig(ringtone: ringtone.trimmingCharacters(in: .whitespacesAndNewlines), fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveRtttlConfig(ringtone: ringtone.trimmingCharacters(in: .whitespacesAndNewlines), fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -83,7 +83,7 @@ struct RtttlConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.rtttlConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired ringtone module config requesting via PKI admin")
|
||||
_ = bleManager.requestRtttlConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestRtttlConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ struct SerialConfig: View {
|
|||
sc.overrideConsoleSerialPort = overrideConsoleSerialPort
|
||||
sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue()
|
||||
|
||||
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -147,7 +147,7 @@ struct SerialConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.serialConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired serial module config requesting via PKI admin")
|
||||
_ = bleManager.requestSerialModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestSerialModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ struct StoreForwardConfig: View {
|
|||
sfc.records = UInt32(self.records)
|
||||
sfc.historyReturnMax = UInt32(self.historyReturnMax)
|
||||
sfc.historyReturnWindow = UInt32(self.historyReturnWindow)
|
||||
let adminMessageId = bleManager.saveStoreForwardModuleConfig(config: sfc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveStoreForwardModuleConfig(config: sfc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -148,7 +148,7 @@ struct StoreForwardConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.storeForwardConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired store & forward module config requesting via PKI admin")
|
||||
_ = bleManager.requestStoreAndForwardModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestStoreAndForwardModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ struct TelemetryConfig: View {
|
|||
tc.powerMeasurementEnabled = powerMeasurementEnabled
|
||||
tc.powerUpdateInterval = UInt32(powerUpdateInterval)
|
||||
tc.powerScreenEnabled = powerScreenEnabled
|
||||
let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -145,7 +145,7 @@ struct TelemetryConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.telemetryConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired telemetry module config requesting via PKI admin")
|
||||
_ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ struct NetworkConfig: View {
|
|||
network.enabledProtocols = self.udpEnabled ? UInt32(Config.NetworkConfig.ProtocolFlags.udpBroadcast.rawValue) : UInt32(Config.NetworkConfig.ProtocolFlags.noBroadcast.rawValue)
|
||||
// network.addressMode = Config.NetworkConfig.AddressMode.dhcp
|
||||
|
||||
let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -140,7 +140,7 @@ struct NetworkConfig: View {
|
|||
Logger.mesh.info("empty network config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestNetworkConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestNetworkConfig(fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ struct NetworkConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.networkConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired network config requesting via PKI admin")
|
||||
_ = bleManager.requestNetworkConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestNetworkConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ struct PositionConfig: View {
|
|||
if includeSpeed { pf.insert(.Speed) }
|
||||
if includeHeading { pf.insert(.Heading) }
|
||||
pc.positionFlags = UInt32(pf.rawValue)
|
||||
let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: connectedNode!.user!, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
// Disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
|
@ -412,7 +412,7 @@ struct PositionConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.positionConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired position config requesting via PKI admin")
|
||||
_ = bleManager.requestPositionConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestPositionConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ struct PowerConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.powerConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired power config requesting via PKI admin")
|
||||
_ = bleManager.requestPowerConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestPowerConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
|
|
@ -194,8 +194,7 @@ struct PowerConfig: View {
|
|||
let adminMessageId = bleManager.savePowerConfig(
|
||||
config: config,
|
||||
fromUser: fromUser,
|
||||
toUser: toUser,
|
||||
adminIndex: connectedNode.myInfo?.adminIndex ?? 0
|
||||
toUser: toUser
|
||||
)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ struct SecurityConfig: View {
|
|||
@State var isManaged = false
|
||||
@State var serialEnabled = false
|
||||
@State var debugLogApiEnabled = false
|
||||
@State var adminChannelEnabled = false
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
|
@ -65,6 +64,21 @@ struct SecurityConfig: View {
|
|||
Text("Used to create a shared key with a remote device.")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(idiom == .phone ? .caption : .callout)
|
||||
HStack(alignment: .firstTextBaseline) {
|
||||
Label("Regenerate Private Key", systemImage: "arrow.clockwise.circle")
|
||||
Spacer()
|
||||
Button {
|
||||
if let keyBytes = generatePrivateKey(count: 32) {
|
||||
privateKey = keyBytes.base64EncodedString()
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "lock.rotation")
|
||||
.font(.title)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.small)
|
||||
}
|
||||
Divider()
|
||||
Label("Primary Admin Key", systemImage: "key.viewfinder")
|
||||
SecureInput("Primary Admin Key", text: $adminKey, isValid: $hasValidAdminKey)
|
||||
|
|
@ -109,19 +123,14 @@ struct SecurityConfig: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
Section(header: Text("Administration")) {
|
||||
if adminKey.length > 0 || adminChannelEnabled {
|
||||
if adminKey.length > 0 || UserDefaults.enableAdministration {
|
||||
Section(header: Text("Administration")) {
|
||||
Toggle(isOn: $isManaged) {
|
||||
Label("Managed Device", systemImage: "gearshape.arrow.triangle.2.circlepath")
|
||||
Text("Device is managed by a mesh administrator, the user is unable to access any of the device settings.")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
Toggle(isOn: $adminChannelEnabled) {
|
||||
Label("Legacy Administration", systemImage: "lock.slash")
|
||||
Text("Allow incoming device control over the insecure legacy admin channel.")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -143,17 +152,14 @@ struct SecurityConfig: View {
|
|||
.onChange(of: debugLogApiEnabled) { _, newDebugLogApiEnabled in
|
||||
if newDebugLogApiEnabled != node?.securityConfig?.debugLogApiEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: adminChannelEnabled) { _, newAdminChannelEnabled in
|
||||
if newAdminChannelEnabled != node?.securityConfig?.adminChannelEnabled { hasChanges = true }
|
||||
}
|
||||
.onChange(of: privateKey) {
|
||||
.onChange(of: privateKey) { _, key in
|
||||
let tempKey = Data(base64Encoded: privateKey) ?? Data()
|
||||
if tempKey.count == 32 {
|
||||
hasValidPrivateKey = true
|
||||
} else {
|
||||
hasValidPrivateKey = false
|
||||
}
|
||||
hasChanges = true
|
||||
if key != node?.securityConfig?.privateKey?.base64EncodedString() ?? "" && hasValidPrivateKey { hasChanges = true }
|
||||
}
|
||||
.onChange(of: adminKey) { _, key in
|
||||
let tempKey = Data(base64Encoded: key) ?? Data()
|
||||
|
|
@ -164,7 +170,7 @@ struct SecurityConfig: View {
|
|||
} else {
|
||||
hasValidAdminKey = false
|
||||
}
|
||||
hasChanges = true
|
||||
if key != node?.securityConfig?.adminKey?.base64EncodedString() ?? "" && hasValidAdminKey { hasChanges = true }
|
||||
}
|
||||
.onChange(of: adminKey2) { _, key in
|
||||
let tempKey = Data(base64Encoded: key) ?? Data()
|
||||
|
|
@ -175,7 +181,7 @@ struct SecurityConfig: View {
|
|||
} else {
|
||||
hasValidAdminKey2 = false
|
||||
}
|
||||
hasChanges = true
|
||||
if key != node?.securityConfig?.adminKey2?.base64EncodedString() ?? "" && hasValidAdminKey2 { hasChanges = true }
|
||||
}
|
||||
.onChange(of: adminKey3) { _, key in
|
||||
let tempKey = Data(base64Encoded: key) ?? Data()
|
||||
|
|
@ -186,10 +192,10 @@ struct SecurityConfig: View {
|
|||
} else {
|
||||
hasValidAdminKey3 = false
|
||||
}
|
||||
hasChanges = true
|
||||
if key != node?.securityConfig?.adminKey3?.base64EncodedString() ?? "" && hasValidAdminKey3 { hasChanges = true }
|
||||
}
|
||||
.onFirstAppear {
|
||||
// Need to request a DeviceConfig from the remote node before allowing changes
|
||||
// Need to request a SecurityConfig from the remote node before allowing changes
|
||||
if let connectedPeripheral = bleManager.connectedPeripheral, let node {
|
||||
let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context)
|
||||
if let connectedNode {
|
||||
|
|
@ -199,7 +205,7 @@ struct SecurityConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.securityConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired security config requesting via PKI admin")
|
||||
_ = bleManager.requestSecurityConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
_ = bleManager.requestSecurityConfig(fromUser: connectedNode.user!, toUser: node.user!)
|
||||
}
|
||||
} else {
|
||||
if node.deviceConfig == nil {
|
||||
|
|
@ -231,18 +237,26 @@ struct SecurityConfig: View {
|
|||
config.isManaged = isManaged
|
||||
config.serialEnabled = serialEnabled
|
||||
config.debugLogApiEnabled = debugLogApiEnabled
|
||||
config.adminChannelEnabled = adminChannelEnabled
|
||||
|
||||
let reboot = node?.securityConfig?.privateKey?.base64EncodedString() ?? "" != privateKey
|
||||
|
||||
let adminMessageId = bleManager.saveSecurityConfig(
|
||||
config: config,
|
||||
fromUser: fromUser,
|
||||
toUser: toUser,
|
||||
adminIndex: connectedNode.myInfo?.adminIndex ?? 0
|
||||
toUser: toUser
|
||||
)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
if reboot {
|
||||
if !bleManager.sendReboot(
|
||||
fromUser: fromUser,
|
||||
toUser: toUser
|
||||
) {
|
||||
Logger.mesh.warning("Reboot Failed")
|
||||
}
|
||||
}
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
|
@ -257,7 +271,24 @@ struct SecurityConfig: View {
|
|||
self.isManaged = node?.securityConfig?.isManaged ?? false
|
||||
self.serialEnabled = node?.securityConfig?.serialEnabled ?? false
|
||||
self.debugLogApiEnabled = node?.securityConfig?.debugLogApiEnabled ?? false
|
||||
self.adminChannelEnabled = node?.securityConfig?.adminChannelEnabled ?? false
|
||||
self.hasChanges = false
|
||||
}
|
||||
|
||||
func generatePrivateKey(count: Int) -> Data? {
|
||||
var randomBytes = Data(count: count)
|
||||
let status = randomBytes.withUnsafeMutableBytes { (mutableBytes: UnsafeMutableRawBufferPointer) -> Int32 in
|
||||
guard let pointer = mutableBytes.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1 // Indicate an error
|
||||
}
|
||||
return SecRandomCopyBytes(kSecRandomDefault, count, pointer)
|
||||
}
|
||||
|
||||
if status == errSecSuccess {
|
||||
return randomBytes
|
||||
} else {
|
||||
// Handle error, perhaps by logging or throwing an exception
|
||||
print("Error generating random bytes: \(status)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ struct Firmware: View {
|
|||
Button {
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context)
|
||||
if connectedNode != nil {
|
||||
if !bleManager.sendRebootOta(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode!.myInfo!.adminIndex) {
|
||||
if !bleManager.sendRebootOta(fromUser: connectedNode!.user!, toUser: node!.user!) {
|
||||
Logger.mesh.error("Reboot Failed")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -432,7 +432,7 @@ struct Settings: View {
|
|||
let connectedNode = nodes.first(where: { $0.num == preferredNodeNum })
|
||||
preferredNodeNum = Int(connectedNode?.num ?? 0)// Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? 0 : 0)
|
||||
if connectedNode != nil && connectedNode?.user != nil && connectedNode?.myInfo != nil && node?.user != nil {// && node?.metadata == nil {
|
||||
let adminMessageId = bleManager.requestDeviceMetadata(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode!.myInfo!.adminIndex, context: context)
|
||||
let adminMessageId = bleManager.requestDeviceMetadata(fromUser: connectedNode!.user!, toUser: node!.user!, context: context)
|
||||
if adminMessageId > 0 {
|
||||
Logger.mesh.info("Sent node metadata request from node details")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ struct ShareChannels: View {
|
|||
.labelsHidden()
|
||||
Text(((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary").camelCaseToWords())
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -96,7 +96,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -109,7 +109,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -122,7 +122,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -135,7 +135,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -148,7 +148,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -161,7 +161,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
@ -174,7 +174,7 @@ struct ShareChannels: View {
|
|||
.disabled(channel.role == 1)
|
||||
Text(((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)").camelCaseToWords()).fixedSize()
|
||||
if channel.psk?.hexDescription.count ?? 0 < 3 {
|
||||
Image(systemName: "lock.slash")
|
||||
Image(systemName: "lock.slash.fill")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ struct UserConfig: View {
|
|||
u.shortName = shortName
|
||||
u.longName = longName
|
||||
u.isUnmessagable = isUnmessagable
|
||||
let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedUser, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedUser, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
hasChanges = false
|
||||
goBack()
|
||||
|
|
@ -188,7 +188,7 @@ struct UserConfig: View {
|
|||
ham.callSign = longName
|
||||
ham.txPower = Int32(txPower)
|
||||
ham.frequency = overrideFrequency
|
||||
let adminMessageId = bleManager.saveLicensedUser(ham: ham, fromUser: connectedUser, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let adminMessageId = bleManager.saveLicensedUser(ham: ham, fromUser: connectedUser, toUser: node!.user!)
|
||||
if adminMessageId > 0 {
|
||||
hasChanges = false
|
||||
goBack()
|
||||
|
|
|
|||
|
|
@ -292,6 +292,17 @@ public struct AdminMessage: @unchecked Sendable {
|
|||
set {payloadVariant = .removeBackupPreferences(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Send an input event to the node.
|
||||
/// This is used to trigger physical input events like button presses, touch events, etc.
|
||||
public var sendInputEvent: AdminMessage.InputEvent {
|
||||
get {
|
||||
if case .sendInputEvent(let v)? = payloadVariant {return v}
|
||||
return AdminMessage.InputEvent()
|
||||
}
|
||||
set {payloadVariant = .sendInputEvent(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the owner for this node
|
||||
public var setOwner: User {
|
||||
|
|
@ -663,6 +674,10 @@ public struct AdminMessage: @unchecked Sendable {
|
|||
/// Remove backups of the node's preferences
|
||||
case removeBackupPreferences(AdminMessage.BackupLocation)
|
||||
///
|
||||
/// Send an input event to the node.
|
||||
/// This is used to trigger physical input events like button presses, touch events, etc.
|
||||
case sendInputEvent(AdminMessage.InputEvent)
|
||||
///
|
||||
/// Set the owner for this node
|
||||
case setOwner(User)
|
||||
///
|
||||
|
|
@ -1014,6 +1029,34 @@ public struct AdminMessage: @unchecked Sendable {
|
|||
|
||||
}
|
||||
|
||||
///
|
||||
/// Input event message to be sent to the node.
|
||||
public struct InputEvent: Sendable {
|
||||
// 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.
|
||||
|
||||
///
|
||||
/// The input event code
|
||||
public var eventCode: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Keyboard character code
|
||||
public var kbChar: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The touch X coordinate
|
||||
public var touchX: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The touch Y coordinate
|
||||
public var touchY: UInt32 = 0
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
|
|
@ -1083,6 +1126,10 @@ public struct SharedContact: Sendable {
|
|||
/// Clears the value of `user`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearUser() {self._user = nil}
|
||||
|
||||
///
|
||||
/// Add this contact to the blocked / ignored list
|
||||
public var shouldIgnore: Bool = false
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
|
@ -1215,6 +1262,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
24: .standard(proto: "backup_preferences"),
|
||||
25: .standard(proto: "restore_preferences"),
|
||||
26: .standard(proto: "remove_backup_preferences"),
|
||||
27: .standard(proto: "send_input_event"),
|
||||
32: .standard(proto: "set_owner"),
|
||||
33: .standard(proto: "set_channel"),
|
||||
34: .standard(proto: "set_config"),
|
||||
|
|
@ -1491,6 +1539,19 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
self.payloadVariant = .removeBackupPreferences(v)
|
||||
}
|
||||
}()
|
||||
case 27: try {
|
||||
var v: AdminMessage.InputEvent?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .sendInputEvent(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .sendInputEvent(v)
|
||||
}
|
||||
}()
|
||||
case 32: try {
|
||||
var v: User?
|
||||
var hadOneofValue = false
|
||||
|
|
@ -1872,6 +1933,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
guard case .removeBackupPreferences(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularEnumField(value: v, fieldNumber: 26)
|
||||
}()
|
||||
case .sendInputEvent?: try {
|
||||
guard case .sendInputEvent(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 27)
|
||||
}()
|
||||
case .setOwner?: try {
|
||||
guard case .setOwner(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 32)
|
||||
|
|
@ -2040,6 +2105,56 @@ extension AdminMessage.BackupLocation: SwiftProtobuf._ProtoNameProviding {
|
|||
]
|
||||
}
|
||||
|
||||
extension AdminMessage.InputEvent: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = AdminMessage.protoMessageName + ".InputEvent"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "event_code"),
|
||||
2: .standard(proto: "kb_char"),
|
||||
3: .standard(proto: "touch_x"),
|
||||
4: .standard(proto: "touch_y"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.eventCode) }()
|
||||
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.kbChar) }()
|
||||
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.touchX) }()
|
||||
case 4: try { try decoder.decodeSingularUInt32Field(value: &self.touchY) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.eventCode != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.eventCode, fieldNumber: 1)
|
||||
}
|
||||
if self.kbChar != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.kbChar, fieldNumber: 2)
|
||||
}
|
||||
if self.touchX != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.touchX, fieldNumber: 3)
|
||||
}
|
||||
if self.touchY != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.touchY, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: AdminMessage.InputEvent, rhs: AdminMessage.InputEvent) -> Bool {
|
||||
if lhs.eventCode != rhs.eventCode {return false}
|
||||
if lhs.kbChar != rhs.kbChar {return false}
|
||||
if lhs.touchX != rhs.touchX {return false}
|
||||
if lhs.touchY != rhs.touchY {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".HamParameters"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
@ -2127,6 +2242,7 @@ extension SharedContact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "node_num"),
|
||||
2: .same(proto: "user"),
|
||||
3: .standard(proto: "should_ignore"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2137,6 +2253,7 @@ extension SharedContact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.nodeNum) }()
|
||||
case 2: try { try decoder.decodeSingularMessageField(value: &self._user) }()
|
||||
case 3: try { try decoder.decodeSingularBoolField(value: &self.shouldIgnore) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2153,12 +2270,16 @@ extension SharedContact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
try { if let v = self._user {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
if self.shouldIgnore != false {
|
||||
try visitor.visitSingularBoolField(value: self.shouldIgnore, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: SharedContact, rhs: SharedContact) -> Bool {
|
||||
if lhs.nodeNum != rhs.nodeNum {return false}
|
||||
if lhs._user != rhs._user {return false}
|
||||
if lhs.shouldIgnore != rhs.shouldIgnore {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,6 +189,11 @@ public struct Config: Sendable {
|
|||
/// If true, disable the default blinking LED (LED_PIN) behavior on the device
|
||||
public var ledHeartbeatDisabled: Bool = false
|
||||
|
||||
///
|
||||
/// Controls buzzer behavior for audio feedback
|
||||
/// Defaults to ENABLED
|
||||
public var buzzerMode: Config.DeviceConfig.BuzzerMode = .allEnabled
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -406,6 +411,67 @@ public struct Config: Sendable {
|
|||
|
||||
}
|
||||
|
||||
///
|
||||
/// Defines buzzer behavior for audio feedback
|
||||
public enum BuzzerMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// Default behavior.
|
||||
/// Buzzer is enabled for all audio feedback including button presses and alerts.
|
||||
case allEnabled // = 0
|
||||
|
||||
///
|
||||
/// Disabled.
|
||||
/// All buzzer audio feedback is disabled.
|
||||
case disabled // = 1
|
||||
|
||||
///
|
||||
/// Notifications Only.
|
||||
/// Buzzer is enabled only for notifications and alerts, but not for button presses.
|
||||
/// External notification config determines the specifics of the notification behavior.
|
||||
case notificationsOnly // = 2
|
||||
|
||||
///
|
||||
/// Non-notification system buzzer tones only.
|
||||
/// Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts.
|
||||
case systemOnly // = 3
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
self = .allEnabled
|
||||
}
|
||||
|
||||
public init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 0: self = .allEnabled
|
||||
case 1: self = .disabled
|
||||
case 2: self = .notificationsOnly
|
||||
case 3: self = .systemOnly
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
public var rawValue: Int {
|
||||
switch self {
|
||||
case .allEnabled: return 0
|
||||
case .disabled: return 1
|
||||
case .notificationsOnly: return 2
|
||||
case .systemOnly: return 3
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DeviceConfig.BuzzerMode] = [
|
||||
.allEnabled,
|
||||
.disabled,
|
||||
.notificationsOnly,
|
||||
.systemOnly,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
|
|
@ -2063,6 +2129,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
10: .standard(proto: "disable_triple_click"),
|
||||
11: .same(proto: "tzdef"),
|
||||
12: .standard(proto: "led_heartbeat_disabled"),
|
||||
13: .standard(proto: "buzzer_mode"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2082,6 +2149,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
case 10: try { try decoder.decodeSingularBoolField(value: &self.disableTripleClick) }()
|
||||
case 11: try { try decoder.decodeSingularStringField(value: &self.tzdef) }()
|
||||
case 12: try { try decoder.decodeSingularBoolField(value: &self.ledHeartbeatDisabled) }()
|
||||
case 13: try { try decoder.decodeSingularEnumField(value: &self.buzzerMode) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2121,6 +2189,9 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
if self.ledHeartbeatDisabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.ledHeartbeatDisabled, fieldNumber: 12)
|
||||
}
|
||||
if self.buzzerMode != .allEnabled {
|
||||
try visitor.visitSingularEnumField(value: self.buzzerMode, fieldNumber: 13)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2136,6 +2207,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
if lhs.disableTripleClick != rhs.disableTripleClick {return false}
|
||||
if lhs.tzdef != rhs.tzdef {return false}
|
||||
if lhs.ledHeartbeatDisabled != rhs.ledHeartbeatDisabled {return false}
|
||||
if lhs.buzzerMode != rhs.buzzerMode {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -2169,6 +2241,15 @@ extension Config.DeviceConfig.RebroadcastMode: SwiftProtobuf._ProtoNameProviding
|
|||
]
|
||||
}
|
||||
|
||||
extension Config.DeviceConfig.BuzzerMode: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "ALL_ENABLED"),
|
||||
1: .same(proto: "DISABLED"),
|
||||
2: .same(proto: "NOTIFICATIONS_ONLY"),
|
||||
3: .same(proto: "SYSTEM_ONLY"),
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = Config.protoMessageName + ".PositionConfig"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
|
|||
|
|
@ -141,6 +141,10 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
/// Ukrainian
|
||||
case ukrainian // = 16
|
||||
|
||||
///
|
||||
/// Bulgarian
|
||||
case bulgarian // = 17
|
||||
|
||||
///
|
||||
/// Simplified Chinese (experimental)
|
||||
case simplifiedChinese // = 30
|
||||
|
|
@ -173,6 +177,7 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case 14: self = .norwegian
|
||||
case 15: self = .slovenian
|
||||
case 16: self = .ukrainian
|
||||
case 17: self = .bulgarian
|
||||
case 30: self = .simplifiedChinese
|
||||
case 31: self = .traditionalChinese
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
|
|
@ -198,6 +203,7 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case .norwegian: return 14
|
||||
case .slovenian: return 15
|
||||
case .ukrainian: return 16
|
||||
case .bulgarian: return 17
|
||||
case .simplifiedChinese: return 30
|
||||
case .traditionalChinese: return 31
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
|
|
@ -223,6 +229,7 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
.norwegian,
|
||||
.slovenian,
|
||||
.ukrainian,
|
||||
.bulgarian,
|
||||
.simplifiedChinese,
|
||||
.traditionalChinese,
|
||||
]
|
||||
|
|
@ -502,6 +509,7 @@ extension Language: SwiftProtobuf._ProtoNameProviding {
|
|||
14: .same(proto: "NORWEGIAN"),
|
||||
15: .same(proto: "SLOVENIAN"),
|
||||
16: .same(proto: "UKRAINIAN"),
|
||||
17: .same(proto: "BULGARIAN"),
|
||||
30: .same(proto: "SIMPLIFIED_CHINESE"),
|
||||
31: .same(proto: "TRADITIONAL_CHINESE"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -458,6 +458,18 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
/// Reserved ID for future and past use
|
||||
case qwantzTinyArms // = 101
|
||||
|
||||
///*
|
||||
/// Lilygo T-Deck Pro
|
||||
case tDeckPro // = 102
|
||||
|
||||
///*
|
||||
/// Lilygo TLora Pager
|
||||
case tLoraPager // = 103
|
||||
|
||||
///*
|
||||
/// GAT562 Mesh Trial Tracker
|
||||
case gat562MeshTrialTracker // = 104
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// 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.
|
||||
|
|
@ -573,6 +585,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case 99: self = .seeedWioTrackerL1
|
||||
case 100: self = .seeedWioTrackerL1Eink
|
||||
case 101: self = .qwantzTinyArms
|
||||
case 102: self = .tDeckPro
|
||||
case 103: self = .tLoraPager
|
||||
case 104: self = .gat562MeshTrialTracker
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -682,6 +697,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
case .seeedWioTrackerL1: return 99
|
||||
case .seeedWioTrackerL1Eink: return 100
|
||||
case .qwantzTinyArms: return 101
|
||||
case .tDeckPro: return 102
|
||||
case .tLoraPager: return 103
|
||||
case .gat562MeshTrialTracker: return 104
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -791,6 +809,9 @@ public enum HardwareModel: SwiftProtobuf.Enum, Swift.CaseIterable {
|
|||
.seeedWioTrackerL1,
|
||||
.seeedWioTrackerL1Eink,
|
||||
.qwantzTinyArms,
|
||||
.tDeckPro,
|
||||
.tLoraPager,
|
||||
.gat562MeshTrialTracker,
|
||||
.privateHw,
|
||||
]
|
||||
|
||||
|
|
@ -2991,12 +3012,30 @@ public struct ClientNotification: Sendable {
|
|||
set {payloadVariant = .keyVerificationFinal(newValue)}
|
||||
}
|
||||
|
||||
public var duplicatedPublicKey: DuplicatedPublicKey {
|
||||
get {
|
||||
if case .duplicatedPublicKey(let v)? = payloadVariant {return v}
|
||||
return DuplicatedPublicKey()
|
||||
}
|
||||
set {payloadVariant = .duplicatedPublicKey(newValue)}
|
||||
}
|
||||
|
||||
public var lowEntropyKey: LowEntropyKey {
|
||||
get {
|
||||
if case .lowEntropyKey(let v)? = payloadVariant {return v}
|
||||
return LowEntropyKey()
|
||||
}
|
||||
set {payloadVariant = .lowEntropyKey(newValue)}
|
||||
}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum OneOf_PayloadVariant: Equatable, Sendable {
|
||||
case keyVerificationNumberInform(KeyVerificationNumberInform)
|
||||
case keyVerificationNumberRequest(KeyVerificationNumberRequest)
|
||||
case keyVerificationFinal(KeyVerificationFinal)
|
||||
case duplicatedPublicKey(DuplicatedPublicKey)
|
||||
case lowEntropyKey(LowEntropyKey)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3053,6 +3092,26 @@ public struct KeyVerificationFinal: Sendable {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
public struct DuplicatedPublicKey: Sendable {
|
||||
// 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 struct LowEntropyKey: Sendable {
|
||||
// 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() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// Individual File info for the device
|
||||
public struct FileInfo: Sendable {
|
||||
|
|
@ -3578,6 +3637,9 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
99: .same(proto: "SEEED_WIO_TRACKER_L1"),
|
||||
100: .same(proto: "SEEED_WIO_TRACKER_L1_EINK"),
|
||||
101: .same(proto: "QWANTZ_TINY_ARMS"),
|
||||
102: .same(proto: "T_DECK_PRO"),
|
||||
103: .same(proto: "T_LORA_PAGER"),
|
||||
104: .same(proto: "GAT562_MESH_TRIAL_TRACKER"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
@ -5348,6 +5410,8 @@ extension ClientNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
11: .standard(proto: "key_verification_number_inform"),
|
||||
12: .standard(proto: "key_verification_number_request"),
|
||||
13: .standard(proto: "key_verification_final"),
|
||||
14: .standard(proto: "duplicated_public_key"),
|
||||
15: .standard(proto: "low_entropy_key"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -5399,6 +5463,32 @@ extension ClientNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
self.payloadVariant = .keyVerificationFinal(v)
|
||||
}
|
||||
}()
|
||||
case 14: try {
|
||||
var v: DuplicatedPublicKey?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .duplicatedPublicKey(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .duplicatedPublicKey(v)
|
||||
}
|
||||
}()
|
||||
case 15: try {
|
||||
var v: LowEntropyKey?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .lowEntropyKey(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .lowEntropyKey(v)
|
||||
}
|
||||
}()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -5434,6 +5524,14 @@ extension ClientNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
guard case .keyVerificationFinal(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 13)
|
||||
}()
|
||||
case .duplicatedPublicKey?: try {
|
||||
guard case .duplicatedPublicKey(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 14)
|
||||
}()
|
||||
case .lowEntropyKey?: try {
|
||||
guard case .lowEntropyKey(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 15)
|
||||
}()
|
||||
case nil: break
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
|
|
@ -5582,6 +5680,44 @@ extension KeyVerificationFinal: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
}
|
||||
}
|
||||
|
||||
extension DuplicatedPublicKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".DuplicatedPublicKey"
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
// Load everything into unknown fields
|
||||
while try decoder.nextFieldNumber() != nil {}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: DuplicatedPublicKey, rhs: DuplicatedPublicKey) -> Bool {
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension LowEntropyKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".LowEntropyKey"
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
// Load everything into unknown fields
|
||||
while try decoder.nextFieldNumber() != nil {}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: LowEntropyKey, rhs: LowEntropyKey) -> Bool {
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension FileInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".FileInfo"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue