mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #327 from meshtastic/2.0.22_Working_Changes
2.0.22 Working Changes
This commit is contained in:
commit
a77442a3d0
108 changed files with 2972 additions and 3192 deletions
|
|
@ -9,8 +9,6 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */; };
|
||||
C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; };
|
||||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */; };
|
||||
C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A88B54278B503C00BD810A /* MapViewModule.swift */; };
|
||||
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; };
|
||||
DD1925B728CDA5A400720036 /* CannedMessagesConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */; };
|
||||
DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1925B828CDA93900720036 /* SerialConfigEnums.swift */; };
|
||||
|
|
@ -97,7 +95,7 @@
|
|||
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */; };
|
||||
DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */; };
|
||||
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */; };
|
||||
DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */; };
|
||||
DDB6ABE028B13AC700384BA1 /* DeviceEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABDF28B13AC700384BA1 /* DeviceEnums.swift */; };
|
||||
DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */; };
|
||||
DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */; };
|
||||
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */; };
|
||||
|
|
@ -170,8 +168,6 @@
|
|||
/* Begin PBXFileReference section */
|
||||
A65FA974296876BF00A97686 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = "<group>"; };
|
||||
C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAnnotationView.swift; sourceTree = "<group>"; };
|
||||
C9A88B54278B503C00BD810A /* MapViewModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewModule.swift; sourceTree = "<group>"; };
|
||||
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = "<group>"; };
|
||||
DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfigEnums.swift; sourceTree = "<group>"; };
|
||||
DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -264,7 +260,7 @@
|
|||
DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = "<group>"; };
|
||||
DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothModes.swift; sourceTree = "<group>"; };
|
||||
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceText.swift; sourceTree = "<group>"; };
|
||||
DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRoles.swift; sourceTree = "<group>"; };
|
||||
DDB6ABDF28B13AC700384BA1 /* DeviceEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceEnums.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfigEnums.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayEnums.swift; sourceTree = "<group>"; };
|
||||
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -290,6 +286,7 @@
|
|||
DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeshtasticTests.swift; sourceTree = "<group>"; };
|
||||
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = "<group>"; };
|
||||
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DDDD527729B5B83F0045BC3C /* MeshtasticDataModelV9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV9.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDDE59F429AF163D00490C6C /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DDDE59F829AF163D00490C6C /* WidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetsBundle.swift; sourceTree = "<group>"; };
|
||||
DDDE59FA29AF163D00490C6C /* WidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetsLiveActivity.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -355,8 +352,6 @@
|
|||
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */,
|
||||
DD964FC32974767D007C176F /* MapViewFitExtension.swift */,
|
||||
DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */,
|
||||
C9A88B54278B503C00BD810A /* MapViewModule.swift */,
|
||||
C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */,
|
||||
);
|
||||
path = Custom;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -478,7 +473,7 @@
|
|||
DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */,
|
||||
DD1925B628CDA5A400720036 /* CannedMessagesConfigEnums.swift */,
|
||||
DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */,
|
||||
DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */,
|
||||
DDB6ABDF28B13AC700384BA1 /* DeviceEnums.swift */,
|
||||
DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */,
|
||||
DD5D0A9B2931B9F200F7EA61 /* EthernetModes.swift */,
|
||||
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */,
|
||||
|
|
@ -866,7 +861,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https: //github.com/realm/SwiftLint\"\nfi\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
|
@ -922,7 +917,6 @@
|
|||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
|
||||
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */,
|
||||
DD5E520D298EE33B00D21B61 /* storeforward.pb.swift in Sources */,
|
||||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
|
||||
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
|
|
@ -934,10 +928,9 @@
|
|||
DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */,
|
||||
DDB6ABD928B0A4BA00384BA1 /* BluetoothModes.swift in Sources */,
|
||||
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */,
|
||||
C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */,
|
||||
DD2553592855B52700E55709 /* PositionConfig.swift in Sources */,
|
||||
DD97E96828EFE9A00056DDA4 /* About.swift in Sources */,
|
||||
DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */,
|
||||
DDB6ABE028B13AC700384BA1 /* DeviceEnums.swift in Sources */,
|
||||
DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */,
|
||||
DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */,
|
||||
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
|
||||
|
|
@ -1187,7 +1180,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.0.21;
|
||||
MARKETING_VERSION = 2.0.22;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1221,7 +1214,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.0.21;
|
||||
MARKETING_VERSION = 2.0.22;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1468,6 +1461,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DDDD527729B5B83F0045BC3C /* MeshtasticDataModelV9.xcdatamodel */,
|
||||
DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */,
|
||||
DD5E51CC2986643400D21B61 /* MeshtasticDataModelV7.xcdatamodel */,
|
||||
DD964FC029724F6D007C176F /* MeshtasticDataModelV6.xcdatamodel */,
|
||||
|
|
@ -1477,7 +1471,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */;
|
||||
currentVersion = DDDD527729B5B83F0045BC3C /* MeshtasticDataModelV9.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -18,73 +18,66 @@ enum KeyboardType: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .defaultKeyboard:
|
||||
return NSLocalizedString("default", comment: "Default Keyboard")
|
||||
case .asciiCapable:
|
||||
return NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard")
|
||||
case .twitter:
|
||||
return NSLocalizedString("twitter", comment: "Twitter Keyboard")
|
||||
case .emailAddress:
|
||||
return NSLocalizedString("email.address", comment: "Email Address Keyboard")
|
||||
case .numbersAndPunctuation:
|
||||
return NSLocalizedString("numbers.punctuation", comment: "Numbers and Punctuation Keyboard")
|
||||
}
|
||||
switch self {
|
||||
case .defaultKeyboard:
|
||||
return NSLocalizedString("default", comment: "Default Keyboard")
|
||||
case .asciiCapable:
|
||||
return NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard")
|
||||
case .twitter:
|
||||
return NSLocalizedString("twitter", comment: "Twitter Keyboard")
|
||||
case .emailAddress:
|
||||
return NSLocalizedString("email.address", comment: "Email Address Keyboard")
|
||||
case .numbersAndPunctuation:
|
||||
return NSLocalizedString("numbers.punctuation", comment: "Numbers and Punctuation Keyboard")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CenteringMode: Int, CaseIterable, Identifiable {
|
||||
|
||||
|
||||
case allAnnotations = 0
|
||||
case allPositions = 1
|
||||
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .allAnnotations:
|
||||
return "All Annotations"// NSLocalizedString("default", comment: "Default Keyboard")
|
||||
case .allPositions:
|
||||
return "All Node Postions"// NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard")
|
||||
}
|
||||
switch self {
|
||||
case .allAnnotations:
|
||||
return "All Annotations"// NSLocalizedString("default", comment: "Default Keyboard")
|
||||
case .allPositions:
|
||||
return "All Node Postions"// NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum MeshMapType: String, CaseIterable, Identifiable {
|
||||
|
||||
case standard = "standard"
|
||||
case mutedStandard = "mutedStandard"
|
||||
case hybrid = "hybrid"
|
||||
case hybridFlyover = "hybridFlyover"
|
||||
case satellite = "satellite"
|
||||
case satelliteFlyover = "satelliteFlyover"
|
||||
|
||||
case standard
|
||||
case mutedStandard
|
||||
case hybrid
|
||||
case hybridFlyover
|
||||
case satellite
|
||||
case satelliteFlyover
|
||||
|
||||
var id: String { self.rawValue }
|
||||
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .standard:
|
||||
return NSLocalizedString("standard", comment: "Standard")
|
||||
case .mutedStandard:
|
||||
return NSLocalizedString("standard.muted", comment: "Standard Muted")
|
||||
case .hybrid:
|
||||
return NSLocalizedString("hybrid", comment: "Hybrid")
|
||||
case .hybridFlyover:
|
||||
return NSLocalizedString("hybrid.flyover", comment: "Hybrid Flyover")
|
||||
case .satellite:
|
||||
return NSLocalizedString("satellite", comment: "Satellite")
|
||||
case .satelliteFlyover:
|
||||
return NSLocalizedString("satellite.flyover", comment: "Satellite Flyover")
|
||||
}
|
||||
switch self {
|
||||
case .standard:
|
||||
return NSLocalizedString("standard", comment: "Standard")
|
||||
case .mutedStandard:
|
||||
return NSLocalizedString("standard.muted", comment: "Standard Muted")
|
||||
case .hybrid:
|
||||
return NSLocalizedString("hybrid", comment: "Hybrid")
|
||||
case .hybridFlyover:
|
||||
return NSLocalizedString("hybrid.flyover", comment: "Hybrid Flyover")
|
||||
case .satellite:
|
||||
return NSLocalizedString("satellite", comment: "Satellite")
|
||||
case .satelliteFlyover:
|
||||
return NSLocalizedString("satellite.flyover", comment: "Satellite Flyover")
|
||||
}
|
||||
}
|
||||
func MKMapTypeValue() -> MKMapType {
|
||||
|
||||
|
||||
switch self {
|
||||
case .standard:
|
||||
return MKMapType.standard
|
||||
|
|
@ -111,19 +104,17 @@ enum UserTrackingModes: Int, CaseIterable, Identifiable {
|
|||
var id: Int { self.rawValue }
|
||||
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .none:
|
||||
return NSLocalizedString("map.usertrackingmode.none", comment: "None")
|
||||
case .follow:
|
||||
return NSLocalizedString("map.usertrackingmode.follow", comment: "Follow")
|
||||
case .followWithHeading:
|
||||
return NSLocalizedString("map.usertrackingmode.followwithheading", comment: "Follow with Heading")
|
||||
}
|
||||
switch self {
|
||||
case .none:
|
||||
return NSLocalizedString("map.usertrackingmode.none", comment: "None")
|
||||
case .follow:
|
||||
return NSLocalizedString("map.usertrackingmode.follow", comment: "Follow")
|
||||
case .followWithHeading:
|
||||
return NSLocalizedString("map.usertrackingmode.followwithheading", comment: "Follow with Heading")
|
||||
}
|
||||
}
|
||||
func MKUserTrackingModeValue() -> MKUserTrackingMode {
|
||||
|
||||
|
||||
switch self {
|
||||
case .none:
|
||||
return MKUserTrackingMode.none
|
||||
|
|
@ -148,25 +139,23 @@ enum LocationUpdateInterval: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
}
|
||||
switch self {
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,21 +14,17 @@ enum BluetoothModes: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .randomPin:
|
||||
return NSLocalizedString("bluetooth.mode.randompin", comment: "Random PIN")
|
||||
case .fixedPin:
|
||||
return NSLocalizedString("bluetooth.mode.fixedpin", comment: "Fixed PIN")
|
||||
case .noPin:
|
||||
return NSLocalizedString("bluetooth.mode.nopin", comment: "No PIN (Just Works)")
|
||||
}
|
||||
switch self {
|
||||
case .randomPin:
|
||||
return NSLocalizedString("bluetooth.mode.randompin", comment: "Random PIN")
|
||||
case .fixedPin:
|
||||
return NSLocalizedString("bluetooth.mode.fixedpin", comment: "Fixed PIN")
|
||||
case .noPin:
|
||||
return NSLocalizedString("bluetooth.mode.nopin", comment: "No PIN (Just Works)")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.BluetoothConfig.PairingMode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .randomPin:
|
||||
return Config.BluetoothConfig.PairingMode.randomPin
|
||||
case .fixedPin:
|
||||
|
|
|
|||
|
|
@ -7,24 +7,22 @@
|
|||
import Foundation
|
||||
|
||||
// Default of 0 is unset
|
||||
enum ConfigPresets : Int, CaseIterable, Identifiable {
|
||||
enum ConfigPresets: Int, CaseIterable, Identifiable {
|
||||
|
||||
case unset = 0
|
||||
case rakRotaryEncoder = 1
|
||||
case cardKB = 2
|
||||
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return NSLocalizedString("canned.messages.preset.manual", comment: "Manual Configuration")
|
||||
case .rakRotaryEncoder:
|
||||
return NSLocalizedString("canned.messages.preset.rakrotary", comment: "RAK Rotary Encoder Module")
|
||||
case .cardKB:
|
||||
return NSLocalizedString("canned.messages.preset.cardkb", comment: "M5 Stack Card KB / RAK Keypad")
|
||||
}
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return NSLocalizedString("canned.messages.preset.manual", comment: "Manual Configuration")
|
||||
case .rakRotaryEncoder:
|
||||
return NSLocalizedString("canned.messages.preset.rakrotary", comment: "RAK Rotary Encoder Module")
|
||||
case .cardKB:
|
||||
return NSLocalizedString("canned.messages.preset.cardkb", comment: "M5 Stack Card KB / RAK Keypad")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -43,30 +41,28 @@ enum InputEventChars: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .none:
|
||||
return NSLocalizedString("inputevent.none", comment: "None")
|
||||
case .up:
|
||||
return NSLocalizedString("inputevent.up", comment: "Up")
|
||||
case .down:
|
||||
return NSLocalizedString("inputevent.down", comment: "Down")
|
||||
case .left:
|
||||
return NSLocalizedString("inputevent.left", comment: "Left")
|
||||
case .right:
|
||||
return NSLocalizedString("inputevent.right", comment: "Right")
|
||||
case .select:
|
||||
return NSLocalizedString("inputevent.select", comment: "Select")
|
||||
case .back:
|
||||
return NSLocalizedString("inputevent.back", comment: "Back")
|
||||
case .cancel:
|
||||
return NSLocalizedString("inputevent.cancel", comment: "Cancel")
|
||||
}
|
||||
switch self {
|
||||
|
||||
case .none:
|
||||
return NSLocalizedString("inputevent.none", comment: "None")
|
||||
case .up:
|
||||
return NSLocalizedString("inputevent.up", comment: "Up")
|
||||
case .down:
|
||||
return NSLocalizedString("inputevent.down", comment: "Down")
|
||||
case .left:
|
||||
return NSLocalizedString("inputevent.left", comment: "Left")
|
||||
case .right:
|
||||
return NSLocalizedString("inputevent.right", comment: "Right")
|
||||
case .select:
|
||||
return NSLocalizedString("inputevent.select", comment: "Select")
|
||||
case .back:
|
||||
return NSLocalizedString("inputevent.back", comment: "Back")
|
||||
case .cancel:
|
||||
return NSLocalizedString("inputevent.cancel", comment: "Cancel")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> ModuleConfig.CannedMessageConfig.InputEventChar {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
case .none:
|
||||
|
|
|
|||
|
|
@ -15,22 +15,20 @@ enum ChannelRoles: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .disabled:
|
||||
return NSLocalizedString("channel.role.disabled", comment: "Disabled")
|
||||
case .primary:
|
||||
return NSLocalizedString("channel.role.primary", comment: "Primary")
|
||||
case .secondary:
|
||||
return NSLocalizedString("channel.role.secondary", comment: "Secondary")
|
||||
}
|
||||
switch self {
|
||||
|
||||
case .disabled:
|
||||
return NSLocalizedString("channel.role.disabled", comment: "Disabled")
|
||||
case .primary:
|
||||
return NSLocalizedString("channel.role.primary", comment: "Primary")
|
||||
case .secondary:
|
||||
return NSLocalizedString("channel.role.secondary", comment: "Secondary")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Channel.Role {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
|
||||
case .disabled:
|
||||
return Channel.Role.disabled
|
||||
case .primary:
|
||||
|
|
|
|||
118
Meshtastic/Enums/DeviceEnums.swift
Normal file
118
Meshtastic/Enums/DeviceEnums.swift
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
//
|
||||
// DeviceRoles.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Default of 0 is Client
|
||||
enum DeviceRoles: Int, CaseIterable, Identifiable {
|
||||
|
||||
case client = 0
|
||||
case clientMute = 1
|
||||
case router = 2
|
||||
case routerClient = 3
|
||||
case repeater = 4
|
||||
case tracker = 5
|
||||
case sensor = 6
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var name: String {
|
||||
switch self {
|
||||
case .client:
|
||||
return "Client"
|
||||
case .clientMute:
|
||||
return "Muted Client"
|
||||
case .router:
|
||||
return "Router"
|
||||
case .routerClient:
|
||||
return "Router & Client"
|
||||
case .repeater:
|
||||
return "Repeater"
|
||||
case .tracker:
|
||||
return "Tracker"
|
||||
case .sensor:
|
||||
return "Sensor"
|
||||
}
|
||||
}
|
||||
var description: String {
|
||||
switch self {
|
||||
case .client:
|
||||
return NSLocalizedString("device.role.client", comment: "Client (default) - App connected client.")
|
||||
case .clientMute:
|
||||
return NSLocalizedString("device.role.clientmute", comment: "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh.")
|
||||
case .router:
|
||||
return NSLocalizedString("device.role.router", comment: "Router - Mesh packets will prefer to be routed over this node. Assumes device will operate in a standalone manner while placed in a location with a coverage advantage. WARNING: The BLE/Wi-Fi radios and the OLED screen will be put to sleep.")
|
||||
case .routerClient:
|
||||
return NSLocalizedString("device.role.routerclient", comment: "Router Client - Hybrid of the Client and Router roles. Similar to Router, except the Router Client can be used as both a Router and an app connected Client. BLE/Wi-Fi and OLED screen will not be put to sleep.")
|
||||
case .repeater:
|
||||
return NSLocalizedString("device.role.repeater", comment: "Repeater - Mesh packets will prefer to be routed over this node. This role eliminates unnecessary overhead such as NodeInfo, DeviceTelemetry, and any other mesh packet, resulting in the device not appearing as part of the network. Please see Rebroadcast Mode for additional settings specific to this role.")
|
||||
case .tracker:
|
||||
return NSLocalizedString("device.role.tracker", comment: "Tracker - For use with devices intended as a GPS tracker. Position packets sent from this device will be higher priority, with position broadcasting every two minutes. Smart Position Broadcast will default to off.")
|
||||
case .sensor:
|
||||
return NSLocalizedString("device.role.sensor", comment: "Sensor - For use with remote telemetry sensors. Setting this role will turn on environment telemetry. Telemetry packets sent from this device will be higher priority, with telemetry broadcasting every 7 minutes")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DeviceConfig.Role {
|
||||
|
||||
switch self {
|
||||
case .client:
|
||||
return Config.DeviceConfig.Role.client
|
||||
case .clientMute:
|
||||
return Config.DeviceConfig.Role.clientMute
|
||||
case .router:
|
||||
return Config.DeviceConfig.Role.router
|
||||
case .routerClient:
|
||||
return Config.DeviceConfig.Role.routerClient
|
||||
case .repeater:
|
||||
return Config.DeviceConfig.Role.repeater
|
||||
case .tracker:
|
||||
return Config.DeviceConfig.Role.tracker
|
||||
case .sensor:
|
||||
return Config.DeviceConfig.Role.sensor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum RebroadcastModes: Int, CaseIterable, Identifiable {
|
||||
|
||||
case all = 0
|
||||
case allSkipDecoding = 1
|
||||
case localOnly = 2
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
|
||||
var name: String {
|
||||
switch self {
|
||||
case .all:
|
||||
return "All"
|
||||
case .allSkipDecoding:
|
||||
return "All Skip Decoding"
|
||||
case .localOnly:
|
||||
return "Local Only"
|
||||
}
|
||||
}
|
||||
var description: String {
|
||||
switch self {
|
||||
case .all:
|
||||
return "Rebroadcast any observed message, if it was on our private channel or from another mesh with the same lora params."
|
||||
case .allSkipDecoding:
|
||||
return "Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. Only available in Repeater role. Setting this on any other roles will result in ALL behavior."
|
||||
case .localOnly:
|
||||
return "Inverted top bar for 2 Color display"
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DeviceConfig.RebroadcastMode {
|
||||
|
||||
switch self {
|
||||
case .all:
|
||||
return Config.DeviceConfig.RebroadcastMode.all
|
||||
case .allSkipDecoding:
|
||||
return Config.DeviceConfig.RebroadcastMode.allSkipDecoding
|
||||
case .localOnly:
|
||||
return Config.DeviceConfig.RebroadcastMode.localOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
//
|
||||
// DeviceRoles.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/20/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Default of 0 is Client
|
||||
enum DeviceRoles: Int, CaseIterable, Identifiable {
|
||||
|
||||
case client = 0
|
||||
case clientMute = 1
|
||||
case router = 2
|
||||
case routerClient = 3
|
||||
case repeater = 4
|
||||
case tracker = 5
|
||||
case sensor = 6
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var name: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return "Client"
|
||||
case .clientMute:
|
||||
return "Muted Client"
|
||||
case .router:
|
||||
return "Router"
|
||||
case .routerClient:
|
||||
return "Router & Client"
|
||||
case .repeater:
|
||||
return "Repeater"
|
||||
case .tracker:
|
||||
return "Tracker"
|
||||
case .sensor:
|
||||
return "Sensor"
|
||||
}
|
||||
}
|
||||
}
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return NSLocalizedString("device.role.client", comment: "Client (default) - App connected client.")
|
||||
case .clientMute:
|
||||
return NSLocalizedString("device.role.clientmute", comment: "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh.")
|
||||
case .router:
|
||||
return NSLocalizedString("device.role.router", comment: "Router - Mesh packets will prefer to be routed over this node. Assumes device will operate in a standalone manner while placed in a location with a coverage advantage. WARNING: The BLE/Wi-Fi radios and the OLED screen will be put to sleep.")
|
||||
case .routerClient:
|
||||
return NSLocalizedString("device.role.routerclient", comment: "Router Client - Hybrid of the Client and Router roles. Similar to Router, except the Router Client can be used as both a Router and an app connected Client. BLE/Wi-Fi and OLED screen will not be put to sleep.")
|
||||
case .repeater:
|
||||
return NSLocalizedString("device.role.repeater", comment: "Repeater - Mesh packets will prefer to be routed over this node. This role eliminates unnecessary overhead such as NodeInfo, DeviceTelemetry, and any other mesh packet, resulting in the device not appearing as part of the network. Please see Rebroadcast Mode for additional settings specific to this role.")
|
||||
case .tracker:
|
||||
return NSLocalizedString("device.role.tracker", comment: "Tracker - For use with devices intended as a GPS tracker. Position packets sent from this device will be higher priority, with position broadcasting every two minutes. Smart Position Broadcast will default to off.")
|
||||
case .sensor:
|
||||
return NSLocalizedString("device.role.sensor", comment: "Sensor - For use with remote telemetry sensors. Setting this role will turn on environment telemetry. Telemetry packets sent from this device will be higher priority, with telemetry broadcasting every 7 minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DeviceConfig.Role {
|
||||
|
||||
switch self {
|
||||
|
||||
case .client:
|
||||
return Config.DeviceConfig.Role.client
|
||||
case .clientMute:
|
||||
return Config.DeviceConfig.Role.clientMute
|
||||
case .router:
|
||||
return Config.DeviceConfig.Role.router
|
||||
case .routerClient:
|
||||
return Config.DeviceConfig.Role.routerClient
|
||||
case .repeater:
|
||||
return Config.DeviceConfig.Role.repeater
|
||||
case .tracker:
|
||||
return Config.DeviceConfig.Role.tracker
|
||||
case .sensor:
|
||||
return Config.DeviceConfig.Role.sensor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,23 +8,21 @@
|
|||
import Foundation
|
||||
|
||||
enum ScreenUnits: Int, CaseIterable, Identifiable {
|
||||
|
||||
|
||||
case metric = 0
|
||||
case imperial = 1
|
||||
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .metric:
|
||||
return "Metric"
|
||||
case .imperial:
|
||||
return "Imperial"
|
||||
}
|
||||
switch self {
|
||||
case .metric:
|
||||
return "Metric"
|
||||
case .imperial:
|
||||
return "Imperial"
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.DisplayUnits {
|
||||
|
||||
|
||||
switch self {
|
||||
case .metric:
|
||||
return Config.DisplayConfig.DisplayUnits.metric
|
||||
|
|
@ -36,6 +34,8 @@ enum ScreenUnits: Int, CaseIterable, Identifiable {
|
|||
|
||||
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case fifteenSeconds = 15
|
||||
case thirtySeconds = 30
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 600
|
||||
|
|
@ -46,23 +46,25 @@ enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
case .max:
|
||||
return NSLocalizedString("always.on", comment: "Always On")
|
||||
}
|
||||
switch self {
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
case .max:
|
||||
return NSLocalizedString("always.on", comment: "Always On")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,24 +81,21 @@ enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .off:
|
||||
return NSLocalizedString("off", comment: "Off")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
}
|
||||
switch self {
|
||||
case .off:
|
||||
return NSLocalizedString("off", comment: "Off")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Default of 0 is auto
|
||||
enum OledTypes: Int, CaseIterable, Identifiable {
|
||||
|
|
@ -108,21 +107,19 @@ enum OledTypes: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .auto:
|
||||
return NSLocalizedString("automatic.detection", comment: "Automatic Detection")
|
||||
case .ssd1306:
|
||||
return "SSD 1306"
|
||||
case .sh1106:
|
||||
return "SH 1106"
|
||||
case .sh1107:
|
||||
return "SH 1107"
|
||||
}
|
||||
switch self {
|
||||
case .auto:
|
||||
return NSLocalizedString("automatic.detection", comment: "Automatic Detection")
|
||||
case .ssd1306:
|
||||
return "SSD 1306"
|
||||
case .sh1106:
|
||||
return "SH 1106"
|
||||
case .sh1107:
|
||||
return "SH 1107"
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.OledType {
|
||||
|
||||
|
||||
switch self {
|
||||
case .auto:
|
||||
return Config.DisplayConfig.OledType.oledAuto
|
||||
|
|
@ -146,21 +143,19 @@ enum DisplayModes: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .defaultMode:
|
||||
return "Default 128x64 screen layout"
|
||||
case .twoColor:
|
||||
return "Optimized for 2 color displays"
|
||||
case .inverted:
|
||||
return "Inverted top bar for 2 Color display"
|
||||
case .color:
|
||||
return "TFT Full Color Displays"
|
||||
}
|
||||
switch self {
|
||||
case .defaultMode:
|
||||
return "Default 128x64 screen layout"
|
||||
case .twoColor:
|
||||
return "Optimized for 2 color displays"
|
||||
case .inverted:
|
||||
return "Inverted top bar for 2 Color display"
|
||||
case .color:
|
||||
return "TFT Full Color Displays"
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.DisplayMode {
|
||||
|
||||
|
||||
switch self {
|
||||
case .defaultMode:
|
||||
return Config.DisplayConfig.DisplayMode.default
|
||||
|
|
|
|||
|
|
@ -14,20 +14,18 @@ enum EthernetMode: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .dhcp:
|
||||
return "DHCP"
|
||||
case .staticip:
|
||||
return "Static IP"
|
||||
}
|
||||
|
||||
switch self {
|
||||
case .dhcp:
|
||||
return "DHCP"
|
||||
case .staticip:
|
||||
return "Static IP"
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.NetworkConfig.AddressMode {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
|
||||
case .dhcp:
|
||||
return Config.NetworkConfig.AddressMode.dhcp
|
||||
case .staticip:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import Foundation
|
||||
|
||||
enum OutputIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
|
||||
case unset = 0
|
||||
case oneSecond = 1000
|
||||
case twoSeconds = 2000
|
||||
|
|
@ -19,33 +19,31 @@ enum OutputIntervals: Int, CaseIterable, Identifiable {
|
|||
case fifteenSeconds = 15000
|
||||
case thirtySeconds = 30000
|
||||
case oneMinute = 60000
|
||||
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return NSLocalizedString("unset", comment: "Unset")
|
||||
case .oneSecond:
|
||||
return NSLocalizedString("interval.one.second", comment: "One Second")
|
||||
case .twoSeconds:
|
||||
return NSLocalizedString("interval.two.seconds", comment: "Two Seconds")
|
||||
case .threeSeconds:
|
||||
return NSLocalizedString("interval.three.seconds", comment: "Three Seconds")
|
||||
case .fourSeconds:
|
||||
return NSLocalizedString("interval.four.seconds", comment: "Four Seconds")
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
}
|
||||
|
||||
switch self {
|
||||
case .unset:
|
||||
return NSLocalizedString("unset", comment: "Unset")
|
||||
case .oneSecond:
|
||||
return NSLocalizedString("interval.one.second", comment: "One Second")
|
||||
case .twoSeconds:
|
||||
return NSLocalizedString("interval.two.seconds", comment: "Two Seconds")
|
||||
case .threeSeconds:
|
||||
return NSLocalizedString("interval.three.seconds", comment: "Three Seconds")
|
||||
case .fourSeconds:
|
||||
return NSLocalizedString("interval.four.seconds", comment: "Four Seconds")
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,30 +61,27 @@ enum SenderIntervals: Int, CaseIterable, Identifiable {
|
|||
case thirtyMinutes = 1800
|
||||
case oneHour = 3600
|
||||
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .off:
|
||||
return NSLocalizedString("off", comment: "Off")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
}
|
||||
switch self {
|
||||
case .off:
|
||||
return NSLocalizedString("off", comment: "Off")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -116,50 +111,48 @@ enum UpdateIntervals: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
case .twoHours:
|
||||
return NSLocalizedString("interval.two.hours", comment: "Two Hours")
|
||||
case .threeHours:
|
||||
return NSLocalizedString("interval.three.hours", comment: "Three Hours")
|
||||
case .fourHours:
|
||||
return NSLocalizedString("interval.four.hours", comment: "Four Hours")
|
||||
case .fiveHours:
|
||||
return NSLocalizedString("interval.five.hours", comment: "Five Hours")
|
||||
case .sixHours:
|
||||
return NSLocalizedString("interval.six.hours", comment: "Six Hours")
|
||||
case .twelveHours:
|
||||
return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours")
|
||||
case .eighteenHours:
|
||||
return NSLocalizedString("interval.eighteen.hours", comment: "Eighteen Hours")
|
||||
case .twentyFourHours:
|
||||
return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours")
|
||||
case .thirtySixHours:
|
||||
return NSLocalizedString("interval.thirtysix.hours", comment: "Thirty Six Hours")
|
||||
case .fortyeightHours:
|
||||
return NSLocalizedString("interval.fortyeight.hours", comment: "Forty Eight Hours")
|
||||
case .seventyTwoHours:
|
||||
return NSLocalizedString("interval.seventytwo.hours", comment: "Seventy Two Hours")
|
||||
}
|
||||
|
||||
switch self {
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
case .twoHours:
|
||||
return NSLocalizedString("interval.two.hours", comment: "Two Hours")
|
||||
case .threeHours:
|
||||
return NSLocalizedString("interval.three.hours", comment: "Three Hours")
|
||||
case .fourHours:
|
||||
return NSLocalizedString("interval.four.hours", comment: "Four Hours")
|
||||
case .fiveHours:
|
||||
return NSLocalizedString("interval.five.hours", comment: "Five Hours")
|
||||
case .sixHours:
|
||||
return NSLocalizedString("interval.six.hours", comment: "Six Hours")
|
||||
case .twelveHours:
|
||||
return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours")
|
||||
case .eighteenHours:
|
||||
return NSLocalizedString("interval.eighteen.hours", comment: "Eighteen Hours")
|
||||
case .twentyFourHours:
|
||||
return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours")
|
||||
case .thirtySixHours:
|
||||
return NSLocalizedString("interval.thirtysix.hours", comment: "Thirty Six Hours")
|
||||
case .fortyeightHours:
|
||||
return NSLocalizedString("interval.fortyeight.hours", comment: "Forty Eight Hours")
|
||||
case .seventyTwoHours:
|
||||
return NSLocalizedString("interval.seventytwo.hours", comment: "Seventy Two Hours")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum RegionCodes : Int, CaseIterable, Identifiable {
|
||||
enum RegionCodes: Int, CaseIterable, Identifiable {
|
||||
|
||||
case unset = 0
|
||||
case us = 1
|
||||
|
|
@ -28,174 +28,131 @@ enum RegionCodes : Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .unset:
|
||||
return "Please set a region"
|
||||
case .us:
|
||||
return "United States"
|
||||
case .eu433:
|
||||
return "European Union 433mhz"
|
||||
case .eu868:
|
||||
return "European Union 868mhz"
|
||||
case .cn:
|
||||
return "China"
|
||||
case .jp:
|
||||
return "Japan"
|
||||
case .anz:
|
||||
return "Australia / New Zealand"
|
||||
case .kr:
|
||||
return "Korea"
|
||||
case .tw:
|
||||
return "Taiwan"
|
||||
case .ru:
|
||||
return "Russia"
|
||||
case .in:
|
||||
return "India"
|
||||
case .nz865:
|
||||
return "New Zealand 865mhz"
|
||||
case .th:
|
||||
return "Thailand"
|
||||
case .ua433:
|
||||
return "Ukraine 433mhz"
|
||||
case .ua868:
|
||||
return "Ukraine 868mhz"
|
||||
case .lora24:
|
||||
return "2.4 GHZ"
|
||||
}
|
||||
switch self {
|
||||
case .unset:
|
||||
return "Please set a region"
|
||||
case .us:
|
||||
return "United States"
|
||||
case .eu433:
|
||||
return "European Union 433mhz"
|
||||
case .eu868:
|
||||
return "European Union 868mhz"
|
||||
case .cn:
|
||||
return "China"
|
||||
case .jp:
|
||||
return "Japan"
|
||||
case .anz:
|
||||
return "Australia / New Zealand"
|
||||
case .kr:
|
||||
return "Korea"
|
||||
case .tw:
|
||||
return "Taiwan"
|
||||
case .ru:
|
||||
return "Russia"
|
||||
case .in:
|
||||
return "India"
|
||||
case .nz865:
|
||||
return "New Zealand 865mhz"
|
||||
case .th:
|
||||
return "Thailand"
|
||||
case .ua433:
|
||||
return "Ukraine 433mhz"
|
||||
case .ua868:
|
||||
return "Ukraine 868mhz"
|
||||
case .lora24:
|
||||
return "2.4 GHZ"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func protoEnumValue() -> Config.LoRaConfig.RegionCode {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
case .unset:
|
||||
return Config.LoRaConfig.RegionCode.unset
|
||||
case .us:
|
||||
return Config.LoRaConfig.RegionCode.us
|
||||
case .eu433:
|
||||
return Config.LoRaConfig.RegionCode.eu433
|
||||
case .eu868:
|
||||
return Config.LoRaConfig.RegionCode.eu868
|
||||
case .cn:
|
||||
return Config.LoRaConfig.RegionCode.cn
|
||||
case .jp:
|
||||
return Config.LoRaConfig.RegionCode.jp
|
||||
case .anz:
|
||||
return Config.LoRaConfig.RegionCode.anz
|
||||
case .kr:
|
||||
return Config.LoRaConfig.RegionCode.kr
|
||||
case .tw:
|
||||
return Config.LoRaConfig.RegionCode.tw
|
||||
case .ru:
|
||||
return Config.LoRaConfig.RegionCode.ru
|
||||
case .in:
|
||||
return Config.LoRaConfig.RegionCode.in
|
||||
case .nz865:
|
||||
return Config.LoRaConfig.RegionCode.nz865
|
||||
case .th:
|
||||
return Config.LoRaConfig.RegionCode.th
|
||||
case .ua433:
|
||||
return Config.LoRaConfig.RegionCode.ua433
|
||||
case .ua868:
|
||||
return Config.LoRaConfig.RegionCode.ua868
|
||||
case .lora24:
|
||||
return Config.LoRaConfig.RegionCode.lora24
|
||||
case .unset:
|
||||
return Config.LoRaConfig.RegionCode.unset
|
||||
case .us:
|
||||
return Config.LoRaConfig.RegionCode.us
|
||||
case .eu433:
|
||||
return Config.LoRaConfig.RegionCode.eu433
|
||||
case .eu868:
|
||||
return Config.LoRaConfig.RegionCode.eu868
|
||||
case .cn:
|
||||
return Config.LoRaConfig.RegionCode.cn
|
||||
case .jp:
|
||||
return Config.LoRaConfig.RegionCode.jp
|
||||
case .anz:
|
||||
return Config.LoRaConfig.RegionCode.anz
|
||||
case .kr:
|
||||
return Config.LoRaConfig.RegionCode.kr
|
||||
case .tw:
|
||||
return Config.LoRaConfig.RegionCode.tw
|
||||
case .ru:
|
||||
return Config.LoRaConfig.RegionCode.ru
|
||||
case .in:
|
||||
return Config.LoRaConfig.RegionCode.in
|
||||
case .nz865:
|
||||
return Config.LoRaConfig.RegionCode.nz865
|
||||
case .th:
|
||||
return Config.LoRaConfig.RegionCode.th
|
||||
case .ua433:
|
||||
return Config.LoRaConfig.RegionCode.ua433
|
||||
case .ua868:
|
||||
return Config.LoRaConfig.RegionCode.ua868
|
||||
case .lora24:
|
||||
return Config.LoRaConfig.RegionCode.lora24
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ModemPresets : Int, CaseIterable, Identifiable {
|
||||
|
||||
case LongFast = 0
|
||||
case LongSlow = 1
|
||||
case LongModerate = 7
|
||||
case VLongSlow = 2
|
||||
case MedSlow = 3
|
||||
case MedFast = 4
|
||||
case ShortSlow = 5
|
||||
case ShortFast = 6
|
||||
|
||||
enum ModemPresets: Int, CaseIterable, Identifiable {
|
||||
|
||||
case longFast = 0
|
||||
case longSlow = 1
|
||||
case longModerate = 7
|
||||
case vLongSlow = 2
|
||||
case medSlow = 3
|
||||
case medFast = 4
|
||||
case shortSlow = 5
|
||||
case shortFast = 6
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return "Long Range - Fast"
|
||||
case .LongSlow:
|
||||
return "Long Range - Slow"
|
||||
case .LongModerate:
|
||||
return "Long Range - Moderate"
|
||||
case .VLongSlow:
|
||||
return "Very Long Range - Slow"
|
||||
case .MedSlow:
|
||||
return "Medium Range - Slow"
|
||||
case .MedFast:
|
||||
return "Medium Range - Fast"
|
||||
case .ShortSlow:
|
||||
return "Short Range - Slow"
|
||||
case .ShortFast:
|
||||
return "Short Range - Fast"
|
||||
}
|
||||
switch self {
|
||||
case .longFast:
|
||||
return "Long Range - Fast"
|
||||
case .longSlow:
|
||||
return "Long Range - Slow"
|
||||
case .longModerate:
|
||||
return "Long Range - Moderate"
|
||||
case .vLongSlow:
|
||||
return "Very Long Range - Slow"
|
||||
case .medSlow:
|
||||
return "Medium Range - Slow"
|
||||
case .medFast:
|
||||
return "Medium Range - Fast"
|
||||
case .shortSlow:
|
||||
return "Short Range - Slow"
|
||||
case .shortFast:
|
||||
return "Short Range - Fast"
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.LoRaConfig.ModemPreset {
|
||||
|
||||
switch self {
|
||||
|
||||
case .LongFast:
|
||||
return Config.LoRaConfig.ModemPreset.longFast
|
||||
case .LongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.longSlow
|
||||
case .LongModerate:
|
||||
return Config.LoRaConfig.ModemPreset.longModerate
|
||||
case .VLongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.veryLongSlow
|
||||
case .MedSlow:
|
||||
return Config.LoRaConfig.ModemPreset.mediumSlow
|
||||
case .MedFast:
|
||||
return Config.LoRaConfig.ModemPreset.mediumFast
|
||||
case .ShortSlow:
|
||||
return Config.LoRaConfig.ModemPreset.shortSlow
|
||||
case .ShortFast:
|
||||
return Config.LoRaConfig.ModemPreset.shortFast
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum HopValues : Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneHop = 1
|
||||
case twoHops = 2
|
||||
case threeHops = 3
|
||||
case fourHops = 4
|
||||
case fiveHops = 5
|
||||
case sixHops = 6
|
||||
case sevenHops = 7
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .oneHop:
|
||||
return "One Hop"
|
||||
case .twoHops:
|
||||
return "Two Hops"
|
||||
case .threeHops:
|
||||
return "Three Hops"
|
||||
case .fourHops:
|
||||
return "Four Hops"
|
||||
case .fiveHops:
|
||||
return "Five Hops"
|
||||
case .sixHops:
|
||||
return "Six Hops"
|
||||
case .sevenHops:
|
||||
return "Seven Hops"
|
||||
}
|
||||
case .longFast:
|
||||
return Config.LoRaConfig.ModemPreset.longFast
|
||||
case .longSlow:
|
||||
return Config.LoRaConfig.ModemPreset.longSlow
|
||||
case .longModerate:
|
||||
return Config.LoRaConfig.ModemPreset.longModerate
|
||||
case .vLongSlow:
|
||||
return Config.LoRaConfig.ModemPreset.veryLongSlow
|
||||
case .medSlow:
|
||||
return Config.LoRaConfig.ModemPreset.mediumSlow
|
||||
case .medFast:
|
||||
return Config.LoRaConfig.ModemPreset.mediumFast
|
||||
case .shortSlow:
|
||||
return Config.LoRaConfig.ModemPreset.shortSlow
|
||||
case .shortFast:
|
||||
return Config.LoRaConfig.ModemPreset.shortFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,43 +23,39 @@ enum Tapbacks: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var emojiString: String {
|
||||
get {
|
||||
switch self {
|
||||
case .heart:
|
||||
return "❤️"
|
||||
case .thumbsUp:
|
||||
return "👍"
|
||||
case .thumbsDown:
|
||||
return "👎"
|
||||
case .haHa:
|
||||
return "🤣"
|
||||
case .exclamation:
|
||||
return "‼️"
|
||||
case .question:
|
||||
return "❓"
|
||||
case .poop:
|
||||
return "💩"
|
||||
}
|
||||
switch self {
|
||||
case .heart:
|
||||
return "❤️"
|
||||
case .thumbsUp:
|
||||
return "👍"
|
||||
case .thumbsDown:
|
||||
return "👎"
|
||||
case .haHa:
|
||||
return "🤣"
|
||||
case .exclamation:
|
||||
return "‼️"
|
||||
case .question:
|
||||
return "❓"
|
||||
case .poop:
|
||||
return "💩"
|
||||
}
|
||||
}
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .heart:
|
||||
return NSLocalizedString("tapback.heart", comment: "Heart")
|
||||
case .thumbsUp:
|
||||
return NSLocalizedString("tapback.thumbsup", comment: "Thumbs Up")
|
||||
case .thumbsDown:
|
||||
return NSLocalizedString("tapback.thumbsdown", comment: "Thumbs Down")
|
||||
case .haHa:
|
||||
return NSLocalizedString("tapback.haha", comment: "HaHa")
|
||||
case .exclamation:
|
||||
return NSLocalizedString("tapback.exclamation", comment: "Exclamation Mark")
|
||||
case .question:
|
||||
return NSLocalizedString("tapback.question", comment: "Question Mark")
|
||||
case .poop:
|
||||
return NSLocalizedString("tapback.poop", comment: "Poop")
|
||||
}
|
||||
switch self {
|
||||
case .heart:
|
||||
return NSLocalizedString("tapback.heart", comment: "Heart")
|
||||
case .thumbsUp:
|
||||
return NSLocalizedString("tapback.thumbsup", comment: "Thumbs Up")
|
||||
case .thumbsDown:
|
||||
return NSLocalizedString("tapback.thumbsdown", comment: "Thumbs Down")
|
||||
case .haHa:
|
||||
return NSLocalizedString("tapback.haha", comment: "HaHa")
|
||||
case .exclamation:
|
||||
return NSLocalizedString("tapback.exclamation", comment: "Exclamation Mark")
|
||||
case .question:
|
||||
return NSLocalizedString("tapback.question", comment: "Question Mark")
|
||||
case .poop:
|
||||
return NSLocalizedString("tapback.poop", comment: "Poop")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,27 +18,25 @@ enum GpsFormats: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .gpsFormatDec:
|
||||
return NSLocalizedString("gpsformat.dec", comment: "Decimal Degrees Format")
|
||||
case .gpsFormatDms:
|
||||
return NSLocalizedString("gpsformat.dms", comment: "Degrees Minutes Seconds")
|
||||
case .gpsFormatUtm:
|
||||
return NSLocalizedString("gpsformat.utm", comment: "Universal Transverse Mercator")
|
||||
case .gpsFormatMgrs:
|
||||
return NSLocalizedString("gpsformat.mgrs", comment: "Military Grid Reference System")
|
||||
case .gpsFormatOlc:
|
||||
return NSLocalizedString("gpsformat.olc", comment: "Open Location Code (aka Plus Codes)")
|
||||
case .gpsFormatOsgr:
|
||||
return NSLocalizedString("gpsformat.osgr", comment: "Ordnance Survey Grid Reference")
|
||||
}
|
||||
switch self {
|
||||
case .gpsFormatDec:
|
||||
return NSLocalizedString("gpsformat.dec", comment: "Decimal Degrees Format")
|
||||
case .gpsFormatDms:
|
||||
return NSLocalizedString("gpsformat.dms", comment: "Degrees Minutes Seconds")
|
||||
case .gpsFormatUtm:
|
||||
return NSLocalizedString("gpsformat.utm", comment: "Universal Transverse Mercator")
|
||||
case .gpsFormatMgrs:
|
||||
return NSLocalizedString("gpsformat.mgrs", comment: "Military Grid Reference System")
|
||||
case .gpsFormatOlc:
|
||||
return NSLocalizedString("gpsformat.olc", comment: "Open Location Code (aka Plus Codes)")
|
||||
case .gpsFormatOsgr:
|
||||
return NSLocalizedString("gpsformat.osgr", comment: "Ordnance Survey Grid Reference")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
|
||||
case .gpsFormatDec:
|
||||
return Config.DisplayConfig.GpsCoordinateFormat.dec
|
||||
case .gpsFormatDms:
|
||||
|
|
@ -55,7 +53,6 @@ enum GpsFormats: Int, CaseIterable, Identifiable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case fiveSeconds = 5
|
||||
|
|
@ -78,44 +75,41 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .twentySeconds:
|
||||
return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds")
|
||||
case .twentyFiveSeconds:
|
||||
return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .twoMinutes:
|
||||
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
case .sixHours:
|
||||
return NSLocalizedString("interval.six.hours", comment: "Six Hours")
|
||||
case .twelveHours:
|
||||
return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours")
|
||||
case .twentyFourHours:
|
||||
return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours")
|
||||
case .maxInt32:
|
||||
return NSLocalizedString("on.boot", comment: "On Boot Only")
|
||||
}
|
||||
switch self {
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .twentySeconds:
|
||||
return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds")
|
||||
case .twentyFiveSeconds:
|
||||
return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .twoMinutes:
|
||||
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
case .thirtyMinutes:
|
||||
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
|
||||
case .oneHour:
|
||||
return NSLocalizedString("interval.one.hour", comment: "One Hour")
|
||||
case .sixHours:
|
||||
return NSLocalizedString("interval.six.hours", comment: "Six Hours")
|
||||
case .twelveHours:
|
||||
return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours")
|
||||
case .twentyFourHours:
|
||||
return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours")
|
||||
case .maxInt32:
|
||||
return NSLocalizedString("on.boot", comment: "On Boot Only")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -137,34 +131,31 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .twoSeconds:
|
||||
return NSLocalizedString("interval.two.seconds", comment: "Two Seconds")
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .twentySeconds:
|
||||
return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds")
|
||||
case .twentyFiveSeconds:
|
||||
return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .twoMinutes:
|
||||
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
}
|
||||
switch self {
|
||||
case .twoSeconds:
|
||||
return NSLocalizedString("interval.two.seconds", comment: "Two Seconds")
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .twentySeconds:
|
||||
return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds")
|
||||
case .twentyFiveSeconds:
|
||||
return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .twoMinutes:
|
||||
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
case .tenMinutes:
|
||||
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
|
||||
case .fifteenMinutes:
|
||||
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,40 +23,38 @@ enum RoutingError: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var display: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .none:
|
||||
return NSLocalizedString("routing.acknowledged", comment: "Acknowledged")
|
||||
case .noRoute:
|
||||
return NSLocalizedString("routing.noroute", comment: "No Route")
|
||||
case .gotNak:
|
||||
return NSLocalizedString("routing.gotnak", comment: "Received a negative acknowledgment")
|
||||
case .timeout:
|
||||
return NSLocalizedString("routing.timeout", comment: "Timeout")
|
||||
case .noInterface:
|
||||
return NSLocalizedString("routing.nointerface", comment: "No Interface")
|
||||
case .maxRetransmit:
|
||||
return NSLocalizedString("routing.maxretransmit", comment: "Max Retransmission Reached")
|
||||
case .noChannel:
|
||||
return NSLocalizedString("routing.nochannel", comment: "No Channel")
|
||||
case .tooLarge:
|
||||
return NSLocalizedString("routing.toolarge", comment: "The packet is too large")
|
||||
case .noResponse:
|
||||
return NSLocalizedString("routing.noresponse", comment: "No Response")
|
||||
case .dutyCycleLimit:
|
||||
return NSLocalizedString("routing.dutycyclelimit", comment: "Regional Duty Cycle Limit Reached")
|
||||
case .badRequest:
|
||||
return NSLocalizedString("routing.badRequest", comment: "Bad Request")
|
||||
case .notAuthorized:
|
||||
return NSLocalizedString("routing.notauthorized", comment: "Not Authorized")
|
||||
}
|
||||
switch self {
|
||||
|
||||
case .none:
|
||||
return NSLocalizedString("routing.acknowledged", comment: "Acknowledged")
|
||||
case .noRoute:
|
||||
return NSLocalizedString("routing.noroute", comment: "No Route")
|
||||
case .gotNak:
|
||||
return NSLocalizedString("routing.gotnak", comment: "Received a negative acknowledgment")
|
||||
case .timeout:
|
||||
return NSLocalizedString("routing.timeout", comment: "Timeout")
|
||||
case .noInterface:
|
||||
return NSLocalizedString("routing.nointerface", comment: "No Interface")
|
||||
case .maxRetransmit:
|
||||
return NSLocalizedString("routing.maxretransmit", comment: "Max Retransmission Reached")
|
||||
case .noChannel:
|
||||
return NSLocalizedString("routing.nochannel", comment: "No Channel")
|
||||
case .tooLarge:
|
||||
return NSLocalizedString("routing.toolarge", comment: "The packet is too large")
|
||||
case .noResponse:
|
||||
return NSLocalizedString("routing.noresponse", comment: "No Response")
|
||||
case .dutyCycleLimit:
|
||||
return NSLocalizedString("routing.dutycyclelimit", comment: "Regional Duty Cycle Limit Reached")
|
||||
case .badRequest:
|
||||
return NSLocalizedString("routing.badRequest", comment: "Bad Request")
|
||||
case .notAuthorized:
|
||||
return NSLocalizedString("routing.notauthorized", comment: "Not Authorized")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> Routing.Error {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
|
||||
case .none:
|
||||
return Routing.Error.none
|
||||
case .noRoute:
|
||||
|
|
@ -81,7 +79,7 @@ enum RoutingError: Int, CaseIterable, Identifiable {
|
|||
return Routing.Error.badRequest
|
||||
case .notAuthorized:
|
||||
return Routing.Error.notAuthorized
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,49 +27,47 @@ enum SerialBaudRates: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
switch self {
|
||||
|
||||
case .baudDefault:
|
||||
return NSLocalizedString("default", comment: "Default")
|
||||
case .baud110:
|
||||
return "110 Baud"
|
||||
case .baud300:
|
||||
return "300 Baud"
|
||||
case .baud600:
|
||||
return "600 Baud"
|
||||
case .baud1200:
|
||||
return "1200 Baud"
|
||||
case .baud2400:
|
||||
return "2400 Baud"
|
||||
case .baud4800:
|
||||
return "4800 Baud"
|
||||
case .baud9600:
|
||||
return "9600 Baud"
|
||||
case .baud19200:
|
||||
return "19200 Baud"
|
||||
case .baud38400:
|
||||
return "38400 Baud"
|
||||
case .baud57600:
|
||||
return "57600 Baud"
|
||||
case .baud115200:
|
||||
return "115200 Baud"
|
||||
case .baud230400:
|
||||
return "230400 Baud"
|
||||
case .baud460800:
|
||||
return "460800 Baud"
|
||||
case .baud576000:
|
||||
return "576000 Baud"
|
||||
case .baud921600:
|
||||
return "921600 Baud"
|
||||
}
|
||||
case .baudDefault:
|
||||
return NSLocalizedString("default", comment: "Default")
|
||||
case .baud110:
|
||||
return "110 Baud"
|
||||
case .baud300:
|
||||
return "300 Baud"
|
||||
case .baud600:
|
||||
return "600 Baud"
|
||||
case .baud1200:
|
||||
return "1200 Baud"
|
||||
case .baud2400:
|
||||
return "2400 Baud"
|
||||
case .baud4800:
|
||||
return "4800 Baud"
|
||||
case .baud9600:
|
||||
return "9600 Baud"
|
||||
case .baud19200:
|
||||
return "19200 Baud"
|
||||
case .baud38400:
|
||||
return "38400 Baud"
|
||||
case .baud57600:
|
||||
return "57600 Baud"
|
||||
case .baud115200:
|
||||
return "115200 Baud"
|
||||
case .baud230400:
|
||||
return "230400 Baud"
|
||||
case .baud460800:
|
||||
return "460800 Baud"
|
||||
case .baud576000:
|
||||
return "576000 Baud"
|
||||
case .baud921600:
|
||||
return "921600 Baud"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func protoEnumValue() -> ModuleConfig.SerialConfig.Serial_Baud {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
|
||||
case .baudDefault:
|
||||
return ModuleConfig.SerialConfig.Serial_Baud.baudDefault
|
||||
case .baud110:
|
||||
|
|
@ -113,28 +111,26 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable {
|
|||
case proto = 2
|
||||
case txtmsg = 3
|
||||
case nmea = 4
|
||||
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .default:
|
||||
return NSLocalizedString("serial.mode.default", comment: "Default")
|
||||
case .simple:
|
||||
return NSLocalizedString("serial.mode.simple", comment: "Simple")
|
||||
case .proto:
|
||||
return NSLocalizedString("serial.mode.proto", comment: "Protobufs")
|
||||
case .txtmsg:
|
||||
return NSLocalizedString("serial.mode.txtmsg", comment: "Text Message")
|
||||
case .nmea:
|
||||
return NSLocalizedString("serial.mode.nmea", comment: "NMEA Positions")
|
||||
}
|
||||
switch self {
|
||||
case .default:
|
||||
return NSLocalizedString("serial.mode.default", comment: "Default")
|
||||
case .simple:
|
||||
return NSLocalizedString("serial.mode.simple", comment: "Simple")
|
||||
case .proto:
|
||||
return NSLocalizedString("serial.mode.proto", comment: "Protobufs")
|
||||
case .txtmsg:
|
||||
return NSLocalizedString("serial.mode.txtmsg", comment: "Text Message")
|
||||
case .nmea:
|
||||
return NSLocalizedString("serial.mode.nmea", comment: "NMEA Positions")
|
||||
}
|
||||
}
|
||||
func protoEnumValue() -> ModuleConfig.SerialConfig.Serial_Mode {
|
||||
|
||||
|
||||
switch self {
|
||||
|
||||
|
||||
case .default:
|
||||
return ModuleConfig.SerialConfig.Serial_Mode.default
|
||||
case .simple:
|
||||
|
|
@ -162,25 +158,23 @@ enum SerialTimeoutIntervals: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .unset:
|
||||
return NSLocalizedString("unset", comment: "Unset")
|
||||
case .oneSecond:
|
||||
return NSLocalizedString("interval.one.second", comment: "One Second")
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
}
|
||||
switch self {
|
||||
case .unset:
|
||||
return NSLocalizedString("unset", comment: "Unset")
|
||||
case .oneSecond:
|
||||
return NSLocalizedString("interval.one.second", comment: "One Second")
|
||||
case .fiveSeconds:
|
||||
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
|
||||
case .tenSeconds:
|
||||
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
|
||||
case .fifteenSeconds:
|
||||
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
|
||||
case .thirtySeconds:
|
||||
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
|
||||
case .oneMinute:
|
||||
return NSLocalizedString("interval.one.minute", comment: "One Minute")
|
||||
case .fiveMinutes:
|
||||
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,24 +17,22 @@ enum WeatherConditions: Int, CaseIterable, Identifiable {
|
|||
|
||||
var id: Int { self.rawValue }
|
||||
var symbolName: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .clear:
|
||||
return "sparkle"
|
||||
case .cloudy:
|
||||
return "cloud"
|
||||
case .hot:
|
||||
return "sun.max.trianglebadge.exclamationmark.fill"
|
||||
case .rain:
|
||||
return "cloud.rain"
|
||||
case .frigid:
|
||||
return "thermometer.snowflake"
|
||||
case .smoky:
|
||||
return "smoke"
|
||||
case .snow:
|
||||
return "cloud.snow"
|
||||
}
|
||||
switch self {
|
||||
|
||||
case .clear:
|
||||
return "sparkle"
|
||||
case .cloudy:
|
||||
return "cloud"
|
||||
case .hot:
|
||||
return "sun.max.trianglebadge.exclamationmark.fill"
|
||||
case .rain:
|
||||
return "cloud.rain"
|
||||
case .frigid:
|
||||
return "thermometer.snowflake"
|
||||
case .smoky:
|
||||
return "smoke"
|
||||
case .snow:
|
||||
return "cloud.snow"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,24 +13,24 @@ struct CsvDocument: FileDocument {
|
|||
static var readableContentTypes = [UTType.commaSeparatedText]
|
||||
|
||||
@State var csvData: String
|
||||
|
||||
|
||||
init(emptyCsv: String = "" ) {
|
||||
|
||||
|
||||
csvData = emptyCsv
|
||||
}
|
||||
|
||||
|
||||
init(configuration: ReadConfiguration) throws {
|
||||
|
||||
|
||||
if let data = configuration.file.regularFileContents {
|
||||
|
||||
|
||||
csvData = String(decoding: data, as: UTF8.self)
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
throw CocoaError(.fileReadCorruptFile)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
|
||||
let data = Data(csvData.utf8)
|
||||
return FileWrapper(regularFileWithContents: data)
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> String {
|
||||
func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> String {
|
||||
var csvString: String = ""
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
if metricsType == 0 {
|
||||
// Create Device Metrics Header
|
||||
csvString = "\(NSLocalizedString("battery.level", comment: "")), \(NSLocalizedString("voltage", comment: "")), \(NSLocalizedString("channel.utilization", comment: "")), \(NSLocalizedString("airtime", comment: "")), \(NSLocalizedString("timestamp", comment: ""))"
|
||||
for dm in telemetry{
|
||||
for dm in telemetry {
|
||||
if dm.metricsType == 0 {
|
||||
csvString += "\n"
|
||||
csvString += String(dm.batteryLevel)
|
||||
|
|
@ -31,7 +31,7 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
|
|||
} else if metricsType == 1 {
|
||||
// Create Environment Telemetry Header
|
||||
csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, \(NSLocalizedString("voltage", comment: "")), \(NSLocalizedString("current", comment: "")), \(NSLocalizedString("timestamp", comment: ""))"
|
||||
for dm in telemetry{
|
||||
for dm in telemetry {
|
||||
if dm.metricsType == 1 {
|
||||
csvString += "\n"
|
||||
csvString += String(dm.temperature.localeTemperature())
|
||||
|
|
@ -53,7 +53,7 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
|
|||
return csvString
|
||||
}
|
||||
|
||||
func PositionToCsvFile(positions: [PositionEntity]) -> String {
|
||||
func positionToCsvFile(positions: [PositionEntity]) -> String {
|
||||
var csvString: String = ""
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,19 +7,19 @@
|
|||
import SwiftUI
|
||||
|
||||
class SwiftUIEmojiTextField: UITextField {
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
|
||||
func setEmoji() {
|
||||
_ = self.textInputMode
|
||||
}
|
||||
|
||||
|
||||
override var textInputContextIdentifier: String? {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
override var textInputMode: UITextInputMode? {
|
||||
for mode in UITextInputMode.activeInputModes {
|
||||
if mode.primaryLanguage == "emoji" {
|
||||
|
|
@ -34,7 +34,7 @@ class SwiftUIEmojiTextField: UITextField {
|
|||
struct EmojiOnlyTextField: UIViewRepresentable {
|
||||
@Binding var text: String
|
||||
var placeholder: String = ""
|
||||
|
||||
|
||||
func makeUIView(context: Context) -> SwiftUIEmojiTextField {
|
||||
let emojiTextField = SwiftUIEmojiTextField()
|
||||
emojiTextField.placeholder = placeholder
|
||||
|
|
@ -42,15 +42,15 @@ struct EmojiOnlyTextField: UIViewRepresentable {
|
|||
emojiTextField.delegate = context.coordinator
|
||||
return emojiTextField
|
||||
}
|
||||
|
||||
|
||||
func updateUIView(_ uiView: SwiftUIEmojiTextField, context: Context) {
|
||||
uiView.text = text
|
||||
}
|
||||
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(parent: self)
|
||||
}
|
||||
|
||||
|
||||
class Coordinator: NSObject, UITextFieldDelegate {
|
||||
var parent: EmojiOnlyTextField
|
||||
init(parent: EmojiOnlyTextField) {
|
||||
|
|
@ -63,12 +63,3 @@ struct EmojiOnlyTextField: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//struct EmojiContentView: View {
|
||||
//
|
||||
// @State private var text: String = ""
|
||||
//
|
||||
// var body: some View {
|
||||
// EmojiTextField(text: $text, placeholder: "Enter emoji")
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ extension Data {
|
|||
}
|
||||
var hexDescription: String {
|
||||
return reduce("") {$0 + String(format: "%02x", $1)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
|
|
@ -53,8 +53,8 @@ extension Float {
|
|||
let locale = NSLocale.current as NSLocale
|
||||
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
|
||||
var format: UnitTemperature = .celsius
|
||||
|
||||
if localeUnit! as! String == "Fahrenheit" {
|
||||
|
||||
if localeUnit! as? String == "Fahrenheit" {
|
||||
format = .fahrenheit
|
||||
}
|
||||
return temperature.converted(to: format).value
|
||||
|
|
@ -90,7 +90,7 @@ extension UIImage {
|
|||
}
|
||||
|
||||
extension String {
|
||||
|
||||
|
||||
func base64urlToBase64() -> String {
|
||||
var base64 = self
|
||||
.replacingOccurrences(of: "-", with: "+")
|
||||
|
|
@ -100,7 +100,7 @@ extension String {
|
|||
}
|
||||
return base64
|
||||
}
|
||||
|
||||
|
||||
func base64ToBase64url() -> String {
|
||||
let base64url = self
|
||||
.replacingOccurrences(of: "+", with: "-")
|
||||
|
|
@ -108,13 +108,12 @@ extension String {
|
|||
.replacingOccurrences(of: "=", with: "")
|
||||
return base64url
|
||||
}
|
||||
|
||||
|
||||
func onlyEmojis() -> Bool {
|
||||
return count > 0 && !contains { !$0.isEmoji }
|
||||
}
|
||||
|
||||
func image(fontSize:CGFloat = 40, bgColor:UIColor = UIColor.clear, imageSize:CGSize? = nil) -> UIImage?
|
||||
{
|
||||
|
||||
func image(fontSize: CGFloat = 40, bgColor: UIColor = UIColor.clear, imageSize: CGSize? = nil) -> UIImage? {
|
||||
let font = UIFont.systemFont(ofSize: fontSize)
|
||||
let attributes = [NSAttributedString.Key.font: font]
|
||||
let imageSize = imageSize ?? self.size(withAttributes: attributes)
|
||||
|
|
@ -127,7 +126,7 @@ extension String {
|
|||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
}
|
||||
|
||||
|
||||
func camelCaseToWords() -> String {
|
||||
return unicodeScalars.dropFirst().reduce(String(prefix(1))) {
|
||||
return CharacterSet.uppercaseLetters.contains($1)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
}
|
||||
return altitude
|
||||
}
|
||||
|
||||
|
||||
static var currentSpeed: CLLocationSpeed {
|
||||
|
||||
guard let speed = shared.locationManager.location?.speed else {
|
||||
|
|
@ -33,7 +33,7 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
}
|
||||
return speed
|
||||
}
|
||||
|
||||
|
||||
static var currentHeading: CLLocationDirection {
|
||||
|
||||
guard let heading = shared.locationManager.location?.course else {
|
||||
|
|
@ -41,7 +41,7 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
}
|
||||
return heading
|
||||
}
|
||||
|
||||
|
||||
static var currentTimestamp: Date {
|
||||
|
||||
guard let timestamp = shared.locationManager.location?.timestamp else {
|
||||
|
|
@ -49,21 +49,21 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
}
|
||||
return timestamp
|
||||
}
|
||||
|
||||
|
||||
static var satsInView: Int {
|
||||
// If we have a position we have a sat
|
||||
var sats = 1
|
||||
if shared.locationManager.location?.verticalAccuracy ?? 0 > 0 {
|
||||
sats = 4
|
||||
if 0...5 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
if 0...5 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 12
|
||||
} else if 6...15 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
} else if 6...15 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 10
|
||||
} else if 16...30 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
} else if 16...30 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 9
|
||||
} else if 31...45 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
} else if 31...45 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 7
|
||||
} else if 46...60 ~= shared.locationManager.location?.horizontalAccuracy ?? 0{
|
||||
} else if 46...60 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
sats = 5
|
||||
}
|
||||
} else if shared.locationManager.location?.verticalAccuracy ?? 0 < 0 && 60...300 ~= shared.locationManager.location?.horizontalAccuracy ?? 0 {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class MeshLogger {
|
|||
}
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmssSSa", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mm:ss.SS a")
|
||||
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = dateFormatString
|
||||
let timestamp = formatter.string(from: Date())
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ import ActivityKit
|
|||
#endif
|
||||
|
||||
func generateMessageMarkdown (message: String) -> String {
|
||||
|
||||
|
||||
let types: NSTextCheckingResult.CheckingType = [.address, .link, .phoneNumber]
|
||||
let detector = try! NSDataDetector(types: types.rawValue)
|
||||
let matches = detector.matches(in: message, options: [], range: NSRange(location: 0, length: message.utf16.count))
|
||||
var messageWithMarkdown = message
|
||||
if matches.count > 0 {
|
||||
|
||||
|
||||
for match in matches {
|
||||
guard let range = Range(match.range, in: message) else { continue }
|
||||
if match.resultType == .address {
|
||||
|
|
@ -39,8 +39,8 @@ func generateMessageMarkdown (message: String) -> String {
|
|||
return messageWithMarkdown
|
||||
}
|
||||
|
||||
func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
func localConfig (config: Config, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
// We don't care about any of the Power settings, config is available for everything else
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) {
|
||||
upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: nodeNum, context: context)
|
||||
|
|
@ -57,8 +57,8 @@ func localConfig (config: Config, context:NSManagedObjectContext, nodeNum: Int64
|
|||
}
|
||||
}
|
||||
|
||||
func moduleConfig (config: ModuleConfig, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(config.cannedMessage) {
|
||||
upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum, context: context)
|
||||
} else if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(config.externalNotification) {
|
||||
|
|
@ -75,18 +75,20 @@ func moduleConfig (config: ModuleConfig, context:NSManagedObjectContext, nodeNum
|
|||
}
|
||||
|
||||
func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedObjectContext) -> MyInfoEntity? {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.myinfo %@", comment: "MyInfo received: %@"), String(myInfo.myNodeNum))
|
||||
MeshLogger.log("ℹ️ \(logString)")
|
||||
|
||||
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(myInfo.myNodeNum))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else {
|
||||
return nil
|
||||
}
|
||||
// Not Found Insert
|
||||
if fetchedMyInfo.isEmpty {
|
||||
|
||||
|
||||
let myInfoEntity = MyInfoEntity(context: context)
|
||||
myInfoEntity.peripheralId = peripheralId
|
||||
myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum)
|
||||
|
|
@ -111,19 +113,19 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO
|
|||
print("💥 Error Inserting New Core Data MyInfoEntity: \(nsError)")
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
fetchedMyInfo[0].peripheralId = peripheralId
|
||||
fetchedMyInfo[0].myNodeNum = Int64(myInfo.myNodeNum)
|
||||
fetchedMyInfo[0].hasGps = myInfo.hasGps_p
|
||||
fetchedMyInfo[0].bitrate = myInfo.bitrate
|
||||
let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")//.lastIndex(of: ".", offsetBy: -1)
|
||||
var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset:6, in: myInfo.firmwareVersion))]
|
||||
let lastDotIndex = myInfo.firmwareVersion.lastIndex(of: ".")// .lastIndex(of: ".", offsetBy: -1)
|
||||
var version = myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: myInfo.firmwareVersion))]
|
||||
version = version.dropLast()
|
||||
fetchedMyInfo[0].firmwareVersion = String(version)
|
||||
fetchedMyInfo[0].messageTimeoutMsec = Int32(bitPattern: myInfo.messageTimeoutMsec)
|
||||
fetchedMyInfo[0].minAppVersion = Int32(bitPattern: myInfo.minAppVersion)
|
||||
fetchedMyInfo[0].maxChannels = Int32(bitPattern: myInfo.maxChannels)
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated myInfo for node number: \(String(myInfo.myNodeNum))")
|
||||
|
|
@ -141,18 +143,19 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO
|
|||
}
|
||||
|
||||
func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled {
|
||||
|
||||
|
||||
if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled {
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.channel.received %d %@", comment: "Channel %d received from: %@"), channel.index, String(fromNum))
|
||||
MeshLogger.log("🎛️ \(logString)")
|
||||
|
||||
|
||||
let fetchedMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchedMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", fromNum)
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as! [MyInfoEntity]
|
||||
guard let fetchedMyInfo = try context.fetch(fetchedMyInfoRequest) as? [MyInfoEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedMyInfo.count == 1 {
|
||||
let newChannel = ChannelEntity(context: context)
|
||||
newChannel.id = Int32(channel.index)
|
||||
|
|
@ -162,7 +165,9 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
|
|||
newChannel.name = channel.settings.name
|
||||
newChannel.role = Int32(channel.role.rawValue)
|
||||
newChannel.psk = channel.settings.psk
|
||||
let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as! NSMutableOrderedSet
|
||||
guard let mutableChannels = fetchedMyInfo[0].channels!.mutableCopy() as? NSMutableOrderedSet else {
|
||||
return
|
||||
}
|
||||
if mutableChannels.contains(newChannel) {
|
||||
mutableChannels.replaceObject(at: Int(newChannel.index), with: newChannel)
|
||||
} else {
|
||||
|
|
@ -190,17 +195,18 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
|
|||
}
|
||||
|
||||
func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
if metadata.isInitialized {
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.metadata.received %@", comment: "Device Metadata admin message received from: %@"), String(fromNum))
|
||||
MeshLogger.log("🏷️ \(logString)")
|
||||
|
||||
|
||||
let fetchedNodeRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchedNodeRequest.predicate = NSPredicate(format: "num == %lld", fromNum)
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchedNodeRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchedNodeRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedNode.count > 0 {
|
||||
let newMetadata = DeviceMetadataEntity(context: context)
|
||||
newMetadata.firmwareVersion = metadata.firmwareVersion
|
||||
|
|
@ -229,26 +235,27 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS
|
|||
}
|
||||
|
||||
func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(nodeInfo.num))
|
||||
MeshLogger.log("📟 \(logString)")
|
||||
|
||||
guard (nodeInfo.num > 0) else { return nil }
|
||||
|
||||
|
||||
guard nodeInfo.num > 0 else { return nil }
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeInfo.num))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return nil
|
||||
}
|
||||
// Not Found Insert
|
||||
if fetchedNode.isEmpty && nodeInfo.hasUser {
|
||||
|
||||
|
||||
let newNode = NodeInfoEntity(context: context)
|
||||
newNode.id = Int64(nodeInfo.num)
|
||||
newNode.num = Int64(nodeInfo.num)
|
||||
newNode.channel = Int32(channel)
|
||||
|
||||
|
||||
if nodeInfo.hasDeviceMetrics {
|
||||
let telemetry = TelemetryEntity(context: context)
|
||||
telemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel)
|
||||
|
|
@ -259,7 +266,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newTelemetries.append(telemetry)
|
||||
newNode.telemetries? = NSOrderedSet(array: newTelemetries)
|
||||
}
|
||||
|
||||
|
||||
newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard)))
|
||||
newNode.snr = nodeInfo.snr
|
||||
if nodeInfo.hasUser {
|
||||
|
|
@ -272,9 +279,8 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newUser.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
newNode.user = newUser
|
||||
}
|
||||
|
||||
if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000)
|
||||
{
|
||||
|
||||
if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) {
|
||||
let position = PositionEntity(context: context)
|
||||
position.seqNo = Int32(nodeInfo.position.seqNumber)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
|
|
@ -288,14 +294,15 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newPostions.append(position)
|
||||
newNode.positions? = NSOrderedSet(array: newPostions)
|
||||
}
|
||||
|
||||
|
||||
// Look for a MyInfo
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else {
|
||||
return nil
|
||||
}
|
||||
if fetchedMyInfo.count > 0 {
|
||||
newNode.myInfo = fetchedMyInfo[0]
|
||||
}
|
||||
|
|
@ -311,15 +318,15 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
print("💥 Fetch MyInfo Error")
|
||||
}
|
||||
} else if nodeInfo.hasUser && nodeInfo.num > 0 {
|
||||
|
||||
|
||||
fetchedNode[0].id = Int64(nodeInfo.num)
|
||||
fetchedNode[0].num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(nodeInfo.lastHeard)))
|
||||
fetchedNode[0].snr = nodeInfo.snr
|
||||
fetchedNode[0].channel = Int32(channel)
|
||||
|
||||
|
||||
if nodeInfo.hasUser {
|
||||
|
||||
|
||||
fetchedNode[0].user!.userId = nodeInfo.user.id
|
||||
fetchedNode[0].user!.num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].user!.longName = nodeInfo.user.longName
|
||||
|
|
@ -327,9 +334,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
fetchedNode[0].user!.macaddr = nodeInfo.user.macaddr
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
}
|
||||
|
||||
|
||||
if nodeInfo.hasDeviceMetrics {
|
||||
|
||||
|
||||
let newTelemetry = TelemetryEntity(context: context)
|
||||
newTelemetry.batteryLevel = Int32(nodeInfo.deviceMetrics.batteryLevel)
|
||||
newTelemetry.voltage = nodeInfo.deviceMetrics.voltage
|
||||
|
|
@ -338,12 +345,11 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as! NSMutableOrderedSet
|
||||
fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet
|
||||
}
|
||||
|
||||
|
||||
if nodeInfo.hasPosition {
|
||||
|
||||
|
||||
|
||||
if nodeInfo.position.longitudeI > 0 || nodeInfo.position.latitudeI > 0 && (nodeInfo.position.latitudeI != 373346000 && nodeInfo.position.longitudeI != -1220090000) {
|
||||
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latitudeI = nodeInfo.position.latitudeI
|
||||
position.longitudeI = nodeInfo.position.longitudeI
|
||||
|
|
@ -353,15 +359,17 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Look for a MyInfo
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(nodeInfo.num))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else {
|
||||
return nil
|
||||
}
|
||||
if fetchedMyInfo.count > 0 {
|
||||
fetchedNode[0].myInfo = fetchedMyInfo[0]
|
||||
}
|
||||
|
|
@ -385,26 +393,26 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
}
|
||||
|
||||
func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(packet.from))
|
||||
MeshLogger.log("📟 \(logString)")
|
||||
|
||||
guard (packet.from > 0) else { return }
|
||||
|
||||
|
||||
guard packet.from > 0 else { return }
|
||||
|
||||
let fetchNodeInfoAppRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoAppRequest) as? [NodeInfoEntity] ?? []
|
||||
|
||||
|
||||
if fetchedNode.count == 1 {
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].channel = Int32(packet.channel)
|
||||
|
||||
|
||||
if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
|
||||
if nodeInfoMessage.hasDeviceMetrics {
|
||||
let telemetry = TelemetryEntity(context: context)
|
||||
|
|
@ -442,23 +450,25 @@ func nodeInfoAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
}
|
||||
|
||||
func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
if let adminMessage = try? AdminMessage(serializedData: packet.decoded.payload) {
|
||||
|
||||
|
||||
if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getCannedMessageModuleMessagesResponse(adminMessage.getCannedMessageModuleMessagesResponse) {
|
||||
|
||||
|
||||
if let cmmc = try? CannedMessageModuleConfig(serializedData: packet.decoded.payload) {
|
||||
|
||||
|
||||
if !cmmc.messages.isEmpty {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessages.messages.received %@", comment: "Canned Messages Messages Received For: %@"), String(packet.from))
|
||||
MeshLogger.log("🥫 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedNode = try context.fetch(fetchNodeRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedNode.count == 1 {
|
||||
let messages = String(cmmc.textFormatString())
|
||||
.replacingOccurrences(of: "11: ", with: "")
|
||||
|
|
@ -481,69 +491,71 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
}
|
||||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getChannelResponse(adminMessage.getChannelResponse) {
|
||||
channelPacket(channel: adminMessage.getChannelResponse, fromNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getDeviceMetadataResponse(adminMessage.getDeviceMetadataResponse) {
|
||||
deviceMetadataPacket(metadata: adminMessage.getDeviceMetadataResponse, fromNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getConfigResponse(adminMessage.getConfigResponse) {
|
||||
|
||||
|
||||
let config = adminMessage.getConfigResponse
|
||||
|
||||
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) {
|
||||
upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) {
|
||||
upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) {
|
||||
upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) {
|
||||
upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) {
|
||||
upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
}
|
||||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getModuleConfigResponse(adminMessage.getModuleConfigResponse) {
|
||||
|
||||
|
||||
let moduleConfig = adminMessage.getModuleConfigResponse
|
||||
|
||||
|
||||
if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfig.cannedMessage) {
|
||||
upsertCannedMessagesModuleConfigPacket(config: moduleConfig.cannedMessage, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(moduleConfig.externalNotification) {
|
||||
upsertExternalNotificationModuleConfigPacket(config: moduleConfig.externalNotification, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(moduleConfig.mqtt) {
|
||||
upsertMqttModuleConfigPacket(config: moduleConfig.mqtt, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(moduleConfig.rangeTest) {
|
||||
upsertRangeTestModuleConfigPacket(config: moduleConfig.rangeTest, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(moduleConfig.serial) {
|
||||
upsertSerialModuleConfigPacket(config: moduleConfig.serial, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(moduleConfig.telemetry) {
|
||||
upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
MeshLogger.log("🕸️ MESH PACKET received for Admin App \(try! packet.decoded.jsonString())")
|
||||
}
|
||||
|
||||
|
||||
// Save an ack for the admin message log for each admin message response received as we stopped sending acks if there is also a response to reduce airtime.
|
||||
adminResponseAck(packet: packet, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let fetchedAdminMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
|
||||
fetchedAdminMessageRequest.predicate = NSPredicate(format: "messageId == %lld", packet.decoded.requestID)
|
||||
do {
|
||||
let fetchedMessage = try context.fetch(fetchedAdminMessageRequest) as! [MessageEntity]
|
||||
guard let fetchedMessage = try context.fetch(fetchedAdminMessageRequest) as? [MessageEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedMessage.count > 0 {
|
||||
fetchedMessage[0].ackTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
fetchedMessage[0].ackError = Int32(RoutingError.none.rawValue)
|
||||
|
|
@ -565,22 +577,22 @@ func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
}
|
||||
|
||||
func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
if let routingMessage = try? Routing(serializedData: packet.decoded.payload) {
|
||||
|
||||
|
||||
let routingError = RoutingError(rawValue: routingMessage.errorReason.rawValue)
|
||||
|
||||
|
||||
let routingErrorString = routingError?.display ?? NSLocalizedString("unknown", comment: "")
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.routing.message %@ %@", comment: "Routing received for RequestID: %@ Ack Status: %@"), String(packet.decoded.requestID), routingErrorString)
|
||||
MeshLogger.log("🕸️ \(logString)")
|
||||
|
||||
|
||||
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
|
||||
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity]
|
||||
if fetchedMessage?.count ?? 0 > 0 {
|
||||
|
||||
|
||||
if fetchedMessage![0].toUser != nil {
|
||||
// Real ACK from DM Recipient
|
||||
if packet.to != packet.from {
|
||||
|
|
@ -588,14 +600,14 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
|
|||
}
|
||||
}
|
||||
fetchedMessage![0].ackError = Int32(routingMessage.errorReason.rawValue)
|
||||
|
||||
|
||||
if routingMessage.errorReason == Routing.Error.none {
|
||||
|
||||
|
||||
fetchedMessage![0].receivedACK = true
|
||||
}
|
||||
fetchedMessage![0].ackSNR = packet.rxSnr
|
||||
fetchedMessage![0].ackTimestamp = Int32(packet.rxTime)
|
||||
|
||||
|
||||
if fetchedMessage![0].toUser != nil {
|
||||
fetchedMessage![0].toUser?.objectWillChange.send()
|
||||
} else {
|
||||
|
|
@ -604,19 +616,19 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
|
|||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity]
|
||||
if fetchedMyInfo?.count ?? 0 > 0 {
|
||||
|
||||
|
||||
for ch in fetchedMyInfo![0].channels!.array as! [ChannelEntity] {
|
||||
|
||||
|
||||
if ch.index == packet.channel {
|
||||
ch.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
|
@ -631,26 +643,28 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
|
|||
}
|
||||
|
||||
func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) {
|
||||
|
||||
|
||||
// Only log telemetry from the mesh not the connected device
|
||||
if connectedNode != Int64(packet.from) {
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.received %@", comment: "Telemetry received for: %@"), String(packet.from))
|
||||
MeshLogger.log("📈 \(logString)")
|
||||
} else {
|
||||
// If it is the connected node
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
let telemetry = TelemetryEntity(context: context)
|
||||
|
||||
|
||||
let fetchNodeTelemetryRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedNode.count == 1 {
|
||||
if telemetryMessage.variant == Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) {
|
||||
// Device Metrics
|
||||
|
|
@ -681,23 +695,42 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
if connectedNode != Int64(packet.from) {
|
||||
print("💾 Telemetry Saved for Node: \(packet.from)")
|
||||
} else if telemetry.metricsType == 0 {
|
||||
// Update our live activity if there is one running
|
||||
// Connected Device Metrics
|
||||
// ------------------------
|
||||
// Low Battery notification
|
||||
if telemetry.batteryLevel > 0 && telemetry.batteryLevel < 5 {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = "Critically Low Battery!"
|
||||
content.body = "Time to charge your radio, there is \(telemetry.batteryLevel)% battery remaining."
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let uuidString = UUID().uuidString
|
||||
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
|
||||
let notificationCenter = UNUserNotificationCenter.current()
|
||||
notificationCenter.add(request) { (error) in
|
||||
if error != nil {
|
||||
// Handle any errors.
|
||||
print("Error creating local low battery notification: \(error?.localizedDescription ?? "no description")")
|
||||
} else {
|
||||
print("Created local low battery notification.")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update our live activity if there is one running, not available on mac iOS >= 16.2
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
if #available(iOS 16.2, *) {
|
||||
|
||||
let oneMinuteLater = Calendar.current.date(byAdding: .minute, value: (Int(1) ), to: Date())!
|
||||
let date = Date.now...oneMinuteLater
|
||||
let updatedMeshStatus = MeshActivityAttributes.MeshActivityStatus(timerRange: date, connected: true, channelUtilization: telemetry.channelUtilization, airtime: telemetry.airUtilTx, batteryLevel: UInt32(telemetry.batteryLevel))
|
||||
|
||||
let alertConfiguration = AlertConfiguration(title: "Mesh activity update", body: "Updated Device Metrics Data.", sound: .default)
|
||||
let updatedContent = ActivityContent(state: updatedMeshStatus, staleDate: nil)
|
||||
print("Update live activity.")
|
||||
|
||||
|
||||
let meshActivity = Activity<MeshActivityAttributes>.activities.first(where: { $0.attributes.nodeNum == connectedNode })
|
||||
if meshActivity != nil {
|
||||
Task {
|
||||
await meshActivity?.update(updatedContent, alertConfiguration: alertConfiguration)
|
||||
//await meshActivity?.update(updatedContent)
|
||||
// await meshActivity?.update(updatedContent)
|
||||
print("Updated live activity.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -714,17 +747,17 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
}
|
||||
|
||||
func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) {
|
||||
|
||||
|
||||
MeshLogger.log("💬 \(NSLocalizedString("mesh.log.textmessage.received", comment: "Message received from the text message app"))")
|
||||
|
||||
|
||||
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
|
||||
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
|
||||
|
||||
do {
|
||||
|
||||
let fetchedUsers = try context.fetch(messageUsers) as! [UserEntity]
|
||||
guard let fetchedUsers = try context.fetch(messageUsers) as? [UserEntity] else {
|
||||
return
|
||||
}
|
||||
let newMessage = MessageEntity(context: context)
|
||||
newMessage.messageId = Int64(packet.id)
|
||||
newMessage.messageTimestamp = Int32(bitPattern: packet.rxTime)
|
||||
|
|
@ -732,11 +765,11 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
|
|||
newMessage.snr = packet.rxSnr
|
||||
newMessage.isEmoji = packet.decoded.emoji == 1
|
||||
newMessage.channel = Int32(packet.channel)
|
||||
|
||||
|
||||
if packet.decoded.replyID > 0 {
|
||||
newMessage.replyID = Int64(packet.decoded.replyID)
|
||||
}
|
||||
|
||||
|
||||
if fetchedUsers.first(where: { $0.num == packet.to }) != nil && packet.to != 4294967295 {
|
||||
newMessage.toUser = fetchedUsers.first(where: { $0.num == packet.to })
|
||||
}
|
||||
|
|
@ -745,20 +778,20 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
|
|||
}
|
||||
newMessage.messagePayload = messageText
|
||||
newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: messageText)
|
||||
|
||||
|
||||
newMessage.fromUser?.objectWillChange.send()
|
||||
newMessage.toUser?.objectWillChange.send()
|
||||
|
||||
|
||||
var messageSaved = false
|
||||
|
||||
|
||||
do {
|
||||
|
||||
|
||||
try context.save()
|
||||
print("💾 Saved a new message for \(newMessage.messageId)")
|
||||
messageSaved = true
|
||||
|
||||
|
||||
if messageSaved {
|
||||
|
||||
|
||||
if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) {
|
||||
// Create an iOS Notification for the received DM message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
|
|
@ -772,17 +805,19 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
|
|||
manager.schedule()
|
||||
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
|
||||
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
|
||||
|
||||
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
guard let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] else {
|
||||
return
|
||||
}
|
||||
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 {
|
||||
// Create an iOS Notification for the received private channel message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
|
|
@ -798,7 +833,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
|
|||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -814,21 +849,22 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
|
|||
}
|
||||
|
||||
func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.received %@", comment: "Waypoint Packet received from node: %@"), String(packet.from))
|
||||
MeshLogger.log("📍 \(logString)")
|
||||
|
||||
|
||||
let fetchWaypointRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "WaypointEntity")
|
||||
fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id))
|
||||
|
||||
do {
|
||||
|
||||
if let waypointMessage = try? Waypoint(serializedData: packet.decoded.payload) {
|
||||
|
||||
let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as! [WaypointEntity]
|
||||
do {
|
||||
|
||||
if let waypointMessage = try? Waypoint(serializedData: packet.decoded.payload) {
|
||||
guard let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as? [WaypointEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedWaypoint.isEmpty {
|
||||
let waypoint = WaypointEntity(context: context)
|
||||
|
||||
|
||||
waypoint.id = Int64(packet.id)
|
||||
waypoint.name = waypointMessage.name
|
||||
waypoint.longDescription = waypointMessage.description_p
|
||||
|
|
@ -838,7 +874,7 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
waypoint.locked = Int64(waypointMessage.lockedTo)
|
||||
if waypointMessage.expire > 0 {
|
||||
waypoint.expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire)))
|
||||
}else {
|
||||
} else {
|
||||
waypoint.expire = nil
|
||||
}
|
||||
waypoint.created = Date()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV8.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV9.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,305 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D68" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
|
||||
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="downlinkEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<fetchedProperty name="allPrivateMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="channel == $FETCH_SOURCE.index && toUser == nil AND isEmoji == false"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="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="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
|
||||
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hwModel" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<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="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="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="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="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="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<fetchedProperty name="tapbacks" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" attributeType="String"/>
|
||||
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ntpServer" optional="YES" attributeType="String"/>
|
||||
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
|
||||
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="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="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="adminMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -5,7 +5,7 @@ import CoreData
|
|||
|
||||
@main
|
||||
struct MeshtasticAppleApp: App {
|
||||
|
||||
|
||||
let persistenceController = PersistenceController.shared
|
||||
@ObservedObject private var bleManager: BLEManager = BLEManager()
|
||||
@ObservedObject private var userSettings: UserSettings = UserSettings()
|
||||
|
|
@ -30,9 +30,9 @@ struct MeshtasticAppleApp: App {
|
|||
|
||||
print("URL received \(userActivity)")
|
||||
self.incomingUrl = userActivity.webpageURL
|
||||
|
||||
if ((self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/e/#")) != nil) {
|
||||
|
||||
|
||||
if (self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/e/#")) != nil {
|
||||
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.channelSettings = components.last!
|
||||
}
|
||||
|
|
@ -44,10 +44,10 @@ struct MeshtasticAppleApp: App {
|
|||
}
|
||||
}
|
||||
.onOpenURL(perform: { (url) in
|
||||
|
||||
|
||||
print("Some sort of URL was received \(url)")
|
||||
self.incomingUrl = url
|
||||
|
||||
|
||||
if url.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.channelSettings = components.last!
|
||||
|
|
@ -59,37 +59,37 @@ struct MeshtasticAppleApp: App {
|
|||
print("User wants to import a MBTILES offline map file: \(self.incomingUrl?.absoluteString ?? "No Tiles link")")
|
||||
}
|
||||
|
||||
//we are expecting a .mbtiles map file that contains raster data
|
||||
//save it to the documents directory, and name it offline_map.mbtiles
|
||||
// we are expecting a .mbtiles map file that contains raster data
|
||||
// save it to the documents directory, and name it offline_map.mbtiles
|
||||
let fileManager = FileManager.default
|
||||
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let destination = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false)
|
||||
|
||||
|
||||
if !self.saveChannels {
|
||||
|
||||
//tell the system we want the file please
|
||||
|
||||
// tell the system we want the file please
|
||||
guard url.startAccessingSecurityScopedResource() else {
|
||||
return
|
||||
}
|
||||
|
||||
//do we need to delete an old one?
|
||||
if (fileManager.fileExists(atPath: destination.path)) {
|
||||
|
||||
// do we need to delete an old one?
|
||||
if fileManager.fileExists(atPath: destination.path) {
|
||||
print("ℹ️ Found an old map file. Deleting it")
|
||||
try? fileManager.removeItem(atPath: destination.path)
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try fileManager.copyItem(at: url, to: destination)
|
||||
} catch {
|
||||
print("Copy MB Tile file failed. Error: \(error)")
|
||||
}
|
||||
|
||||
if (fileManager.fileExists(atPath: destination.path)) {
|
||||
|
||||
if fileManager.fileExists(atPath: destination.path) {
|
||||
print("ℹ️ Saved the map file")
|
||||
|
||||
//need to tell the map view that it needs to update and try loading the new overlay
|
||||
|
||||
// need to tell the map view that it needs to update and try loading the new overlay
|
||||
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastUpdatedLocalMapFile")
|
||||
|
||||
|
||||
} else {
|
||||
print("💥 Didn't save the map file")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,15 +23,13 @@ struct Peripheral: Identifiable {
|
|||
self.lastUpdate = lastUpdate
|
||||
self.peripheral = peripheral
|
||||
}
|
||||
|
||||
|
||||
func getSignalStrength() -> SignalStrength {
|
||||
if (NSNumber(value: rssi).compare(NSNumber(-65)) == ComparisonResult.orderedDescending) {
|
||||
if NSNumber(value: rssi).compare(NSNumber(-65)) == ComparisonResult.orderedDescending {
|
||||
return SignalStrength.strong
|
||||
}
|
||||
else if (NSNumber(value: rssi).compare(NSNumber(-85)) == ComparisonResult.orderedDescending) {
|
||||
} else if NSNumber(value: rssi).compare(NSNumber(-85)) == ComparisonResult.orderedDescending {
|
||||
return SignalStrength.normal
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return SignalStrength.weak
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class UserSettings: ObservableObject {
|
|||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
|
||||
self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
import Foundation
|
||||
|
||||
extension ChannelEntity {
|
||||
|
||||
|
||||
var allPrivateMessages: [MessageEntity] {
|
||||
|
||||
|
||||
self.value(forKey: "allPrivateMessages") as? [MessageEntity] ?? [MessageEntity]()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,19 +32,19 @@ class PersistenceController {
|
|||
let container: NSPersistentContainer
|
||||
|
||||
init(inMemory: Bool = false) {
|
||||
|
||||
|
||||
container = NSPersistentContainer(name: "Meshtastic")
|
||||
|
||||
|
||||
if inMemory {
|
||||
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
||||
}
|
||||
|
||||
|
||||
container.loadPersistentStores(completionHandler: { (_, error) in
|
||||
|
||||
|
||||
// Merge policy that favors in memory data over data in the db
|
||||
self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
self.container.viewContext.automaticallyMergesChangesFromParent = true
|
||||
|
||||
|
||||
if let error = error as NSError? {
|
||||
|
||||
print("💥 CoreData Error: \(error.localizedDescription). Now attempting to truncate CoreData database. All app data will be lost.")
|
||||
|
|
@ -52,18 +52,18 @@ class PersistenceController {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
public func clearDatabase() {
|
||||
guard let url = self.container.persistentStoreDescriptions.first?.url else { return }
|
||||
|
||||
let persistentStoreCoordinator = self.container.persistentStoreCoordinator
|
||||
|
||||
do {
|
||||
|
||||
try persistentStoreCoordinator.destroyPersistentStore(at:url, ofType: NSSQLiteStoreType, options: nil)
|
||||
|
||||
try persistentStoreCoordinator.destroyPersistentStore(at: url, ofType: NSSQLiteStoreType, options: nil)
|
||||
try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
|
||||
print("💥 CoreData database truncated. All app data has been erased.")
|
||||
|
||||
|
||||
} catch let error {
|
||||
print("💣 Failed to destroy CoreData database, delete the app and re-install to clear data. Attempted to clear persistent store: " + error.localizedDescription)
|
||||
}
|
||||
|
|
@ -71,17 +71,17 @@ class PersistenceController {
|
|||
}
|
||||
|
||||
extension NSManagedObjectContext {
|
||||
|
||||
|
||||
/// Executes the given `NSBatchDeleteRequest` and directly merges the changes to bring the given managed object context up to date.
|
||||
///
|
||||
/// - Parameter batchDeleteRequest: The `NSBatchDeleteRequest` to execute.
|
||||
/// - Throws: An error if anything went wrong executing the batch deletion.
|
||||
public func executeAndMergeChanges(using batchDeleteRequest: NSBatchDeleteRequest) throws {
|
||||
batchDeleteRequest.resultType = .resultTypeObjectIDs
|
||||
|
||||
|
||||
let result = try execute(batchDeleteRequest) as? NSBatchDeleteResult
|
||||
let changes: [AnyHashable: Any] = [NSDeletedObjectsKey: result?.result as? [NSManagedObjectID] ?? []]
|
||||
|
||||
|
||||
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ extension PositionEntity {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var nodeLocation: CLLocation? {
|
||||
if latitudeI != 0 && longitudeI != 0 {
|
||||
let location = CLLocation(latitude: latitude!, longitude: longitude!)
|
||||
|
|
@ -40,7 +40,7 @@ extension PositionEntity {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var annotaton: MKPointAnnotation {
|
||||
let pointAnn = MKPointAnnotation()
|
||||
if nodeCoordinate != nil {
|
||||
|
|
|
|||
|
|
@ -8,14 +8,16 @@
|
|||
import CoreData
|
||||
|
||||
public func getNodeInfo(id: Int64, context: NSManagedObjectContext) -> NodeInfoEntity? {
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(id))
|
||||
|
||||
|
||||
do {
|
||||
let fetchNodeInfo = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
if fetchNodeInfo.count == 1 {
|
||||
return fetchNodeInfo[0]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return nil
|
||||
}
|
||||
if fetchedNode.count == 1 {
|
||||
return fetchedNode[0]
|
||||
}
|
||||
} catch {
|
||||
return nil
|
||||
|
|
@ -24,12 +26,14 @@ public func getNodeInfo(id: Int64, context: NSManagedObjectContext) -> NodeInfoE
|
|||
}
|
||||
|
||||
public func getUser(id: Int64, context: NSManagedObjectContext) -> UserEntity {
|
||||
|
||||
|
||||
let fetchUserRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
|
||||
fetchUserRequest.predicate = NSPredicate(format: "num == %lld", Int64(id))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedUser = try context.fetch(fetchUserRequest) as! [UserEntity]
|
||||
guard let fetchedUser = try context.fetch(fetchUserRequest) as? [UserEntity] else {
|
||||
return UserEntity(context: context)
|
||||
}
|
||||
if fetchedUser.count == 1 {
|
||||
return fetchedUser[0]
|
||||
}
|
||||
|
|
@ -40,12 +44,14 @@ public func getUser(id: Int64, context: NSManagedObjectContext) -> UserEntity {
|
|||
}
|
||||
|
||||
public func getWaypoint(id: Int64, context: NSManagedObjectContext) -> WaypointEntity {
|
||||
|
||||
|
||||
let fetchWaypointRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "WaypointEntity")
|
||||
fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(id))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as! [WaypointEntity]
|
||||
guard let fetchedWaypoint = try context.fetch(fetchWaypointRequest) as? [WaypointEntity] else {
|
||||
return WaypointEntity(context: context)
|
||||
}
|
||||
if fetchedWaypoint.count == 1 {
|
||||
return fetchedWaypoint[0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,26 +7,24 @@
|
|||
import CoreData
|
||||
|
||||
public func clearPositions(destNum: Int64, context: NSManagedObjectContext) -> Bool {
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(destNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return false
|
||||
}
|
||||
let newPostions = [PositionEntity]()
|
||||
fetchedNode[0].positions? = NSOrderedSet(array: newPostions)
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
return true
|
||||
|
||||
|
||||
} catch {
|
||||
context.rollback()
|
||||
return false
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("💥 Fetch NodeInfoEntity Error")
|
||||
return false
|
||||
|
|
@ -34,26 +32,24 @@ public func clearPositions(destNum: Int64, context: NSManagedObjectContext) -> B
|
|||
}
|
||||
|
||||
public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManagedObjectContext) -> Bool {
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(destNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return false
|
||||
}
|
||||
let emptyTelemetry = [TelemetryEntity]()
|
||||
fetchedNode[0].telemetries? = NSOrderedSet(array: emptyTelemetry)
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
return true
|
||||
|
||||
|
||||
} catch {
|
||||
context.rollback()
|
||||
return false
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("💥 Fetch NodeInfoEntity Error")
|
||||
return false
|
||||
|
|
@ -73,7 +69,7 @@ public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObje
|
|||
}
|
||||
|
||||
public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
do {
|
||||
let objects = user.messageList
|
||||
for object in objects {
|
||||
|
|
@ -86,13 +82,13 @@ public func deleteUserMessages(user: UserEntity, context: NSManagedObjectContext
|
|||
}
|
||||
|
||||
public func clearCoreDataDatabase(context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let persistenceController = PersistenceController.shared.container
|
||||
for i in 0...persistenceController.managedObjectModel.entities.count-1 {
|
||||
let entity = persistenceController.managedObjectModel.entities[i]
|
||||
let query = NSFetchRequest<NSFetchRequestResult>(entityName: entity.name!)
|
||||
let deleteRequest = NSBatchDeleteRequest(fetchRequest: query)
|
||||
|
||||
|
||||
do {
|
||||
try context.executeAndMergeChanges(using: deleteRequest)
|
||||
} catch let error as NSError {
|
||||
|
|
@ -102,33 +98,37 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext) {
|
|||
}
|
||||
|
||||
func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.received %@", comment: "Position Packet received from node: %@"), String(packet.from))
|
||||
MeshLogger.log("📍 \(logString)")
|
||||
|
||||
|
||||
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodePositionRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
|
||||
if let positionMessage = try? Position(serializedData: packet.decoded.payload) {
|
||||
|
||||
|
||||
// Don't save empty position packets
|
||||
if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 && (positionMessage.latitudeI != 373346000 && positionMessage.longitudeI != -1220090000)
|
||||
{
|
||||
let fetchedNode = try context.fetch(fetchNodePositionRequest) as! [NodeInfoEntity]
|
||||
if positionMessage.longitudeI > 0 || positionMessage.latitudeI > 0 && (positionMessage.latitudeI != 373346000 && positionMessage.longitudeI != -1220090000) {
|
||||
guard let fetchedNode = try context.fetch(fetchNodePositionRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedNode.count == 1 {
|
||||
|
||||
|
||||
// Unset the current latest position for this node
|
||||
let fetchCurrentLatestPositionsRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "PositionEntity")
|
||||
fetchCurrentLatestPositionsRequest.predicate = NSPredicate(format: "nodePosition.num == %lld && latest = true", Int64(packet.from))
|
||||
let fetchedPositions = try context.fetch(fetchCurrentLatestPositionsRequest) as! [PositionEntity]
|
||||
|
||||
guard let fetchedPositions = try context.fetch(fetchCurrentLatestPositionsRequest) as? [PositionEntity] else {
|
||||
return
|
||||
}
|
||||
if fetchedPositions.count > 0 {
|
||||
for position in fetchedPositions {
|
||||
position.latest = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let position = PositionEntity(context: context)
|
||||
position.latest = true
|
||||
position.snr = packet.rxSnr
|
||||
|
|
@ -144,15 +144,16 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
} else {
|
||||
position.time = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
}
|
||||
let mutablePositions = fetchedNode[0].positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
|
||||
guard let mutablePositions = fetchedNode[0].positions!.mutableCopy() as? NSMutableOrderedSet else {
|
||||
return
|
||||
}
|
||||
mutablePositions.add(position)
|
||||
fetchedNode[0].id = Int64(packet.from)
|
||||
fetchedNode[0].num = Int64(packet.from)
|
||||
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
|
||||
fetchedNode[0].snr = packet.rxSnr
|
||||
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Node Position Coordinates, SNR and Time from Position App Packet For: \(fetchedNode[0].num)")
|
||||
|
|
@ -164,7 +165,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
}
|
||||
} else {
|
||||
print("💥 Empty POSITION_APP Packet")
|
||||
print(try! packet.jsonString())
|
||||
print((try? packet.jsonString()) ?? "JSON Decode Failure")
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -173,16 +174,17 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
}
|
||||
|
||||
func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.bluetooth.config %@", comment: "Bluetooth config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("📶 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
if fetchedNode[0].bluetoothConfig == nil {
|
||||
|
|
@ -214,15 +216,16 @@ func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, node
|
|||
}
|
||||
|
||||
func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.config %@", comment: "Device config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("📟 \(logString)")
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
if fetchedNode[0].deviceConfig == nil {
|
||||
|
|
@ -232,6 +235,8 @@ func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: I
|
|||
newDeviceConfig.debugLogEnabled = config.debugLogEnabled
|
||||
newDeviceConfig.buttonGpio = Int32(config.buttonGpio)
|
||||
newDeviceConfig.buzzerGpio = Int32(config.buzzerGpio)
|
||||
newDeviceConfig.rebroadcastMode = Int32(config.rebroadcastMode.rawValue)
|
||||
newDeviceConfig.nodeInfoBroadcastSecs = Int32(config.nodeInfoBroadcastSecs)
|
||||
fetchedNode[0].deviceConfig = newDeviceConfig
|
||||
} else {
|
||||
fetchedNode[0].deviceConfig?.role = Int32(config.role.rawValue)
|
||||
|
|
@ -239,6 +244,8 @@ func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: I
|
|||
fetchedNode[0].deviceConfig?.debugLogEnabled = config.debugLogEnabled
|
||||
fetchedNode[0].deviceConfig?.buttonGpio = Int32(config.buttonGpio)
|
||||
fetchedNode[0].deviceConfig?.buzzerGpio = Int32(config.buzzerGpio)
|
||||
fetchedNode[0].deviceConfig?.rebroadcastMode = Int32(config.rebroadcastMode.rawValue)
|
||||
fetchedNode[0].deviceConfig?.nodeInfoBroadcastSecs = Int32(config.nodeInfoBroadcastSecs)
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
|
|
@ -256,22 +263,22 @@ func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: I
|
|||
}
|
||||
|
||||
func upsertDisplayConfigPacket(config: Meshtastic.Config.DisplayConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.display.config %@", comment: "Display config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("🖥️ \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
|
||||
if fetchedNode[0].displayConfig == nil {
|
||||
|
||||
|
||||
let newDisplayConfig = DisplayConfigEntity(context: context)
|
||||
newDisplayConfig.gpsFormat = Int32(config.gpsFormat.rawValue)
|
||||
newDisplayConfig.screenOnSeconds = Int32(config.screenOnSecs)
|
||||
|
|
@ -280,10 +287,11 @@ func upsertDisplayConfigPacket(config: Meshtastic.Config.DisplayConfig, nodeNum:
|
|||
newDisplayConfig.flipScreen = config.flipScreen
|
||||
newDisplayConfig.oledType = Int32(config.oled.rawValue)
|
||||
newDisplayConfig.displayMode = Int32(config.displaymode.rawValue)
|
||||
newDisplayConfig.headingBold = config.headingBold
|
||||
fetchedNode[0].displayConfig = newDisplayConfig
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
fetchedNode[0].displayConfig?.gpsFormat = Int32(config.gpsFormat.rawValue)
|
||||
fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.screenOnSecs)
|
||||
fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.autoScreenCarouselSecs)
|
||||
|
|
@ -291,43 +299,44 @@ func upsertDisplayConfigPacket(config: Meshtastic.Config.DisplayConfig, nodeNum:
|
|||
fetchedNode[0].displayConfig?.flipScreen = config.flipScreen
|
||||
fetchedNode[0].displayConfig?.oledType = Int32(config.oled.rawValue)
|
||||
fetchedNode[0].displayConfig?.displayMode = Int32(config.displaymode.rawValue)
|
||||
fetchedNode[0].displayConfig?.headingBold = config.headingBold
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
|
||||
|
||||
try context.save()
|
||||
print("💾 Updated Display Config for node number: \(String(nodeNum))")
|
||||
|
||||
|
||||
} catch {
|
||||
|
||||
|
||||
context.rollback()
|
||||
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data DisplayConfigEntity: \(nsError)")
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Display Config")
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Fetching node for core data DisplayConfigEntity failed: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.lora.config %@", comment: "LoRa config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("📻 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", nodeNum)
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save LoRa Config
|
||||
if fetchedNode.count > 0 {
|
||||
if fetchedNode[0].loRaConfig == nil {
|
||||
|
|
@ -381,16 +390,18 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64
|
|||
}
|
||||
|
||||
func upsertNetworkConfigPacket(config: Meshtastic.Config.NetworkConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.network.config %@", comment: "Network config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("🌐 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save WiFi Config
|
||||
if !fetchedNode.isEmpty {
|
||||
if fetchedNode[0].networkConfig == nil {
|
||||
|
|
@ -406,11 +417,11 @@ func upsertNetworkConfigPacket(config: Meshtastic.Config.NetworkConfig, nodeNum:
|
|||
fetchedNode[0].networkConfig?.wifiSsid = config.wifiSsid
|
||||
fetchedNode[0].networkConfig?.wifiPsk = config.wifiPsk
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Network Config for node number: \(String(nodeNum))")
|
||||
|
||||
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
|
|
@ -426,16 +437,18 @@ func upsertNetworkConfigPacket(config: Meshtastic.Config.NetworkConfig, nodeNum:
|
|||
}
|
||||
|
||||
func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.config %@", comment: "Positon config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("🗺️ \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save LoRa Config
|
||||
if !fetchedNode.isEmpty {
|
||||
if fetchedNode[0].positionConfig == nil {
|
||||
|
|
@ -475,24 +488,25 @@ func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNu
|
|||
}
|
||||
|
||||
func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.CannedMessageConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessage.config %@", comment: "Canned Message module config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("🥫 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save Canned Message Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
|
||||
if fetchedNode[0].cannedMessageConfig == nil {
|
||||
|
||||
|
||||
let newCannedMessageConfig = CannedMessageConfigEntity(context: context)
|
||||
|
||||
|
||||
newCannedMessageConfig.enabled = config.enabled
|
||||
newCannedMessageConfig.sendBell = config.sendBell
|
||||
newCannedMessageConfig.rotary1Enabled = config.rotary1Enabled
|
||||
|
|
@ -503,11 +517,11 @@ func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.Cann
|
|||
newCannedMessageConfig.inputbrokerEventCw = Int32(config.inputbrokerEventCw.rawValue)
|
||||
newCannedMessageConfig.inputbrokerEventCcw = Int32(config.inputbrokerEventCcw.rawValue)
|
||||
newCannedMessageConfig.inputbrokerEventPress = Int32(config.inputbrokerEventPress.rawValue)
|
||||
|
||||
|
||||
fetchedNode[0].cannedMessageConfig = newCannedMessageConfig
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
fetchedNode[0].cannedMessageConfig?.enabled = config.enabled
|
||||
fetchedNode[0].cannedMessageConfig?.sendBell = config.sendBell
|
||||
fetchedNode[0].cannedMessageConfig?.rotary1Enabled = config.rotary1Enabled
|
||||
|
|
@ -519,7 +533,7 @@ func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.Cann
|
|||
fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.inputbrokerEventCcw.rawValue)
|
||||
fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.inputbrokerEventPress.rawValue)
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Canned Message Module Config for node number: \(String(nodeNum))")
|
||||
|
|
@ -538,19 +552,21 @@ func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.Cann
|
|||
}
|
||||
|
||||
func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfig.ExternalNotificationConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.externalnotification.config %@", comment: "External Notifiation module config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("📣 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save External Notificaitone Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
|
||||
if fetchedNode[0].externalNotificationConfig == nil {
|
||||
let newExternalNotificationConfig = ExternalNotificationConfigEntity(context: context)
|
||||
newExternalNotificationConfig.enabled = config.enabled
|
||||
|
|
@ -568,7 +584,7 @@ func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfi
|
|||
newExternalNotificationConfig.outputMilliseconds = Int32(config.outputMs)
|
||||
newExternalNotificationConfig.nagTimeout = Int32(config.nagTimeout)
|
||||
fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig
|
||||
|
||||
|
||||
} else {
|
||||
fetchedNode[0].externalNotificationConfig?.enabled = config.enabled
|
||||
fetchedNode[0].externalNotificationConfig?.usePWM = config.usePwm
|
||||
|
|
@ -585,7 +601,7 @@ func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfi
|
|||
fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.outputMs)
|
||||
fetchedNode[0].externalNotificationConfig?.nagTimeout = Int32(config.nagTimeout)
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated External Notification Module Config for node number: \(String(nodeNum))")
|
||||
|
|
@ -604,19 +620,21 @@ func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfi
|
|||
}
|
||||
|
||||
func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.mqtt.config %@", comment: "MQTT module config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("🌉 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save MQTT Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
|
||||
if fetchedNode[0].mqttConfig == nil {
|
||||
let newMQTTConfig = MQTTConfigEntity(context: context)
|
||||
newMQTTConfig.enabled = config.enabled
|
||||
|
|
@ -652,16 +670,18 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no
|
|||
}
|
||||
|
||||
func upsertRangeTestModuleConfigPacket(config: Meshtastic.ModuleConfig.RangeTestConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.rangetest.config %@", comment: "Range Test module config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("⛰️ \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
if fetchedNode[0].rangeTestConfig == nil {
|
||||
|
|
@ -693,22 +713,24 @@ func upsertRangeTestModuleConfigPacket(config: Meshtastic.ModuleConfig.RangeTest
|
|||
}
|
||||
|
||||
func upsertSerialModuleConfigPacket(config: Meshtastic.ModuleConfig.SerialConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.serial.config %@", comment: "Serial module config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("🤖 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
|
||||
if fetchedNode[0].serialConfig == nil {
|
||||
|
||||
|
||||
let newSerialConfig = SerialConfigEntity(context: context)
|
||||
newSerialConfig.enabled = config.enabled
|
||||
newSerialConfig.echo = config.echo
|
||||
|
|
@ -718,7 +740,7 @@ func upsertSerialModuleConfigPacket(config: Meshtastic.ModuleConfig.SerialConfig
|
|||
newSerialConfig.timeout = Int32(config.timeout)
|
||||
newSerialConfig.mode = Int32(config.mode.rawValue)
|
||||
fetchedNode[0].serialConfig = newSerialConfig
|
||||
|
||||
|
||||
} else {
|
||||
fetchedNode[0].serialConfig?.enabled = config.enabled
|
||||
fetchedNode[0].serialConfig?.echo = config.echo
|
||||
|
|
@ -728,26 +750,26 @@ func upsertSerialModuleConfigPacket(config: Meshtastic.ModuleConfig.SerialConfig
|
|||
fetchedNode[0].serialConfig?.timeout = Int32(config.timeout)
|
||||
fetchedNode[0].serialConfig?.mode = Int32(config.mode.rawValue)
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Serial Module Config for node number: \(String(nodeNum))")
|
||||
|
||||
|
||||
} catch {
|
||||
|
||||
|
||||
context.rollback()
|
||||
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data SerialConfigEntity: \(nsError)")
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Serial Module Config")
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Fetching node for core data SerialConfigEntity failed: \(nsError)")
|
||||
}
|
||||
|
|
@ -757,51 +779,53 @@ func upsertTelemetryModuleConfigPacket(config: Meshtastic.ModuleConfig.Telemetry
|
|||
|
||||
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.config %@", comment: "Telemetry module config received: %@"), String(nodeNum))
|
||||
MeshLogger.log("📈 \(logString)")
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, save Telemetry Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
|
||||
if fetchedNode[0].telemetryConfig == nil {
|
||||
|
||||
|
||||
let newTelemetryConfig = TelemetryConfigEntity(context: context)
|
||||
|
||||
|
||||
newTelemetryConfig.deviceUpdateInterval = Int32(config.deviceUpdateInterval)
|
||||
newTelemetryConfig.environmentUpdateInterval = Int32(config.environmentUpdateInterval)
|
||||
newTelemetryConfig.environmentMeasurementEnabled = config.environmentMeasurementEnabled
|
||||
newTelemetryConfig.environmentScreenEnabled = config.environmentScreenEnabled
|
||||
newTelemetryConfig.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit
|
||||
|
||||
|
||||
fetchedNode[0].telemetryConfig = newTelemetryConfig
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.deviceUpdateInterval)
|
||||
fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.environmentUpdateInterval)
|
||||
fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.environmentMeasurementEnabled
|
||||
fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.environmentScreenEnabled
|
||||
fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Updated Telemetry Module Config for node number: \(String(nodeNum))")
|
||||
|
||||
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data TelemetryConfigEntity: \(nsError)")
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Telemetry Module Config")
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
let nsError = error as NSError
|
||||
print("💥 Fetching node for core data TelemetryConfigEntity failed: \(nsError)")
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@
|
|||
import Foundation
|
||||
|
||||
extension UserEntity {
|
||||
|
||||
|
||||
var messageList: [MessageEntity] {
|
||||
|
||||
|
||||
self.value(forKey: "allMessages") as? [MessageEntity] ?? [MessageEntity]()
|
||||
}
|
||||
|
||||
|
||||
var adminMessageList: [MessageEntity] {
|
||||
|
||||
|
||||
self.value(forKey: "adminMessages") as? [MessageEntity] ?? [MessageEntity]()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ extension WaypointEntity {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var annotaton: MKPointAnnotation {
|
||||
let pointAnn = MKPointAnnotation()
|
||||
if waypointCoordinate != nil {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ struct AdminMessage {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var payloadVariant: AdminMessage.OneOf_PayloadVariant? = nil
|
||||
var payloadVariant: AdminMessage.OneOf_PayloadVariant?
|
||||
|
||||
///
|
||||
/// Send the specified channel in the response to this message
|
||||
|
|
@ -752,7 +752,7 @@ extension AdminMessage.ConfigType: CaseIterable {
|
|||
.networkConfig,
|
||||
.displayConfig,
|
||||
.loraConfig,
|
||||
.bluetoothConfig,
|
||||
.bluetoothConfig
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -767,7 +767,7 @@ extension AdminMessage.ModuleConfigType: CaseIterable {
|
|||
.telemetryConfig,
|
||||
.cannedmsgConfig,
|
||||
.audioConfig,
|
||||
.remotehardwareConfig,
|
||||
.remotehardwareConfig
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -813,7 +813,7 @@ extension HamParameters: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".AdminMessage"
|
||||
|
|
@ -848,7 +848,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
97: .standard(proto: "reboot_seconds"),
|
||||
98: .standard(proto: "shutdown_seconds"),
|
||||
99: .standard(proto: "factory_reset"),
|
||||
100: .standard(proto: "nodedb_reset"),
|
||||
100: .standard(proto: "nodedb_reset")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1315,7 +1315,7 @@ extension AdminMessage.ConfigType: SwiftProtobuf._ProtoNameProviding {
|
|||
3: .same(proto: "NETWORK_CONFIG"),
|
||||
4: .same(proto: "DISPLAY_CONFIG"),
|
||||
5: .same(proto: "LORA_CONFIG"),
|
||||
6: .same(proto: "BLUETOOTH_CONFIG"),
|
||||
6: .same(proto: "BLUETOOTH_CONFIG")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1329,7 +1329,7 @@ extension AdminMessage.ModuleConfigType: SwiftProtobuf._ProtoNameProviding {
|
|||
5: .same(proto: "TELEMETRY_CONFIG"),
|
||||
6: .same(proto: "CANNEDMSG_CONFIG"),
|
||||
7: .same(proto: "AUDIO_CONFIG"),
|
||||
8: .same(proto: "REMOTEHARDWARE_CONFIG"),
|
||||
8: .same(proto: "REMOTEHARDWARE_CONFIG")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1339,7 +1339,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
1: .standard(proto: "call_sign"),
|
||||
2: .standard(proto: "tx_power"),
|
||||
3: .same(proto: "frequency"),
|
||||
4: .standard(proto: "short_name"),
|
||||
4: .standard(proto: "short_name")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ struct ChannelSet {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _loraConfig: Config.LoRaConfig? = nil
|
||||
fileprivate var _loraConfig: Config.LoRaConfig?
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
|
|
@ -59,13 +59,13 @@ extension ChannelSet: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension ChannelSet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ChannelSet"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "settings"),
|
||||
2: .standard(proto: "lora_config"),
|
||||
2: .standard(proto: "lora_config")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -42,12 +42,12 @@ extension CannedMessageModuleConfig: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".CannedMessageModuleConfig"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "messages"),
|
||||
1: .same(proto: "messages")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ struct Channel {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _settings: ChannelSettings? = nil
|
||||
fileprivate var _settings: ChannelSettings?
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
|
@ -193,7 +193,7 @@ extension Channel.Role: CaseIterable {
|
|||
static var allCases: [Channel.Role] = [
|
||||
.disabled,
|
||||
.primary,
|
||||
.secondary,
|
||||
.secondary
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ extension Channel.Role: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ChannelSettings"
|
||||
|
|
@ -217,7 +217,7 @@ extension ChannelSettings: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen
|
|||
3: .same(proto: "name"),
|
||||
4: .same(proto: "id"),
|
||||
5: .standard(proto: "uplink_enabled"),
|
||||
6: .standard(proto: "downlink_enabled"),
|
||||
6: .standard(proto: "downlink_enabled")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -276,7 +276,7 @@ extension Channel: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "index"),
|
||||
2: .same(proto: "settings"),
|
||||
3: .same(proto: "role"),
|
||||
3: .same(proto: "role")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -323,6 +323,6 @@ extension Channel.Role: SwiftProtobuf._ProtoNameProviding {
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "DISABLED"),
|
||||
1: .same(proto: "PRIMARY"),
|
||||
2: .same(proto: "SECONDARY"),
|
||||
2: .same(proto: "SECONDARY")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ struct Config {
|
|||
|
||||
///
|
||||
/// Payload Variant
|
||||
var payloadVariant: Config.OneOf_PayloadVariant? = nil
|
||||
var payloadVariant: Config.OneOf_PayloadVariant?
|
||||
|
||||
var device: Config.DeviceConfig {
|
||||
get {
|
||||
|
|
@ -632,7 +632,7 @@ struct Config {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _ipv4Config: Config.NetworkConfig.IpV4Config? = nil
|
||||
fileprivate var _ipv4Config: Config.NetworkConfig.IpV4Config?
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -1252,7 +1252,7 @@ extension Config.DeviceConfig.Role: CaseIterable {
|
|||
.routerClient,
|
||||
.repeater,
|
||||
.tracker,
|
||||
.sensor,
|
||||
.sensor
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1261,7 +1261,7 @@ extension Config.DeviceConfig.RebroadcastMode: CaseIterable {
|
|||
static var allCases: [Config.DeviceConfig.RebroadcastMode] = [
|
||||
.all,
|
||||
.allSkipDecoding,
|
||||
.localOnly,
|
||||
.localOnly
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1278,7 +1278,7 @@ extension Config.PositionConfig.PositionFlags: CaseIterable {
|
|||
.seqNo,
|
||||
.timestamp,
|
||||
.heading,
|
||||
.speed,
|
||||
.speed
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1286,7 +1286,7 @@ extension Config.NetworkConfig.AddressMode: CaseIterable {
|
|||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static var allCases: [Config.NetworkConfig.AddressMode] = [
|
||||
.dhcp,
|
||||
.static,
|
||||
.static
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1298,7 +1298,7 @@ extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable {
|
|||
.utm,
|
||||
.mgrs,
|
||||
.olc,
|
||||
.osgr,
|
||||
.osgr
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1306,7 +1306,7 @@ extension Config.DisplayConfig.DisplayUnits: CaseIterable {
|
|||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static var allCases: [Config.DisplayConfig.DisplayUnits] = [
|
||||
.metric,
|
||||
.imperial,
|
||||
.imperial
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1316,7 +1316,7 @@ extension Config.DisplayConfig.OledType: CaseIterable {
|
|||
.oledAuto,
|
||||
.oledSsd1306,
|
||||
.oledSh1106,
|
||||
.oledSh1107,
|
||||
.oledSh1107
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1326,7 +1326,7 @@ extension Config.DisplayConfig.DisplayMode: CaseIterable {
|
|||
.default,
|
||||
.twocolor,
|
||||
.inverted,
|
||||
.color,
|
||||
.color
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1348,7 +1348,7 @@ extension Config.LoRaConfig.RegionCode: CaseIterable {
|
|||
.th,
|
||||
.lora24,
|
||||
.ua433,
|
||||
.ua868,
|
||||
.ua868
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1362,7 +1362,7 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable {
|
|||
.mediumFast,
|
||||
.shortSlow,
|
||||
.shortFast,
|
||||
.longModerate,
|
||||
.longModerate
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1371,7 +1371,7 @@ extension Config.BluetoothConfig.PairingMode: CaseIterable {
|
|||
static var allCases: [Config.BluetoothConfig.PairingMode] = [
|
||||
.randomPin,
|
||||
.fixedPin,
|
||||
.noPin,
|
||||
.noPin
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1403,7 +1403,7 @@ extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".Config"
|
||||
|
|
@ -1414,7 +1414,7 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
|
|||
4: .same(proto: "network"),
|
||||
5: .same(proto: "display"),
|
||||
6: .same(proto: "lora"),
|
||||
7: .same(proto: "bluetooth"),
|
||||
7: .same(proto: "bluetooth")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1574,7 +1574,7 @@ extension Config.DeviceConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
|||
4: .standard(proto: "button_gpio"),
|
||||
5: .standard(proto: "buzzer_gpio"),
|
||||
6: .standard(proto: "rebroadcast_mode"),
|
||||
7: .standard(proto: "node_info_broadcast_secs"),
|
||||
7: .standard(proto: "node_info_broadcast_secs")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1641,7 +1641,7 @@ extension Config.DeviceConfig.Role: SwiftProtobuf._ProtoNameProviding {
|
|||
3: .same(proto: "ROUTER_CLIENT"),
|
||||
4: .same(proto: "REPEATER"),
|
||||
5: .same(proto: "TRACKER"),
|
||||
6: .same(proto: "SENSOR"),
|
||||
6: .same(proto: "SENSOR")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1649,7 +1649,7 @@ extension Config.DeviceConfig.RebroadcastMode: SwiftProtobuf._ProtoNameProviding
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "ALL"),
|
||||
1: .same(proto: "ALL_SKIP_DECODING"),
|
||||
2: .same(proto: "LOCAL_ONLY"),
|
||||
2: .same(proto: "LOCAL_ONLY")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1664,7 +1664,7 @@ extension Config.PositionConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
6: .standard(proto: "gps_attempt_time"),
|
||||
7: .standard(proto: "position_flags"),
|
||||
8: .standard(proto: "rx_gpio"),
|
||||
9: .standard(proto: "tx_gpio"),
|
||||
9: .standard(proto: "tx_gpio")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1745,7 +1745,7 @@ extension Config.PositionConfig.PositionFlags: SwiftProtobuf._ProtoNameProviding
|
|||
64: .same(proto: "SEQ_NO"),
|
||||
128: .same(proto: "TIMESTAMP"),
|
||||
256: .same(proto: "HEADING"),
|
||||
512: .same(proto: "SPEED"),
|
||||
512: .same(proto: "SPEED")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1759,7 +1759,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
5: .standard(proto: "mesh_sds_timeout_secs"),
|
||||
6: .standard(proto: "sds_secs"),
|
||||
7: .standard(proto: "ls_secs"),
|
||||
8: .standard(proto: "min_wake_secs"),
|
||||
8: .standard(proto: "min_wake_secs")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1833,7 +1833,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
6: .standard(proto: "eth_enabled"),
|
||||
7: .standard(proto: "address_mode"),
|
||||
8: .standard(proto: "ipv4_config"),
|
||||
9: .standard(proto: "rsyslog_server"),
|
||||
9: .standard(proto: "rsyslog_server")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1904,7 +1904,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
extension Config.NetworkConfig.AddressMode: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "DHCP"),
|
||||
1: .same(proto: "STATIC"),
|
||||
1: .same(proto: "STATIC")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1914,7 +1914,7 @@ extension Config.NetworkConfig.IpV4Config: SwiftProtobuf.Message, SwiftProtobuf.
|
|||
1: .same(proto: "ip"),
|
||||
2: .same(proto: "gateway"),
|
||||
3: .same(proto: "subnet"),
|
||||
4: .same(proto: "dns"),
|
||||
4: .same(proto: "dns")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1969,7 +1969,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
6: .same(proto: "units"),
|
||||
7: .same(proto: "oled"),
|
||||
8: .same(proto: "displaymode"),
|
||||
9: .standard(proto: "heading_bold"),
|
||||
9: .standard(proto: "heading_bold")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2045,14 +2045,14 @@ extension Config.DisplayConfig.GpsCoordinateFormat: SwiftProtobuf._ProtoNameProv
|
|||
2: .same(proto: "UTM"),
|
||||
3: .same(proto: "MGRS"),
|
||||
4: .same(proto: "OLC"),
|
||||
5: .same(proto: "OSGR"),
|
||||
5: .same(proto: "OSGR")
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.DisplayUnits: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "METRIC"),
|
||||
1: .same(proto: "IMPERIAL"),
|
||||
1: .same(proto: "IMPERIAL")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2061,7 +2061,7 @@ extension Config.DisplayConfig.OledType: SwiftProtobuf._ProtoNameProviding {
|
|||
0: .same(proto: "OLED_AUTO"),
|
||||
1: .same(proto: "OLED_SSD1306"),
|
||||
2: .same(proto: "OLED_SH1106"),
|
||||
3: .same(proto: "OLED_SH1107"),
|
||||
3: .same(proto: "OLED_SH1107")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2070,7 +2070,7 @@ extension Config.DisplayConfig.DisplayMode: SwiftProtobuf._ProtoNameProviding {
|
|||
0: .same(proto: "DEFAULT"),
|
||||
1: .same(proto: "TWOCOLOR"),
|
||||
2: .same(proto: "INVERTED"),
|
||||
3: .same(proto: "COLOR"),
|
||||
3: .same(proto: "COLOR")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2091,7 +2091,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
12: .standard(proto: "override_duty_cycle"),
|
||||
13: .standard(proto: "sx126x_rx_boosted_gain"),
|
||||
14: .standard(proto: "override_frequency"),
|
||||
103: .standard(proto: "ignore_incoming"),
|
||||
103: .standard(proto: "ignore_incoming")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2207,7 +2207,7 @@ extension Config.LoRaConfig.RegionCode: SwiftProtobuf._ProtoNameProviding {
|
|||
12: .same(proto: "TH"),
|
||||
13: .same(proto: "LORA_24"),
|
||||
14: .same(proto: "UA_433"),
|
||||
15: .same(proto: "UA_868"),
|
||||
15: .same(proto: "UA_868")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2220,7 +2220,7 @@ extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding {
|
|||
4: .same(proto: "MEDIUM_FAST"),
|
||||
5: .same(proto: "SHORT_SLOW"),
|
||||
6: .same(proto: "SHORT_FAST"),
|
||||
7: .same(proto: "LONG_MODERATE"),
|
||||
7: .same(proto: "LONG_MODERATE")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2229,7 +2229,7 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "enabled"),
|
||||
2: .same(proto: "mode"),
|
||||
3: .standard(proto: "fixed_pin"),
|
||||
3: .standard(proto: "fixed_pin")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2272,6 +2272,6 @@ extension Config.BluetoothConfig.PairingMode: SwiftProtobuf._ProtoNameProviding
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "RANDOM_PIN"),
|
||||
1: .same(proto: "FIXED_PIN"),
|
||||
2: .same(proto: "NO_PIN"),
|
||||
2: .same(proto: "NO_PIN")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -73,10 +73,10 @@ struct DeviceConnectionStatus {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _wifi: WifiConnectionStatus? = nil
|
||||
fileprivate var _ethernet: EthernetConnectionStatus? = nil
|
||||
fileprivate var _bluetooth: BluetoothConnectionStatus? = nil
|
||||
fileprivate var _serial: SerialConnectionStatus? = nil
|
||||
fileprivate var _wifi: WifiConnectionStatus?
|
||||
fileprivate var _ethernet: EthernetConnectionStatus?
|
||||
fileprivate var _bluetooth: BluetoothConnectionStatus?
|
||||
fileprivate var _serial: SerialConnectionStatus?
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -109,7 +109,7 @@ struct WifiConnectionStatus {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _status: NetworkConnectionStatus? = nil
|
||||
fileprivate var _status: NetworkConnectionStatus?
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -134,7 +134,7 @@ struct EthernetConnectionStatus {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _status: NetworkConnectionStatus? = nil
|
||||
fileprivate var _status: NetworkConnectionStatus?
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -220,7 +220,7 @@ extension SerialConnectionStatus: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension DeviceConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".DeviceConnectionStatus"
|
||||
|
|
@ -228,7 +228,7 @@ extension DeviceConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
1: .same(proto: "wifi"),
|
||||
2: .same(proto: "ethernet"),
|
||||
3: .same(proto: "bluetooth"),
|
||||
4: .same(proto: "serial"),
|
||||
4: .same(proto: "serial")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -281,7 +281,7 @@ extension WifiConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "status"),
|
||||
2: .same(proto: "ssid"),
|
||||
3: .same(proto: "rssi"),
|
||||
3: .same(proto: "rssi")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -327,7 +327,7 @@ extension WifiConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
extension EthernetConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".EthernetConnectionStatus"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "status"),
|
||||
1: .same(proto: "status")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -366,7 +366,7 @@ extension NetworkConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
1: .standard(proto: "ip_address"),
|
||||
2: .standard(proto: "is_connected"),
|
||||
3: .standard(proto: "is_mqtt_connected"),
|
||||
4: .standard(proto: "is_syslog_connected"),
|
||||
4: .standard(proto: "is_syslog_connected")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -415,7 +415,7 @@ extension BluetoothConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._Messa
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "pin"),
|
||||
2: .same(proto: "rssi"),
|
||||
3: .standard(proto: "is_connected"),
|
||||
3: .standard(proto: "is_connected")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -458,7 +458,7 @@ extension SerialConnectionStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
static let protoMessageName: String = _protobuf_package + ".SerialConnectionStatus"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "baud"),
|
||||
2: .standard(proto: "is_connected"),
|
||||
2: .standard(proto: "is_connected")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -69,7 +69,7 @@ extension ScreenFonts: CaseIterable {
|
|||
static var allCases: [ScreenFonts] = [
|
||||
.fontSmall,
|
||||
.fontMedium,
|
||||
.fontLarge,
|
||||
.fontLarge
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -246,8 +246,8 @@ struct OEMStore {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _oemLocalConfig: LocalConfig? = nil
|
||||
fileprivate var _oemLocalModuleConfig: LocalModuleConfig? = nil
|
||||
fileprivate var _oemLocalConfig: LocalConfig?
|
||||
fileprivate var _oemLocalModuleConfig: LocalModuleConfig?
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
|
|
@ -259,13 +259,13 @@ extension OEMStore: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension ScreenFonts: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "FONT_SMALL"),
|
||||
1: .same(proto: "FONT_MEDIUM"),
|
||||
2: .same(proto: "FONT_LARGE"),
|
||||
2: .same(proto: "FONT_LARGE")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -279,16 +279,16 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
8: .same(proto: "version"),
|
||||
7: .standard(proto: "rx_text_message"),
|
||||
9: .standard(proto: "no_save"),
|
||||
11: .standard(proto: "did_gps_reset"),
|
||||
11: .standard(proto: "did_gps_reset")
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _myNode: MyNodeInfo? = nil
|
||||
var _owner: User? = nil
|
||||
var _myNode: MyNodeInfo?
|
||||
var _owner: User?
|
||||
var _nodeDb: [NodeInfo] = []
|
||||
var _receiveQueue: [MeshPacket] = []
|
||||
var _version: UInt32 = 0
|
||||
var _rxTextMessage: MeshPacket? = nil
|
||||
var _rxTextMessage: MeshPacket?
|
||||
var _noSave: Bool = false
|
||||
var _didGpsReset: Bool = false
|
||||
|
||||
|
|
@ -397,7 +397,7 @@ extension ChannelFile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
static let protoMessageName: String = _protobuf_package + ".ChannelFile"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "channels"),
|
||||
2: .same(proto: "version"),
|
||||
2: .same(proto: "version")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -441,7 +441,7 @@ extension OEMStore: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
5: .standard(proto: "oem_text"),
|
||||
6: .standard(proto: "oem_aes_key"),
|
||||
7: .standard(proto: "oem_local_config"),
|
||||
8: .standard(proto: "oem_local_module_config"),
|
||||
8: .standard(proto: "oem_local_module_config")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -245,7 +245,7 @@ extension LocalModuleConfig: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".LocalConfig"
|
||||
|
|
@ -257,17 +257,17 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
5: .same(proto: "display"),
|
||||
6: .same(proto: "lora"),
|
||||
7: .same(proto: "bluetooth"),
|
||||
8: .same(proto: "version"),
|
||||
8: .same(proto: "version")
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _device: Config.DeviceConfig? = nil
|
||||
var _position: Config.PositionConfig? = nil
|
||||
var _power: Config.PowerConfig? = nil
|
||||
var _network: Config.NetworkConfig? = nil
|
||||
var _display: Config.DisplayConfig? = nil
|
||||
var _lora: Config.LoRaConfig? = nil
|
||||
var _bluetooth: Config.BluetoothConfig? = nil
|
||||
var _device: Config.DeviceConfig?
|
||||
var _position: Config.PositionConfig?
|
||||
var _power: Config.PowerConfig?
|
||||
var _network: Config.NetworkConfig?
|
||||
var _display: Config.DisplayConfig?
|
||||
var _lora: Config.LoRaConfig?
|
||||
var _bluetooth: Config.BluetoothConfig?
|
||||
var _version: UInt32 = 0
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
|
@ -383,19 +383,19 @@ extension LocalModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
7: .standard(proto: "canned_message"),
|
||||
9: .same(proto: "audio"),
|
||||
10: .standard(proto: "remote_hardware"),
|
||||
8: .same(proto: "version"),
|
||||
8: .same(proto: "version")
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _mqtt: ModuleConfig.MQTTConfig? = nil
|
||||
var _serial: ModuleConfig.SerialConfig? = nil
|
||||
var _externalNotification: ModuleConfig.ExternalNotificationConfig? = nil
|
||||
var _storeForward: ModuleConfig.StoreForwardConfig? = nil
|
||||
var _rangeTest: ModuleConfig.RangeTestConfig? = nil
|
||||
var _telemetry: ModuleConfig.TelemetryConfig? = nil
|
||||
var _cannedMessage: ModuleConfig.CannedMessageConfig? = nil
|
||||
var _audio: ModuleConfig.AudioConfig? = nil
|
||||
var _remoteHardware: ModuleConfig.RemoteHardwareConfig? = nil
|
||||
var _mqtt: ModuleConfig.MQTTConfig?
|
||||
var _serial: ModuleConfig.SerialConfig?
|
||||
var _externalNotification: ModuleConfig.ExternalNotificationConfig?
|
||||
var _storeForward: ModuleConfig.StoreForwardConfig?
|
||||
var _rangeTest: ModuleConfig.RangeTestConfig?
|
||||
var _telemetry: ModuleConfig.TelemetryConfig?
|
||||
var _cannedMessage: ModuleConfig.CannedMessageConfig?
|
||||
var _audio: ModuleConfig.AudioConfig?
|
||||
var _remoteHardware: ModuleConfig.RemoteHardwareConfig?
|
||||
var _version: UInt32 = 0
|
||||
|
||||
static let defaultInstance = _StorageClass()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -302,7 +302,7 @@ extension HardwareModel: CaseIterable {
|
|||
.heltecWslV3,
|
||||
.betafpv2400Tx,
|
||||
.betafpv900NanoTx,
|
||||
.privateHw,
|
||||
.privateHw
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +353,7 @@ extension Constants: CaseIterable {
|
|||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static var allCases: [Constants] = [
|
||||
.zero,
|
||||
.dataPayloadLen,
|
||||
.dataPayloadLen
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +476,7 @@ extension CriticalErrorCode: CaseIterable {
|
|||
.transmitFailed,
|
||||
.brownout,
|
||||
.sx1262Failure,
|
||||
.radioSpiBug,
|
||||
.radioSpiBug
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -781,7 +781,7 @@ extension Position.LocSource: CaseIterable {
|
|||
.locUnset,
|
||||
.locManual,
|
||||
.locInternal,
|
||||
.locExternal,
|
||||
.locExternal
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -792,7 +792,7 @@ extension Position.AltSource: CaseIterable {
|
|||
.altManual,
|
||||
.altInternal,
|
||||
.altExternal,
|
||||
.altBarometric,
|
||||
.altBarometric
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -886,7 +886,7 @@ struct Routing {
|
|||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var variant: Routing.OneOf_Variant? = nil
|
||||
var variant: Routing.OneOf_Variant?
|
||||
|
||||
///
|
||||
/// A route request going from the requester
|
||||
|
|
@ -1075,7 +1075,7 @@ extension Routing.Error: CaseIterable {
|
|||
.noResponse,
|
||||
.dutyCycleLimit,
|
||||
.badRequest,
|
||||
.notAuthorized,
|
||||
.notAuthorized
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1503,7 +1503,7 @@ extension MeshPacket.Priority: CaseIterable {
|
|||
.default,
|
||||
.reliable,
|
||||
.ack,
|
||||
.max,
|
||||
.max
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1512,7 +1512,7 @@ extension MeshPacket.Delayed: CaseIterable {
|
|||
static var allCases: [MeshPacket.Delayed] = [
|
||||
.noDelay,
|
||||
.broadcast,
|
||||
.direct,
|
||||
.direct
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1591,9 +1591,9 @@ struct NodeInfo {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _user: User? = nil
|
||||
fileprivate var _position: Position? = nil
|
||||
fileprivate var _deviceMetrics: DeviceMetrics? = nil
|
||||
fileprivate var _user: User?
|
||||
fileprivate var _position: Position?
|
||||
fileprivate var _deviceMetrics: DeviceMetrics?
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -1796,7 +1796,7 @@ extension LogRecord.Level: CaseIterable {
|
|||
.warning,
|
||||
.info,
|
||||
.debug,
|
||||
.trace,
|
||||
.trace
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2102,7 +2102,7 @@ struct ToRadio {
|
|||
|
||||
///
|
||||
/// Log levels, chosen to match python logging conventions.
|
||||
var payloadVariant: ToRadio.OneOf_PayloadVariant? = nil
|
||||
var payloadVariant: ToRadio.OneOf_PayloadVariant?
|
||||
|
||||
///
|
||||
/// Send this packet on the mesh
|
||||
|
|
@ -2308,7 +2308,7 @@ extension DeviceMetadata: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
@ -2346,14 +2346,14 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
44: .same(proto: "HELTEC_WSL_V3"),
|
||||
45: .same(proto: "BETAFPV_2400_TX"),
|
||||
46: .same(proto: "BETAFPV_900_NANO_TX"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
255: .same(proto: "PRIVATE_HW")
|
||||
]
|
||||
}
|
||||
|
||||
extension Constants: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "ZERO"),
|
||||
237: .same(proto: "DATA_PAYLOAD_LEN"),
|
||||
237: .same(proto: "DATA_PAYLOAD_LEN")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2370,7 +2370,7 @@ extension CriticalErrorCode: SwiftProtobuf._ProtoNameProviding {
|
|||
8: .same(proto: "TRANSMIT_FAILED"),
|
||||
9: .same(proto: "BROWNOUT"),
|
||||
10: .same(proto: "SX1262_FAILURE"),
|
||||
11: .same(proto: "RADIO_SPI_BUG"),
|
||||
11: .same(proto: "RADIO_SPI_BUG")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2398,7 +2398,7 @@ extension Position: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
19: .standard(proto: "sats_in_view"),
|
||||
20: .standard(proto: "sensor_id"),
|
||||
21: .standard(proto: "next_update"),
|
||||
22: .standard(proto: "seq_number"),
|
||||
22: .standard(proto: "seq_number")
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -2611,7 +2611,7 @@ extension Position.LocSource: SwiftProtobuf._ProtoNameProviding {
|
|||
0: .same(proto: "LOC_UNSET"),
|
||||
1: .same(proto: "LOC_MANUAL"),
|
||||
2: .same(proto: "LOC_INTERNAL"),
|
||||
3: .same(proto: "LOC_EXTERNAL"),
|
||||
3: .same(proto: "LOC_EXTERNAL")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2621,7 +2621,7 @@ extension Position.AltSource: SwiftProtobuf._ProtoNameProviding {
|
|||
1: .same(proto: "ALT_MANUAL"),
|
||||
2: .same(proto: "ALT_INTERNAL"),
|
||||
3: .same(proto: "ALT_EXTERNAL"),
|
||||
4: .same(proto: "ALT_BAROMETRIC"),
|
||||
4: .same(proto: "ALT_BAROMETRIC")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2633,7 +2633,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
|
|||
3: .standard(proto: "short_name"),
|
||||
4: .same(proto: "macaddr"),
|
||||
5: .standard(proto: "hw_model"),
|
||||
6: .standard(proto: "is_licensed"),
|
||||
6: .standard(proto: "is_licensed")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2690,7 +2690,7 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
|
|||
extension RouteDiscovery: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".RouteDiscovery"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "route"),
|
||||
1: .same(proto: "route")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2724,7 +2724,7 @@ extension Routing: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "route_request"),
|
||||
2: .standard(proto: "route_reply"),
|
||||
3: .standard(proto: "error_reason"),
|
||||
3: .standard(proto: "error_reason")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2815,7 +2815,7 @@ extension Routing.Error: SwiftProtobuf._ProtoNameProviding {
|
|||
8: .same(proto: "NO_RESPONSE"),
|
||||
9: .same(proto: "DUTY_CYCLE_LIMIT"),
|
||||
32: .same(proto: "BAD_REQUEST"),
|
||||
33: .same(proto: "NOT_AUTHORIZED"),
|
||||
33: .same(proto: "NOT_AUTHORIZED")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2829,7 +2829,7 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
5: .same(proto: "source"),
|
||||
6: .standard(proto: "request_id"),
|
||||
7: .standard(proto: "reply_id"),
|
||||
8: .same(proto: "emoji"),
|
||||
8: .same(proto: "emoji")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2903,7 +2903,7 @@ extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
5: .standard(proto: "locked_to"),
|
||||
6: .same(proto: "name"),
|
||||
7: .same(proto: "description"),
|
||||
8: .same(proto: "icon"),
|
||||
8: .same(proto: "icon")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2982,7 +2982,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
10: .standard(proto: "want_ack"),
|
||||
11: .same(proto: "priority"),
|
||||
12: .standard(proto: "rx_rssi"),
|
||||
13: .same(proto: "delayed"),
|
||||
13: .same(proto: "delayed")
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -3160,7 +3160,7 @@ extension MeshPacket.Priority: SwiftProtobuf._ProtoNameProviding {
|
|||
64: .same(proto: "DEFAULT"),
|
||||
70: .same(proto: "RELIABLE"),
|
||||
120: .same(proto: "ACK"),
|
||||
127: .same(proto: "MAX"),
|
||||
127: .same(proto: "MAX")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -3168,7 +3168,7 @@ extension MeshPacket.Delayed: SwiftProtobuf._ProtoNameProviding {
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "NO_DELAY"),
|
||||
1: .same(proto: "DELAYED_BROADCAST"),
|
||||
2: .same(proto: "DELAYED_DIRECT"),
|
||||
2: .same(proto: "DELAYED_DIRECT")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -3180,7 +3180,7 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
3: .same(proto: "position"),
|
||||
4: .same(proto: "snr"),
|
||||
5: .standard(proto: "last_heard"),
|
||||
6: .standard(proto: "device_metrics"),
|
||||
6: .standard(proto: "device_metrics")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3256,7 +3256,7 @@ extension MyNodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
13: .standard(proto: "air_period_rx"),
|
||||
14: .standard(proto: "has_wifi"),
|
||||
15: .standard(proto: "channel_utilization"),
|
||||
16: .standard(proto: "air_util_tx"),
|
||||
16: .standard(proto: "air_util_tx")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3366,7 +3366,7 @@ extension LogRecord: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
1: .same(proto: "message"),
|
||||
2: .same(proto: "time"),
|
||||
3: .same(proto: "source"),
|
||||
4: .same(proto: "level"),
|
||||
4: .same(proto: "level")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3418,7 +3418,7 @@ extension LogRecord.Level: SwiftProtobuf._ProtoNameProviding {
|
|||
20: .same(proto: "INFO"),
|
||||
30: .same(proto: "WARNING"),
|
||||
40: .same(proto: "ERROR"),
|
||||
50: .same(proto: "CRITICAL"),
|
||||
50: .same(proto: "CRITICAL")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -3428,7 +3428,7 @@ extension QueueStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
|
|||
1: .same(proto: "res"),
|
||||
2: .same(proto: "free"),
|
||||
3: .same(proto: "maxlen"),
|
||||
4: .standard(proto: "mesh_packet_id"),
|
||||
4: .standard(proto: "mesh_packet_id")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3487,7 +3487,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
10: .same(proto: "channel"),
|
||||
11: .same(proto: "queueStatus"),
|
||||
12: .same(proto: "xmodemPacket"),
|
||||
13: .same(proto: "metadata"),
|
||||
13: .same(proto: "metadata")
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -3758,7 +3758,7 @@ extension ToRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBa
|
|||
1: .same(proto: "packet"),
|
||||
3: .standard(proto: "want_config_id"),
|
||||
4: .same(proto: "disconnect"),
|
||||
5: .same(proto: "xmodemPacket"),
|
||||
5: .same(proto: "xmodemPacket")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3852,7 +3852,7 @@ extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
static let protoMessageName: String = _protobuf_package + ".Compressed"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "portnum"),
|
||||
2: .same(proto: "data"),
|
||||
2: .same(proto: "data")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -3897,7 +3897,7 @@ extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplement
|
|||
6: .same(proto: "hasEthernet"),
|
||||
7: .same(proto: "role"),
|
||||
8: .standard(proto: "position_flags"),
|
||||
9: .standard(proto: "hw_model"),
|
||||
9: .standard(proto: "hw_model")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var payloadVariant: ModuleConfig.OneOf_PayloadVariant? = nil
|
||||
var payloadVariant: ModuleConfig.OneOf_PayloadVariant?
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
|
|
@ -829,7 +829,7 @@ extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable {
|
|||
.codec21300,
|
||||
.codec21200,
|
||||
.codec2700,
|
||||
.codec2700B,
|
||||
.codec2700B
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -851,7 +851,7 @@ extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable {
|
|||
.baud230400,
|
||||
.baud460800,
|
||||
.baud576000,
|
||||
.baud921600,
|
||||
.baud921600
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -862,7 +862,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable {
|
|||
.simple,
|
||||
.proto,
|
||||
.textmsg,
|
||||
.nmea,
|
||||
.nmea
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -876,7 +876,7 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable {
|
|||
.right,
|
||||
.select,
|
||||
.back,
|
||||
.cancel,
|
||||
.cancel
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -902,7 +902,7 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable {
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ModuleConfig"
|
||||
|
|
@ -915,7 +915,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
6: .same(proto: "telemetry"),
|
||||
7: .standard(proto: "canned_message"),
|
||||
8: .same(proto: "audio"),
|
||||
9: .standard(proto: "remote_hardware"),
|
||||
9: .standard(proto: "remote_hardware")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1108,7 +1108,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
3: .same(proto: "username"),
|
||||
4: .same(proto: "password"),
|
||||
5: .standard(proto: "encryption_enabled"),
|
||||
6: .standard(proto: "json_enabled"),
|
||||
6: .standard(proto: "json_enabled")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1165,7 +1165,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
extension ModuleConfig.RemoteHardwareConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = ModuleConfig.protoMessageName + ".RemoteHardwareConfig"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "enabled"),
|
||||
1: .same(proto: "enabled")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1203,7 +1203,7 @@ extension ModuleConfig.AudioConfig: SwiftProtobuf.Message, SwiftProtobuf._Messag
|
|||
4: .standard(proto: "i2s_ws"),
|
||||
5: .standard(proto: "i2s_sd"),
|
||||
6: .standard(proto: "i2s_din"),
|
||||
7: .standard(proto: "i2s_sck"),
|
||||
7: .standard(proto: "i2s_sck")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1272,7 +1272,7 @@ extension ModuleConfig.AudioConfig.Audio_Baud: SwiftProtobuf._ProtoNameProviding
|
|||
5: .same(proto: "CODEC2_1300"),
|
||||
6: .same(proto: "CODEC2_1200"),
|
||||
7: .same(proto: "CODEC2_700"),
|
||||
8: .same(proto: "CODEC2_700B"),
|
||||
8: .same(proto: "CODEC2_700B")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1285,7 +1285,7 @@ extension ModuleConfig.SerialConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
|
|||
4: .same(proto: "txd"),
|
||||
5: .same(proto: "baud"),
|
||||
6: .same(proto: "timeout"),
|
||||
7: .same(proto: "mode"),
|
||||
7: .same(proto: "mode")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1361,7 +1361,7 @@ extension ModuleConfig.SerialConfig.Serial_Baud: SwiftProtobuf._ProtoNameProvidi
|
|||
12: .same(proto: "BAUD_230400"),
|
||||
13: .same(proto: "BAUD_460800"),
|
||||
14: .same(proto: "BAUD_576000"),
|
||||
15: .same(proto: "BAUD_921600"),
|
||||
15: .same(proto: "BAUD_921600")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1371,7 +1371,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProvidi
|
|||
1: .same(proto: "SIMPLE"),
|
||||
2: .same(proto: "PROTO"),
|
||||
3: .same(proto: "TEXTMSG"),
|
||||
4: .same(proto: "NMEA"),
|
||||
4: .same(proto: "NMEA")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1391,7 +1391,7 @@ extension ModuleConfig.ExternalNotificationConfig: SwiftProtobuf.Message, SwiftP
|
|||
12: .standard(proto: "alert_bell_vibra"),
|
||||
13: .standard(proto: "alert_bell_buzzer"),
|
||||
7: .standard(proto: "use_pwm"),
|
||||
14: .standard(proto: "nag_timeout"),
|
||||
14: .standard(proto: "nag_timeout")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1492,7 +1492,7 @@ extension ModuleConfig.StoreForwardConfig: SwiftProtobuf.Message, SwiftProtobuf.
|
|||
2: .same(proto: "heartbeat"),
|
||||
3: .same(proto: "records"),
|
||||
4: .standard(proto: "history_return_max"),
|
||||
5: .standard(proto: "history_return_window"),
|
||||
5: .standard(proto: "history_return_window")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1546,7 +1546,7 @@ extension ModuleConfig.RangeTestConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "enabled"),
|
||||
2: .same(proto: "sender"),
|
||||
3: .same(proto: "save"),
|
||||
3: .same(proto: "save")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1594,7 +1594,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
|
|||
4: .standard(proto: "environment_screen_enabled"),
|
||||
5: .standard(proto: "environment_display_fahrenheit"),
|
||||
6: .standard(proto: "air_quality_enabled"),
|
||||
7: .standard(proto: "air_quality_interval"),
|
||||
7: .standard(proto: "air_quality_interval")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1666,7 +1666,7 @@ extension ModuleConfig.CannedMessageConfig: SwiftProtobuf.Message, SwiftProtobuf
|
|||
8: .standard(proto: "updown1_enabled"),
|
||||
9: .same(proto: "enabled"),
|
||||
10: .standard(proto: "allow_input_source"),
|
||||
11: .standard(proto: "send_bell"),
|
||||
11: .standard(proto: "send_bell")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1754,6 +1754,6 @@ extension ModuleConfig.CannedMessageConfig.InputEventChar: SwiftProtobuf._ProtoN
|
|||
19: .same(proto: "LEFT"),
|
||||
20: .same(proto: "RIGHT"),
|
||||
24: .same(proto: "CANCEL"),
|
||||
27: .same(proto: "BACK"),
|
||||
27: .same(proto: "BACK")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ struct ServiceEnvelope {
|
|||
|
||||
init() {}
|
||||
|
||||
fileprivate var _packet: MeshPacket? = nil
|
||||
fileprivate var _packet: MeshPacket?
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
|
|
@ -61,14 +61,14 @@ extension ServiceEnvelope: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension ServiceEnvelope: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ServiceEnvelope"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "packet"),
|
||||
2: .standard(proto: "channel_id"),
|
||||
3: .standard(proto: "gateway_id"),
|
||||
3: .standard(proto: "gateway_id")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -238,7 +238,7 @@ extension PortNum: CaseIterable {
|
|||
.tracerouteApp,
|
||||
.privateApp,
|
||||
.atakForwarder,
|
||||
.max,
|
||||
.max
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -273,6 +273,6 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
|
|||
70: .same(proto: "TRACEROUTE_APP"),
|
||||
256: .same(proto: "PRIVATE_APP"),
|
||||
257: .same(proto: "ATAK_FORWARDER"),
|
||||
511: .same(proto: "MAX"),
|
||||
511: .same(proto: "MAX")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ extension HardwareMessage.TypeEnum: CaseIterable {
|
|||
.watchGpios,
|
||||
.gpiosChanged,
|
||||
.readGpios,
|
||||
.readGpiosReply,
|
||||
.readGpiosReply
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -138,14 +138,14 @@ extension HardwareMessage.TypeEnum: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension HardwareMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".HardwareMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "type"),
|
||||
2: .standard(proto: "gpio_mask"),
|
||||
3: .standard(proto: "gpio_value"),
|
||||
3: .standard(proto: "gpio_value")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -191,6 +191,6 @@ extension HardwareMessage.TypeEnum: SwiftProtobuf._ProtoNameProviding {
|
|||
2: .same(proto: "WATCH_GPIOS"),
|
||||
3: .same(proto: "GPIOS_CHANGED"),
|
||||
4: .same(proto: "READ_GPIOS"),
|
||||
5: .same(proto: "READ_GPIOS_REPLY"),
|
||||
5: .same(proto: "READ_GPIOS_REPLY")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -42,12 +42,12 @@ extension RTTTLConfig: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension RTTTLConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".RTTTLConfig"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "ringtone"),
|
||||
1: .same(proto: "ringtone")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ struct StoreAndForward {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
var variant: StoreAndForward.OneOf_Variant? = nil
|
||||
var variant: StoreAndForward.OneOf_Variant?
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
|
|
@ -345,7 +345,7 @@ extension StoreAndForward.RequestResponse: CaseIterable {
|
|||
.clientStats,
|
||||
.clientPing,
|
||||
.clientPong,
|
||||
.clientAbort,
|
||||
.clientAbort
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +362,7 @@ extension StoreAndForward.Heartbeat: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension StoreAndForward: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".StoreAndForward"
|
||||
|
|
@ -371,7 +371,7 @@ extension StoreAndForward: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen
|
|||
2: .same(proto: "stats"),
|
||||
3: .same(proto: "history"),
|
||||
4: .same(proto: "heartbeat"),
|
||||
5: .same(proto: "empty"),
|
||||
5: .same(proto: "empty")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -486,7 +486,7 @@ extension StoreAndForward.RequestResponse: SwiftProtobuf._ProtoNameProviding {
|
|||
66: .same(proto: "CLIENT_STATS"),
|
||||
67: .same(proto: "CLIENT_PING"),
|
||||
68: .same(proto: "CLIENT_PONG"),
|
||||
106: .same(proto: "CLIENT_ABORT"),
|
||||
106: .same(proto: "CLIENT_ABORT")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -501,7 +501,7 @@ extension StoreAndForward.Statistics: SwiftProtobuf.Message, SwiftProtobuf._Mess
|
|||
6: .standard(proto: "requests_history"),
|
||||
7: .same(proto: "heartbeat"),
|
||||
8: .standard(proto: "return_max"),
|
||||
9: .standard(proto: "return_window"),
|
||||
9: .standard(proto: "return_window")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -575,7 +575,7 @@ extension StoreAndForward.History: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "history_messages"),
|
||||
2: .same(proto: "window"),
|
||||
3: .standard(proto: "last_request"),
|
||||
3: .standard(proto: "last_request")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -618,7 +618,7 @@ extension StoreAndForward.Heartbeat: SwiftProtobuf.Message, SwiftProtobuf._Messa
|
|||
static let protoMessageName: String = StoreAndForward.protoMessageName + ".Heartbeat"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "period"),
|
||||
2: .same(proto: "secondary"),
|
||||
2: .same(proto: "secondary")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ extension TelemetrySensorType: CaseIterable {
|
|||
.qmi8658,
|
||||
.qmc5883L,
|
||||
.sht31,
|
||||
.pmsa003I,
|
||||
.pmsa003I
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ struct Telemetry {
|
|||
/// seconds since 1970
|
||||
var time: UInt32 = 0
|
||||
|
||||
var variant: Telemetry.OneOf_Variant? = nil
|
||||
var variant: Telemetry.OneOf_Variant?
|
||||
|
||||
///
|
||||
/// Key native device metrics such as battery level
|
||||
|
|
@ -374,7 +374,7 @@ extension Telemetry.OneOf_Variant: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
@ -391,7 +391,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
|
|||
10: .same(proto: "QMI8658"),
|
||||
11: .same(proto: "QMC5883L"),
|
||||
12: .same(proto: "SHT31"),
|
||||
13: .same(proto: "PMSA003I"),
|
||||
13: .same(proto: "PMSA003I")
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -401,7 +401,7 @@ extension DeviceMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
1: .standard(proto: "battery_level"),
|
||||
2: .same(proto: "voltage"),
|
||||
3: .standard(proto: "channel_utilization"),
|
||||
4: .standard(proto: "air_util_tx"),
|
||||
4: .standard(proto: "air_util_tx")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -453,7 +453,7 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
3: .standard(proto: "barometric_pressure"),
|
||||
4: .standard(proto: "gas_resistance"),
|
||||
5: .same(proto: "voltage"),
|
||||
6: .same(proto: "current"),
|
||||
6: .same(proto: "current")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -521,7 +521,7 @@ extension AirQualityMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
9: .standard(proto: "particles_10um"),
|
||||
10: .standard(proto: "particles_25um"),
|
||||
11: .standard(proto: "particles_50um"),
|
||||
12: .standard(proto: "particles_100um"),
|
||||
12: .standard(proto: "particles_100um")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -611,7 +611,7 @@ extension Telemetry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
1: .same(proto: "time"),
|
||||
2: .standard(proto: "device_metrics"),
|
||||
3: .standard(proto: "environment_metrics"),
|
||||
4: .standard(proto: "air_quality_metrics"),
|
||||
4: .standard(proto: "air_quality_metrics")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import SwiftProtobuf
|
|||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that you are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
private struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ extension XModem.Control: CaseIterable {
|
|||
.ack,
|
||||
.nak,
|
||||
.can,
|
||||
.ctrlz,
|
||||
.ctrlz
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ extension XModem.Control: @unchecked Sendable {}
|
|||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
private let _protobuf_package = "meshtastic"
|
||||
|
||||
extension XModem: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".XModem"
|
||||
|
|
@ -117,7 +117,7 @@ extension XModem: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
|
|||
1: .same(proto: "control"),
|
||||
2: .same(proto: "seq"),
|
||||
3: .same(proto: "crc16"),
|
||||
4: .same(proto: "buffer"),
|
||||
4: .same(proto: "buffer")
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -170,6 +170,6 @@ extension XModem.Control: SwiftProtobuf._ProtoNameProviding {
|
|||
6: .same(proto: "ACK"),
|
||||
21: .same(proto: "NAK"),
|
||||
24: .same(proto: "CAN"),
|
||||
26: .same(proto: "CTRLZ"),
|
||||
26: .same(proto: "CTRLZ")
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ import ActivityKit
|
|||
#endif
|
||||
|
||||
struct Connect: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
@State var node: NodeInfoEntity? = nil
|
||||
@State var node: NodeInfoEntity?
|
||||
@State var isUnsetRegion = false
|
||||
@State var invalidFirmwareVersion = false
|
||||
@State var liveActivityStarted = false
|
||||
@State var presentingSwitchPreferredPeripheral = false
|
||||
@State var selectedPeripherialId = ""
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
NavigationStack {
|
||||
VStack {
|
||||
List {
|
||||
|
|
@ -63,7 +63,7 @@ struct Connect: View {
|
|||
.font(.caption).foregroundColor(Color.gray)
|
||||
.padding([.top, .bottom])
|
||||
.swipeActions {
|
||||
|
||||
|
||||
Button(role: .destructive) {
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
||||
bleManager.disconnectPeripheral(reconnect: false)
|
||||
|
|
@ -72,8 +72,8 @@ struct Connect: View {
|
|||
Label("disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
||||
}
|
||||
}
|
||||
.contextMenu{
|
||||
|
||||
.contextMenu {
|
||||
|
||||
if node != nil {
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
if #available(iOS 16.2, *) {
|
||||
|
|
@ -114,7 +114,7 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
if bleManager.isConnecting {
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right")
|
||||
|
|
@ -129,7 +129,7 @@ struct Connect: View {
|
|||
.foregroundColor(.orange)
|
||||
} else {
|
||||
VStack {
|
||||
|
||||
|
||||
Text("Connection Attempt \(bleManager.timeoutTimerCount) of 10")
|
||||
.font(.callout)
|
||||
.foregroundColor(.orange)
|
||||
|
|
@ -137,9 +137,9 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
if bleManager.lastConnectionError.count > 0 {
|
||||
Text(bleManager.lastConnectionError).font(.callout).foregroundColor(.red)
|
||||
}
|
||||
|
|
@ -157,7 +157,7 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
|
||||
if !self.bleManager.isConnected {
|
||||
Section(header: Text("available.radios").font(.title)) {
|
||||
ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.name > $1.name })) { peripheral in
|
||||
|
|
@ -183,7 +183,7 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
.confirmationDialog("Connecting to a new radio will clear all local app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) {
|
||||
|
||||
|
||||
Button("Connect to new radio?", role: .destructive) {
|
||||
bleManager.stopScanning()
|
||||
bleManager.connectedPeripheral = nil
|
||||
|
|
@ -191,9 +191,9 @@ struct Connect: View {
|
|||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected {
|
||||
bleManager.disconnectPeripheral()
|
||||
}
|
||||
|
||||
|
||||
clearCoreDataDatabase(context: context)
|
||||
let radio = bleManager.peripherals.first(where: { $0.peripheral.identifier.uuidString == selectedPeripherialId} )
|
||||
let radio = bleManager.peripherals.first(where: { $0.peripheral.identifier.uuidString == selectedPeripherialId})
|
||||
bleManager.connectTo(peripheral: radio!.peripheral)
|
||||
presentingSwitchPreferredPeripheral = false
|
||||
selectedPeripherialId = ""
|
||||
|
|
@ -201,14 +201,14 @@ struct Connect: View {
|
|||
}
|
||||
.textCase(nil)
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
Text("bluetooth.off")
|
||||
.foregroundColor(.red)
|
||||
.font(.title)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Spacer()
|
||||
#if targetEnvironment(macCatalyst)
|
||||
|
|
@ -236,24 +236,25 @@ struct Connect: View {
|
|||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
}
|
||||
.sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) {
|
||||
.sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) {
|
||||
InvalidVersion(minimumVersion: self.bleManager.minimumVersion, version: self.bleManager.connectedVersion)
|
||||
.presentationDetents([.large])
|
||||
.presentationDragIndicator(.automatic)
|
||||
}
|
||||
.onChange(of: (self.bleManager.invalidVersion)) { cv in
|
||||
.onChange(of: (self.bleManager.invalidVersion)) { _ in
|
||||
invalidFirmwareVersion = self.bleManager.invalidVersion
|
||||
}
|
||||
.onChange(of: (self.bleManager.isSubscribed)) { sub in
|
||||
|
||||
|
||||
if userSettings.preferredPeripheralId.count > 0 && sub {
|
||||
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(bleManager.connectedPeripheral?.num ?? -1))
|
||||
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, check it for a region
|
||||
if !fetchedNode.isEmpty {
|
||||
node = fetchedNode[0]
|
||||
|
|
@ -264,14 +265,14 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear(perform: {
|
||||
self.bleManager.context = context
|
||||
self.bleManager.userSettings = userSettings
|
||||
|
||||
|
||||
// Ask for notification permission
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
|
||||
if success {
|
||||
|
|
@ -287,17 +288,17 @@ struct Connect: View {
|
|||
if #available(iOS 16.2, *) {
|
||||
liveActivityStarted = true
|
||||
let timerSeconds = 60
|
||||
|
||||
|
||||
let mostRecent = node?.telemetries?.lastObject as! TelemetryEntity
|
||||
|
||||
|
||||
let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName ?? "unknown")
|
||||
|
||||
|
||||
let future = Date(timeIntervalSinceNow: Double(timerSeconds))
|
||||
|
||||
|
||||
let initialContentState = MeshActivityAttributes.ContentState(timerRange: Date.now...future, connected: true, channelUtilization: mostRecent.channelUtilization, airtime: mostRecent.airUtilTx, batteryLevel: UInt32(mostRecent.batteryLevel))
|
||||
|
||||
|
||||
let activityContent = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .minute, value: 2, to: Date())!)
|
||||
|
||||
|
||||
do {
|
||||
let myActivity = try Activity<MeshActivityAttributes>.request(attributes: activityAttributes, content: activityContent,
|
||||
pushType: nil)
|
||||
|
|
@ -307,7 +308,7 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func endActivity() {
|
||||
liveActivityStarted = false
|
||||
Task {
|
||||
|
|
@ -322,7 +323,7 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if os(iOS)
|
||||
func postNotification() {
|
||||
let timerSeconds = 60
|
||||
|
|
@ -344,7 +345,7 @@ struct Connect: View {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
func didDismissSheet() {
|
||||
bleManager.disconnectPeripheral(reconnect: false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,20 @@
|
|||
import SwiftUI
|
||||
|
||||
struct InvalidVersion: View {
|
||||
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
|
||||
@State var minimumVersion = ""
|
||||
@State var version = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
|
||||
|
||||
Text("update.firmware")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.orange)
|
||||
|
||||
|
||||
Divider()
|
||||
VStack {
|
||||
Text("The Meshtastic Apple apps support firmware version \(minimumVersion) and above.")
|
||||
|
|
@ -36,7 +36,7 @@ struct InvalidVersion: View {
|
|||
.padding()
|
||||
Divider()
|
||||
.padding(.top)
|
||||
VStack{
|
||||
VStack {
|
||||
Text("🦕 End of life Version 🦖 ☄️")
|
||||
.font(.title3)
|
||||
.foregroundColor(.orange)
|
||||
|
|
@ -46,20 +46,20 @@ struct InvalidVersion: View {
|
|||
.padding([.leading, .trailing, .bottom])
|
||||
Link("Version 1.2 End of life (EOL) Info", destination: URL(string: "https://meshtastic.org/docs/1.2-End-of-life/")!)
|
||||
.font(.callout)
|
||||
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Label("close", systemImage: "xmark")
|
||||
|
||||
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding()
|
||||
#endif
|
||||
|
||||
|
||||
}.padding()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
|
||||
@State private var selection: Tab = .ble
|
||||
|
||||
|
||||
enum Tab {
|
||||
case contacts
|
||||
case messages
|
||||
|
|
@ -18,11 +18,11 @@ struct ContentView: View {
|
|||
case nodes
|
||||
case settings
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
TabView(selection: $selection) {
|
||||
|
||||
|
||||
Contacts()
|
||||
.tabItem {
|
||||
Label("messages", systemImage: "message")
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ import SwiftUI
|
|||
import Charts
|
||||
|
||||
struct BatteryGauge: View {
|
||||
|
||||
@State var batteryLevel = 0.0
|
||||
|
||||
private let minValue = 1.0
|
||||
private let maxValue = 100.00
|
||||
|
||||
|
|
@ -24,7 +22,6 @@ struct BatteryGauge: View {
|
|||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
} else {
|
||||
|
||||
let gradient = Gradient(colors: [.red, .orange, .green])
|
||||
Gauge(value: batteryLevel, in: minValue...maxValue) {
|
||||
if batteryLevel > 1.0 && batteryLevel < 10 {
|
||||
|
|
@ -52,7 +49,6 @@ struct BatteryGauge: View {
|
|||
|
||||
struct BatteryGauge_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
||||
VStack {
|
||||
BatteryGauge(batteryLevel: 0.0)
|
||||
BatteryGauge(batteryLevel: 9.0)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ struct CircleText: View {
|
|||
var brightness: Double? = 0
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
let font = Font.system(size: fontSize!)
|
||||
|
||||
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(color)
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ import SwiftUI
|
|||
//
|
||||
struct DateTimeText: View {
|
||||
var dateTime: Date?
|
||||
|
||||
|
||||
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
|
||||
|
||||
var body: some View {
|
||||
if (dateTime != nil && dateTime! >= sixMonthsAgo!){
|
||||
|
||||
if dateTime != nil && dateTime! >= sixMonthsAgo! {
|
||||
|
||||
Text("\(dateTime!, style: .date) \(dateTime!, style: .time)")
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
Text("unknown.age")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ import CoreLocation
|
|||
import MapKit
|
||||
|
||||
struct DistanceText: View {
|
||||
|
||||
|
||||
var meters: CLLocationDistance
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
let distanceFormatter = MKDistanceFormatter()
|
||||
Text("distance")+Text(": \(distanceFormatter.string(fromDistance: Double(meters)))")
|
||||
}
|
||||
}
|
||||
struct DistanceText_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
DistanceText(meters: 100)
|
||||
DistanceText(meters: 1000)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct LastHeardText: View {
|
|||
var lastHeard: Date?
|
||||
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
|
||||
var body: some View {
|
||||
if (lastHeard != nil && lastHeard! >= sixMonthsAgo!){
|
||||
if lastHeard != nil && lastHeard! >= sixMonthsAgo! {
|
||||
Text("heard")+Text(": \(lastHeard!, style: .relative) ")+Text("ago")
|
||||
} else {
|
||||
Text("unknown.age")
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
import SwiftUI
|
||||
|
||||
struct MeshtasticLogo: View {
|
||||
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
VStack {
|
||||
Image("logo-white")
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@
|
|||
import SwiftUI
|
||||
|
||||
struct MessageTemplate: View {
|
||||
|
||||
|
||||
var user: UserEntity
|
||||
var message: MessageEntity
|
||||
var messageReply: MessageEntity?
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
// Display the message being replied to and the arrow
|
||||
if message.replyID > 0 {
|
||||
|
||||
|
||||
HStack {
|
||||
|
||||
Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.blue).font(.caption2)
|
||||
|
|
@ -31,8 +31,8 @@ struct MessageTemplate: View {
|
|||
.padding(.trailing)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Message
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import SwiftUI
|
|||
|
||||
struct NodeAnnotation: View {
|
||||
let time: Date
|
||||
|
||||
|
||||
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
|
||||
|
||||
var body: some View {
|
||||
|
||||
if (time >= sixMonthsAgo!) {
|
||||
|
||||
|
||||
if time >= sixMonthsAgo! {
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Text(time, style: .offset)
|
||||
.font(.caption2).foregroundColor(.accentColor)
|
||||
|
|
@ -17,9 +17,9 @@ struct NodeAnnotation: View {
|
|||
.background(Color(.white))
|
||||
.cornerRadius(10)
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Text("unknown.age")
|
||||
.font(.caption2).foregroundColor(.accentColor)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import SwiftUI
|
|||
|
||||
struct SignalStrengthIndicator: View {
|
||||
let signalStrength: SignalStrength
|
||||
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
ForEach(0..<3) { bar in
|
||||
|
|
@ -44,7 +44,7 @@ struct SignalStrengthIndicator: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func getColor() -> Color {
|
||||
switch signalStrength {
|
||||
case .weak:
|
||||
|
|
@ -71,7 +71,7 @@ extension Shape {
|
|||
}
|
||||
}
|
||||
|
||||
enum SignalStrength : Int {
|
||||
enum SignalStrength: Int {
|
||||
case weak = 0
|
||||
case normal = 1
|
||||
case strong = 2
|
||||
|
|
|
|||
|
|
@ -10,14 +10,13 @@ struct AirQualityIndexCompact: View {
|
|||
var aqi: Int
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
HStack (spacing: 0.5) {
|
||||
HStack(spacing: 0.5) {
|
||||
Text("AQI \(aqi)")
|
||||
.foregroundColor(.gray)
|
||||
.padding(.trailing, 0)
|
||||
.font(.caption)
|
||||
|
||||
|
||||
if aqi > 0 && aqi < 51 {
|
||||
// Good
|
||||
Circle()
|
||||
|
|
@ -38,7 +37,7 @@ struct AirQualityIndexCompact: View {
|
|||
Circle()
|
||||
.fill(.orange)
|
||||
.frame(width: 10, height: 10)
|
||||
|
||||
|
||||
} else if aqi > 300 && aqi < 401 {
|
||||
// Very Poor
|
||||
Circle()
|
||||
|
|
@ -55,7 +54,7 @@ struct AirQualityIndexCompact: View {
|
|||
}
|
||||
struct AQICircleDisplay_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
AirQualityIndexCompact(aqi: 5)
|
||||
AirQualityIndexCompact(aqi: 51)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct CurrentConditionsCompact: View {
|
|||
}
|
||||
struct CurrentConditionsCompact_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
CurrentConditionsCompact(temp: 22, condition: WeatherConditions.clear)
|
||||
CurrentConditionsCompact(temp: 17, condition: WeatherConditions.cloudy)
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ import WeatherKit
|
|||
|
||||
struct NodeWeatherForecastView: View {
|
||||
var location: CLLocation
|
||||
|
||||
|
||||
@State private var forecast: NodeWeatherForecast = placeholderForecast
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
chart
|
||||
.frame(width: 400)
|
||||
}
|
||||
//.frame(width: 350, height: 200)
|
||||
// .frame(width: 350, height: 200)
|
||||
.padding(10)
|
||||
.background()
|
||||
.task {
|
||||
|
|
@ -38,12 +38,12 @@ struct NodeWeatherForecastView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var chart: some View {
|
||||
Chart {
|
||||
areaMarks(seriesKey: "Temperature", value: 0)
|
||||
.foregroundStyle(.linearGradient(colors: [.teal, .yellow], startPoint: .bottom, endPoint: .top))
|
||||
|
||||
|
||||
ForEach(forecast.nightTimeRanges, id: \.lowerBound) { range in
|
||||
RectangleMark(
|
||||
xStart: .value("Hour", range.lowerBound),
|
||||
|
|
@ -53,7 +53,7 @@ struct NodeWeatherForecastView: View {
|
|||
.mask {
|
||||
areaMarks(seriesKey: "Mask", value: range.lowerBound.timeIntervalSince1970)
|
||||
}
|
||||
|
||||
|
||||
if range.lowerBound != forecast.entries.first!.date {
|
||||
let date = range.lowerBound
|
||||
RectangleMark(
|
||||
|
|
@ -71,7 +71,7 @@ struct NodeWeatherForecastView: View {
|
|||
.foregroundStyle(.white, .indigo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if range.upperBound != forecast.entries.last!.date {
|
||||
let date = range.upperBound
|
||||
RectangleMark(
|
||||
|
|
@ -107,7 +107,7 @@ struct NodeWeatherForecastView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ChartContentBuilder
|
||||
func areaMarks(seriesKey: String, value: Double) -> some ChartContent {
|
||||
ForEach(forecast.entries) { entry in
|
||||
|
|
@ -120,14 +120,14 @@ struct NodeWeatherForecastView: View {
|
|||
.interpolationMethod(.catmullRom)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static var placeholderForecast: NodeWeatherForecast {
|
||||
func entry(hourOffset: Int, degrees: Double, isDaylight: Bool) -> NodeWeatherForecast.WeatherEntry {
|
||||
let startDate = Calendar.current.date(from: DateComponents(year: 2022, month: 5, day: 6, hour: 9))!
|
||||
let date = Calendar.current.date(byAdding: DateComponents(hour: hourOffset), to: startDate)!
|
||||
return NodeWeatherForecast.WeatherEntry(date: date, degrees: degrees, isDaylight: isDaylight)
|
||||
}
|
||||
|
||||
|
||||
return NodeWeatherForecast(entries: [
|
||||
entry(hourOffset: 0, degrees: 63, isDaylight: true),
|
||||
entry(hourOffset: 1, degrees: 68, isDaylight: true),
|
||||
|
|
@ -165,17 +165,17 @@ struct NodeWeatherForecast {
|
|||
var degrees: Double
|
||||
var isDaylight: Bool
|
||||
}
|
||||
|
||||
|
||||
var entries: [WeatherEntry]
|
||||
|
||||
|
||||
var low: Double {
|
||||
return entries.map(\.degrees).min()! - 2
|
||||
}
|
||||
|
||||
|
||||
var hottestEntry: WeatherEntry {
|
||||
return entries.sorted { $0.degrees > $1.degrees }.first!
|
||||
}
|
||||
|
||||
|
||||
var nightTimeRanges: [Range<Date>] {
|
||||
var currentLowerBound: Date?
|
||||
var results: [Range<Date>] = []
|
||||
|
|
@ -192,7 +192,7 @@ struct NodeWeatherForecast {
|
|||
}
|
||||
return results
|
||||
}
|
||||
|
||||
|
||||
var binRange: ClosedRange<Date> {
|
||||
let startDate: Date = entries.map(\.date).first(where: {
|
||||
Calendar.current.component(.hour, from: $0).isMultiple(of: 3)
|
||||
|
|
@ -202,7 +202,7 @@ struct NodeWeatherForecast {
|
|||
})!
|
||||
return startDate ... endDate
|
||||
}
|
||||
|
||||
|
||||
func temperature(at date: Date) -> Double {
|
||||
entries.first(where: { $0.date == date })!.degrees
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ extension MKMapRect {
|
|||
left = min(left, coordinate.longitude)
|
||||
right = max(right, coordinate.longitude)
|
||||
}
|
||||
let topLeft = MKMapPoint(CLLocationCoordinate2D(latitude:top, longitude:left))
|
||||
let bottomRight = MKMapPoint(CLLocationCoordinate2D(latitude:bottom, longitude:right))
|
||||
self = MKMapRect(x:topLeft.x, y:topLeft.y,
|
||||
width:bottomRight.x - topLeft.x, height:bottomRight.y - topLeft.y)
|
||||
let topLeft = MKMapPoint(CLLocationCoordinate2D(latitude: top, longitude: left))
|
||||
let bottomRight = MKMapPoint(CLLocationCoordinate2D(latitude: bottom, longitude: right))
|
||||
self = MKMapRect(x: topLeft.x, y: topLeft.y,
|
||||
width: bottomRight.x - topLeft.x, height: bottomRight.y - topLeft.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,36 +44,34 @@ class LocalMBTileOverlay: MKTileOverlay {
|
|||
var mb: Connection!
|
||||
private var _boundingMapRect: MKMapRect!
|
||||
override var boundingMapRect: MKMapRect {
|
||||
get {
|
||||
return _boundingMapRect
|
||||
}
|
||||
return _boundingMapRect
|
||||
}
|
||||
|
||||
|
||||
init?(mbTilePath path: String) {
|
||||
|
||||
|
||||
super.init(urlTemplate: nil)
|
||||
self.path = path
|
||||
do {
|
||||
self.mb = try Connection(self.path, readonly: true)
|
||||
let metadata = Table("metadata")
|
||||
|
||||
|
||||
let name = Expression<String>("name")
|
||||
let value = Expression<String>("value")
|
||||
|
||||
//make sure it's raster
|
||||
|
||||
// make sure it's raster
|
||||
let formatQuery = try mb.pluck(metadata.select(value).filter(name == "format"))
|
||||
if formatQuery?[value] == nil || (formatQuery![value] != "jpg" && formatQuery![value] != "png") {
|
||||
throw MapTileError.invalidFormat
|
||||
}
|
||||
|
||||
|
||||
let minZQuery = try mb.pluck(metadata.select(value).filter(name == "minzoom"))
|
||||
self.minimumZ = Int(minZQuery![value])!
|
||||
|
||||
|
||||
let maxZQuery = try mb.pluck(metadata.select(value).filter(name == "maxzoom"))
|
||||
self.maximumZ = Int(maxZQuery![value])!
|
||||
|
||||
|
||||
self.isGeometryFlipped = true
|
||||
|
||||
|
||||
let boundingBoxString = try mb.pluck(metadata.select(value).filter(name == "bounds"))
|
||||
let boundCoords = boundingBoxString![value].split(separator: ",")
|
||||
let coords = [
|
||||
|
|
@ -83,15 +81,15 @@ class LocalMBTileOverlay: MKTileOverlay {
|
|||
longitude: Double(boundCoords[2]) ?? 0)
|
||||
]
|
||||
self._boundingMapRect = MKMapRect(coordinates: coords)
|
||||
|
||||
|
||||
} catch {
|
||||
print("💥 Map tile error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void) {
|
||||
|
||||
|
||||
let tileX = Int64(path.x)
|
||||
let tileY = Int64(path.y)
|
||||
let tileZ = Int64(path.z)
|
||||
|
|
@ -99,9 +97,9 @@ class LocalMBTileOverlay: MKTileOverlay {
|
|||
let zoomLevel = Expression<Int64>("zoom_level")
|
||||
let tileColumn = Expression<Int64>("tile_column")
|
||||
let tileRow = Expression<Int64>("tile_row")
|
||||
|
||||
|
||||
if let dataQuery = try? self.mb.pluck(Table("tiles").select(tileData).filter(zoomLevel == tileZ).filter(tileColumn == tileX).filter(tileRow == tileY)) {
|
||||
let data = Data(bytes: dataQuery[tileData].bytes, count: dataQuery[tileData].bytes.count)//dataQuery![tileData].bytes
|
||||
let data = Data(bytes: dataQuery[tileData].bytes, count: dataQuery[tileData].bytes.count)// dataQuery![tileData].bytes
|
||||
result(data, nil)
|
||||
} else {
|
||||
print("💥 No tile here: x:\(tileX) y:\(tileY) z:\(tileZ)")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import MapKit
|
||||
|
||||
extension MKMapView {
|
||||
|
||||
|
||||
func fitAllAnnotations(with padding: UIEdgeInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)) {
|
||||
var zoomRect: MKMapRect = .null
|
||||
annotations.forEach({
|
||||
|
|
@ -16,10 +16,10 @@ extension MKMapView {
|
|||
let pointRect = MKMapRect(x: annotationPoint.x, y: annotationPoint.y, width: 0.01, height: 0.01)
|
||||
zoomRect = zoomRect.union(pointRect)
|
||||
})
|
||||
|
||||
|
||||
setVisibleMapRect(zoomRect, edgePadding: padding, animated: true)
|
||||
}
|
||||
|
||||
|
||||
func fit(annotations: [MKAnnotation], andShow show: Bool, with padding: UIEdgeInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)) {
|
||||
var zoomRect: MKMapRect = .null
|
||||
annotations.forEach({
|
||||
|
|
@ -27,11 +27,11 @@ extension MKMapView {
|
|||
let rect = MKMapRect(x: aPoint.x, y: aPoint.y, width: 0.1, height: 0.1)
|
||||
zoomRect = zoomRect.isNull ? rect : zoomRect.union(rect)
|
||||
})
|
||||
|
||||
|
||||
if show {
|
||||
addAnnotations(annotations)
|
||||
}
|
||||
|
||||
|
||||
setVisibleMapRect(zoomRect, edgePadding: padding, animated: true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,469 +0,0 @@
|
|||
////
|
||||
//// MapView.swift
|
||||
//// MapViewTest
|
||||
////
|
||||
//// Created by Cem Yilmaz on 05.07.21.
|
||||
////
|
||||
//import SwiftUI
|
||||
//import MapKit
|
||||
//import CoreData
|
||||
//
|
||||
//#if canImport(MapKit) && canImport(UIKit)
|
||||
//public struct MapView: UIViewRepresentable {
|
||||
//
|
||||
// @Environment(\.managedObjectContext) var context
|
||||
//
|
||||
// //var context: NSManagedObjectContext?
|
||||
//
|
||||
// //@Binding private var region: MKCoordinateRegion
|
||||
//
|
||||
// //make this view dependent on the UserDefault that is updated when importing a new map file
|
||||
// @AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0
|
||||
// @State private var loadedLastUpdatedLocalMapFile = 0
|
||||
//
|
||||
// private var customMapOverlay: CustomMapOverlay?
|
||||
// @State private var presentCustomMapOverlayHash: CustomMapOverlay?
|
||||
//
|
||||
// private var mapType: MKMapType
|
||||
//
|
||||
// private var showZoomScale: Bool
|
||||
// private var zoomEnabled: Bool
|
||||
// private var zoomRange: (minHeight: CLLocationDistance?, maxHeight: CLLocationDistance?)
|
||||
//
|
||||
// private var scrollEnabled: Bool
|
||||
// private var scrollBoundaries: MKCoordinateRegion?
|
||||
//
|
||||
// private var rotationEnabled: Bool
|
||||
// private var showCompassWhenRotated: Bool
|
||||
//
|
||||
// private var showUserLocation: Bool
|
||||
// private var userTrackingMode: MKUserTrackingMode
|
||||
// @Binding private var userLocation: CLLocationCoordinate2D?
|
||||
//
|
||||
// private var overlays: [Overlay]
|
||||
//
|
||||
// @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: false)], animation: .default)
|
||||
// private var positions: FetchedResults<PositionEntity>
|
||||
//
|
||||
// public init(
|
||||
// customMapOverlay: CustomMapOverlay? = nil,
|
||||
// mapType: String = "hybrid",
|
||||
// zoomEnabled: Bool = true,
|
||||
// showZoomScale: Bool = false,
|
||||
// zoomRange: (minHeight: CLLocationDistance?, maxHeight: CLLocationDistance?) = (nil, nil),
|
||||
// scrollEnabled: Bool = true,
|
||||
// scrollBoundaries: MKCoordinateRegion? = nil,
|
||||
// rotationEnabled: Bool = true,
|
||||
// showCompassWhenRotated: Bool = true,
|
||||
// showUserLocation: Bool = true,
|
||||
// userTrackingMode: MKUserTrackingMode = MKUserTrackingMode.none,
|
||||
// userLocation: Binding<CLLocationCoordinate2D?> = .constant(nil),
|
||||
// overlays: [Overlay] = []
|
||||
// ) {
|
||||
// self.customMapOverlay = customMapOverlay
|
||||
//
|
||||
// switch mapType {
|
||||
// case "satellite":
|
||||
// self.mapType = .satellite
|
||||
// break
|
||||
// case "standard":
|
||||
// self.mapType = .standard
|
||||
// break
|
||||
// case "hybrid":
|
||||
// self.mapType = .hybrid
|
||||
// break
|
||||
// default:
|
||||
// self.mapType = .hybrid
|
||||
// }
|
||||
//
|
||||
// self.showZoomScale = showZoomScale
|
||||
// self.zoomEnabled = zoomEnabled
|
||||
// self.zoomRange = zoomRange
|
||||
//
|
||||
// self.scrollEnabled = scrollEnabled
|
||||
// self.scrollBoundaries = scrollBoundaries
|
||||
//
|
||||
// self.rotationEnabled = rotationEnabled
|
||||
// self.showCompassWhenRotated = showCompassWhenRotated
|
||||
//
|
||||
// self.showUserLocation = showUserLocation
|
||||
// self.userTrackingMode = userTrackingMode
|
||||
// self._userLocation = userLocation
|
||||
//
|
||||
// self.overlays = overlays
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public func makeUIView(context: Context) -> MKMapView {
|
||||
// let mapView = MKMapView()
|
||||
// mapView.delegate = context.coordinator
|
||||
// mapView.register(PositionAnnotationView.self, forAnnotationViewWithReuseIdentifier: NSStringFromClass(PositionAnnotationView.self))
|
||||
//
|
||||
// return mapView
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public func updateUIView(_ mapView: MKMapView, context: Context) {
|
||||
//
|
||||
// if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile {
|
||||
// mapView.removeOverlays(mapView.overlays)
|
||||
// if let customMapOverlay = self.customMapOverlay {
|
||||
//
|
||||
// let fileManager = FileManager.default
|
||||
// let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
// let tilePath = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false).path
|
||||
// if fileManager.fileExists(atPath: tilePath) {
|
||||
// //if let tilePath = Bundle.main.path(forResource: "offline_map", ofType: "mbtiles") {
|
||||
//
|
||||
// print("Loading local map file")
|
||||
//
|
||||
// if let overlay = LocalMBTileOverlay(mbTilePath: tilePath) {
|
||||
//
|
||||
// overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent
|
||||
//
|
||||
// mapView.addOverlay(overlay)
|
||||
// }
|
||||
// } else {
|
||||
// print("Couldn't find a local map file to load")
|
||||
// }
|
||||
// }
|
||||
// DispatchQueue.main.async {
|
||||
// self.presentCustomMapOverlayHash = self.customMapOverlay
|
||||
// self.loadedLastUpdatedLocalMapFile = self.lastUpdatedLocalMapFile
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if mapView.mapType != self.mapType {
|
||||
// mapView.mapType = self.mapType
|
||||
// }
|
||||
//
|
||||
// mapView.showsScale = self.zoomEnabled ? self.showZoomScale : false
|
||||
//
|
||||
// if mapView.isZoomEnabled != self.zoomEnabled {
|
||||
// mapView.isZoomEnabled = self.zoomEnabled
|
||||
// }
|
||||
//
|
||||
// if mapView.cameraZoomRange.minCenterCoordinateDistance != self.zoomRange.minHeight ?? 0 ||
|
||||
// mapView.cameraZoomRange.maxCenterCoordinateDistance != self.zoomRange.maxHeight ?? .infinity {
|
||||
// mapView.cameraZoomRange = MKMapView.CameraZoomRange(
|
||||
// minCenterCoordinateDistance: self.zoomRange.minHeight ?? 0,
|
||||
// maxCenterCoordinateDistance: self.zoomRange.maxHeight ?? .infinity
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// mapView.isScrollEnabled = self.userTrackingMode == MKUserTrackingMode.none ? self.scrollEnabled : false
|
||||
//
|
||||
// if let scrollBoundary = self.scrollBoundaries, (mapView.cameraBoundary?.region.center.latitude != scrollBoundary.center.latitude || mapView.cameraBoundary?.region.center.longitude != scrollBoundary.center.longitude || mapView.camera Boundary?.region.span.latitudeDelta != scrollBoundary.span.latitudeDelta || mapView.cameraBoundary?.region.span.longitudeDelta != scrollBoundary.span.longitudeDelta) {
|
||||
// mapView.cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: scrollBoundary)
|
||||
// } else if self.scrollBoundaries == nil && mapView.cameraBoundary != nil {
|
||||
// mapView.cameraBoundary = nil
|
||||
// }
|
||||
//
|
||||
// mapView.isRotateEnabled = self.userTrackingMode != .followWithHeading ? self.rotationEnabled : false
|
||||
// mapView.showsCompass = self.userTrackingMode != .followWithHeading ? self.showCompassWhenRotated : false
|
||||
//
|
||||
// if mapView.showsUserLocation != self.showUserLocation {
|
||||
// mapView.showsUserLocation = self.showUserLocation
|
||||
// }
|
||||
//
|
||||
// if mapView.userTrackingMode != self.userTrackingMode {
|
||||
// mapView.userTrackingMode = self.userTrackingMode
|
||||
// }
|
||||
//
|
||||
// // clear any existing annotations
|
||||
// var shouldMoveRegion = false
|
||||
// if !mapView.annotations.isEmpty {
|
||||
// mapView.removeAnnotations(mapView.annotations)
|
||||
// } else {
|
||||
// shouldMoveRegion = true
|
||||
// }
|
||||
//
|
||||
// var displayedNodes: [Int64] = []
|
||||
// for position in self.positions {
|
||||
// if position.nodePosition == nil || displayedNodes.contains(position.nodePosition!.num) || position.coordinate == nil {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// let annotation = PositionAnnotation()
|
||||
// annotation.coordinate = position.nodeCoordinate!
|
||||
// annotation.title = position.nodePosition!.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
|
||||
// annotation.shortName = position.nodePosition!.user?.shortName?.uppercased() ?? "???"
|
||||
//
|
||||
// mapView.addAnnotation(annotation)
|
||||
//
|
||||
// displayedNodes.append(position.nodePosition!.num)
|
||||
// }
|
||||
//
|
||||
// if shouldMoveRegion {
|
||||
// self.moveToMeshRegion(mapView)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func moveToMeshRegion(_ mapView: MKMapView) {
|
||||
// //go through the annotations and create a bounding box that encloses them
|
||||
//
|
||||
// var minLat: CLLocationDegrees = 90.0
|
||||
// var maxLat: CLLocationDegrees = -90.0
|
||||
// var minLon: CLLocationDegrees = 180.0
|
||||
// var maxLon: CLLocationDegrees = -180.0
|
||||
//
|
||||
// for annotation in mapView.annotations {
|
||||
// if annotation.isKind(of: PositionAnnotation.self) {
|
||||
// minLat = min(minLat, annotation.coordinate.latitude)
|
||||
// maxLat = max(maxLat, annotation.coordinate.latitude)
|
||||
// minLon = min(minLon, annotation.coordinate.longitude)
|
||||
// maxLon = max(maxLon, annotation.coordinate.longitude)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //check if the mesh region looks sensible before we move to it. Otherwise we won't move the map (leave it at the current location)
|
||||
// if maxLat < minLat || (maxLat-minLat) > 5 || maxLon < minLon || (maxLon-minLon) > 5 {
|
||||
// return
|
||||
// } else if minLat == maxLat && minLon == maxLon {
|
||||
// //then we are focussed on a single point (probably because there is only one node with a position)
|
||||
// //widen that out a little (don't zoom way in to that point)
|
||||
//
|
||||
// //0.001 degrees latitude is about 100m
|
||||
// //the mapView.regionThatFits call below will expand this out to a rectangle
|
||||
// minLat = minLat - 0.001
|
||||
// maxLat = maxLat + 0.001
|
||||
// }
|
||||
//
|
||||
// let centerCoord = CLLocationCoordinate2D(latitude: (minLat+maxLat)/2, longitude: (minLon+maxLon)/2)
|
||||
//
|
||||
// let span = MKCoordinateSpan(latitudeDelta: (maxLat-minLat)*1.5, longitudeDelta: (maxLon-minLon)*1.5)
|
||||
//
|
||||
// let region = mapView.regionThatFits(MKCoordinateRegion(center: centerCoord, span: span))
|
||||
//
|
||||
// mapView.setRegion(region, animated: true)
|
||||
// }
|
||||
//
|
||||
// public func makeCoordinator() -> Coordinator {
|
||||
// Coordinator(parent: self)
|
||||
// }
|
||||
//
|
||||
// public class Coordinator: NSObject, MKMapViewDelegate {
|
||||
//
|
||||
// private var parent: MapView
|
||||
// public var overlays: [Overlay] = []
|
||||
//
|
||||
// init(parent: MapView) {
|
||||
// self.parent = parent
|
||||
// }
|
||||
//
|
||||
// public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
|
||||
//
|
||||
// guard !annotation.isKind(of: MKUserLocation.self) else {
|
||||
// // Make a fast exit if the annotation is the `MKUserLocation`, as it's not an annotation view we wish to customize.
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// if let annotation = annotation as? PositionAnnotation {
|
||||
//
|
||||
// let annotationView = PositionAnnotationView(annotation: annotation, reuseIdentifier: "PositionAnnotation")
|
||||
// annotationView.name = annotation.shortName ?? "????"
|
||||
// annotationView.canShowCallout = true
|
||||
//
|
||||
// return annotationView
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
|
||||
//
|
||||
// if let index = self.overlays.firstIndex(where: { overlay_ in overlay_.shape.hash == overlay.hash }) {
|
||||
//
|
||||
// let unwrappedOverlay = self.overlays[index]
|
||||
//
|
||||
// if let circleOverlay = unwrappedOverlay.shape as? MKCircle {
|
||||
//
|
||||
// let renderer = MKCircleRenderer(circle: circleOverlay)
|
||||
// renderer.fillColor = unwrappedOverlay.fillColor
|
||||
// renderer.strokeColor = unwrappedOverlay.strokeColor
|
||||
// renderer.lineWidth = unwrappedOverlay.lineWidth
|
||||
// return renderer
|
||||
//
|
||||
// } else if let polygonOverlay = unwrappedOverlay.shape as? MKPolygon {
|
||||
//
|
||||
// let renderer = MKPolygonRenderer(polygon: polygonOverlay)
|
||||
// renderer.fillColor = unwrappedOverlay.fillColor
|
||||
// renderer.strokeColor = unwrappedOverlay.strokeColor
|
||||
// renderer.lineWidth = unwrappedOverlay.lineWidth
|
||||
// return renderer
|
||||
//
|
||||
// } else if let multiPolygonOverlay = unwrappedOverlay.shape as? MKMultiPolygon {
|
||||
//
|
||||
// let renderer = MKMultiPolygonRenderer(multiPolygon: multiPolygonOverlay)
|
||||
// renderer.fillColor = unwrappedOverlay.fillColor
|
||||
// renderer.strokeColor = unwrappedOverlay.strokeColor
|
||||
// renderer.lineWidth = unwrappedOverlay.lineWidth
|
||||
// return renderer
|
||||
//
|
||||
// } else if let polyLineOverlay = unwrappedOverlay.shape as? MKPolyline {
|
||||
//
|
||||
// let renderer = MKPolylineRenderer(polyline: polyLineOverlay)
|
||||
// renderer.fillColor = unwrappedOverlay.fillColor
|
||||
// renderer.strokeColor = unwrappedOverlay.strokeColor
|
||||
// renderer.lineWidth = unwrappedOverlay.lineWidth
|
||||
// return renderer
|
||||
//
|
||||
// } else if let multiPolylineOverlay = unwrappedOverlay.shape as? MKMultiPolyline {
|
||||
//
|
||||
// let renderer = MKMultiPolylineRenderer(multiPolyline: multiPolylineOverlay)
|
||||
// renderer.fillColor = unwrappedOverlay.fillColor
|
||||
// renderer.strokeColor = unwrappedOverlay.strokeColor
|
||||
// renderer.lineWidth = unwrappedOverlay.lineWidth
|
||||
// return renderer
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// return MKOverlayRenderer()
|
||||
//
|
||||
// }
|
||||
//
|
||||
// } else if let tileOverlay = overlay as? MKTileOverlay {
|
||||
//
|
||||
// return MKTileOverlayRenderer(tileOverlay: tileOverlay)
|
||||
//
|
||||
// } else {
|
||||
// return MKOverlayRenderer()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// is supposed to be located in the folder with the map name
|
||||
// public struct DefaultTile: Hashable {
|
||||
// let tileName: String
|
||||
// let tileType: String
|
||||
//
|
||||
// public init(tileName: String, tileType: String) {
|
||||
// self.tileName = tileName
|
||||
// self.tileType = tileType
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public struct CustomMapOverlay: Equatable, Hashable {
|
||||
// let mapName: String
|
||||
// let tileType: String
|
||||
// var canReplaceMapContent: Bool
|
||||
// var minimumZoomLevel: Int?
|
||||
// var maximumZoomLevel: Int?
|
||||
// let defaultTile: DefaultTile?
|
||||
//
|
||||
// public init(
|
||||
// mapName: String,
|
||||
// tileType: String,
|
||||
// canReplaceMapContent: Bool = true, // false for transparent tiles
|
||||
// minimumZoomLevel: Int? = nil,
|
||||
// maximumZoomLevel: Int? = nil,
|
||||
// defaultTile: DefaultTile? = nil
|
||||
// ) {
|
||||
//
|
||||
// self.mapName = mapName
|
||||
// self.tileType = tileType
|
||||
// self.canReplaceMapContent = canReplaceMapContent
|
||||
// self.minimumZoomLevel = minimumZoomLevel
|
||||
// self.maximumZoomLevel = maximumZoomLevel
|
||||
// self.defaultTile = defaultTile
|
||||
// }
|
||||
//
|
||||
// public init?(
|
||||
// mapName: String?,
|
||||
// tileType: String,
|
||||
// canReplaceMapContent: Bool = true, // false for transparent tiles
|
||||
// minimumZoomLevel: Int? = nil,
|
||||
// maximumZoomLevel: Int? = nil,
|
||||
// defaultTile: DefaultTile? = nil
|
||||
// ) {
|
||||
// if (mapName == nil || mapName! == "") {
|
||||
// return nil
|
||||
// }
|
||||
// self.mapName = mapName!
|
||||
// self.tileType = tileType
|
||||
// self.canReplaceMapContent = canReplaceMapContent
|
||||
// self.minimumZoomLevel = minimumZoomLevel
|
||||
// self.maximumZoomLevel = maximumZoomLevel
|
||||
// self.defaultTile = defaultTile
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public class CustomMapOverlaySource: MKTileOverlay {
|
||||
//
|
||||
// // requires folder: tiles/{mapName}/z/y/y,{tileType}
|
||||
// private var parent: MapView
|
||||
// private let mapName: String
|
||||
// private let tileType: String
|
||||
// private let defaultTile: DefaultTile?
|
||||
//
|
||||
// public init(
|
||||
// parent: MapView,
|
||||
// mapName: String,
|
||||
// tileType: String,
|
||||
// defaultTile: DefaultTile?
|
||||
// ) {
|
||||
// self.parent = parent
|
||||
// self.mapName = mapName
|
||||
// self.tileType = tileType
|
||||
// self.defaultTile = defaultTile
|
||||
// super.init(urlTemplate: "")
|
||||
// }
|
||||
//
|
||||
// public override func url(forTilePath path: MKTileOverlayPath) -> URL {
|
||||
// if let tileUrl = Bundle.main.url(
|
||||
// forResource: "\(path.y)",
|
||||
// withExtension: self.tileType,
|
||||
// subdirectory: "tiles/\(self.mapName)/\(path.z)/\(path.x)",
|
||||
// localization: nil
|
||||
// ) {
|
||||
// return tileUrl
|
||||
// } else if let defaultTile = self.defaultTile, let defaultTileUrl = Bundle.main.url(
|
||||
// forResource: defaultTile.tileName,
|
||||
// withExtension: defaultTile.tileType,
|
||||
// subdirectory: "tiles/\(self.mapName)",
|
||||
// localization: nil
|
||||
// ) {
|
||||
// return defaultTileUrl
|
||||
// } else {
|
||||
// let urlstring = self.mapName+"\(path.z)/\(path.x)/\(path.y).png"
|
||||
// return URL(string: urlstring)!
|
||||
// // Bundle.main.url(forResource: "surrounding", withExtension: "png", subdirectory: "tiles")!
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// public struct Overlay {
|
||||
//
|
||||
// public static func == (lhs: MapView.Overlay, rhs: MapView.Overlay) -> Bool {
|
||||
// // maybe to use in the future for comparison of full array
|
||||
// lhs.shape.coordinate.latitude == rhs.shape.coordinate.latitude &&
|
||||
// lhs.shape.coordinate.longitude == rhs.shape.coordinate.longitude &&
|
||||
// lhs.fillColor == rhs.fillColor
|
||||
// }
|
||||
//
|
||||
// var shape: MKOverlay
|
||||
// var fillColor: UIColor?
|
||||
// var strokeColor: UIColor?
|
||||
// var lineWidth: CGFloat
|
||||
//
|
||||
// public init(
|
||||
// shape: MKOverlay,
|
||||
// fillColor: UIColor? = nil,
|
||||
// strokeColor: UIColor? = nil,
|
||||
// lineWidth: CGFloat = 0
|
||||
// ) {
|
||||
// self.shape = shape
|
||||
// self.fillColor = fillColor
|
||||
// self.strokeColor = strokeColor
|
||||
// self.lineWidth = lineWidth
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//// MARK: End of implementation
|
||||
//#endif
|
||||
|
|
@ -12,7 +12,7 @@ func degreesToRadians(_ number: Double) -> Double {
|
|||
}
|
||||
|
||||
struct MapViewSwiftUI: UIViewRepresentable {
|
||||
|
||||
|
||||
var onLongPress: (_ waypointCoordinate: CLLocationCoordinate2D) -> Void
|
||||
var onWaypointEdit: (_ waypointId: Int ) -> Void
|
||||
let mapView = MKMapView()
|
||||
|
|
@ -21,28 +21,30 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
let mapViewType: MKMapType
|
||||
let userTrackingMode: MKUserTrackingMode
|
||||
let centeringMode: CenteringMode
|
||||
|
||||
|
||||
let centerOnPositionsOnly: Bool
|
||||
@AppStorage("meshMapRecentering") private var recenter: Bool = false
|
||||
|
||||
|
||||
// Offline Maps
|
||||
//make this view dependent on the UserDefault that is updated when importing a new map file
|
||||
// make this view dependent on the UserDefault that is updated when importing a new map file
|
||||
@AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0
|
||||
@State private var loadedLastUpdatedLocalMapFile = 0
|
||||
var customMapOverlay: CustomMapOverlay?
|
||||
@State private var presentCustomMapOverlayHash: CustomMapOverlay?
|
||||
var overlays: [Overlay] = []
|
||||
let dynamicRegion: Bool = true
|
||||
|
||||
|
||||
func makeUIView(context: Context) -> MKMapView {
|
||||
// Map View Parameters
|
||||
mapView.mapType = mapViewType
|
||||
mapView.addAnnotations(waypoints)
|
||||
mapView.setUserTrackingMode(userTrackingMode, animated: true)
|
||||
// Do the initial map centering
|
||||
let span = MKCoordinateSpan(latitudeDelta: 0.003, longitudeDelta: 0.003)
|
||||
let center = LocationHelper.currentLocation
|
||||
let region = MKCoordinateRegion(center: center, span: span)
|
||||
mapView.setRegion(region, animated: true)
|
||||
// Set user (phone gps) tracking options
|
||||
mapView.setUserTrackingMode(userTrackingMode, animated: true)
|
||||
if userTrackingMode != MKUserTrackingMode.none {
|
||||
mapView.showsUserLocation = true
|
||||
} else {
|
||||
|
|
@ -55,13 +57,13 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
mapView.fitAllAnnotations()
|
||||
}
|
||||
case .allPositions:
|
||||
if userTrackingMode != MKUserTrackingMode.none {
|
||||
if userTrackingMode == MKUserTrackingMode.none {
|
||||
mapView.fit(annotations: positions, andShow: true)
|
||||
} else {
|
||||
mapView.addAnnotations(positions)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Other MKMapView Settings
|
||||
mapView.preferredConfiguration.elevationStyle = .realistic// .flat
|
||||
mapView.isPitchEnabled = true
|
||||
|
|
@ -71,14 +73,14 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
mapView.showsBuildings = true
|
||||
mapView.showsScale = true
|
||||
mapView.showsTraffic = true
|
||||
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
// Show the default always visible compass and the mac only controls
|
||||
mapView.showsCompass = true
|
||||
mapView.showsZoomControls = true
|
||||
mapView.showsPitchControl = true
|
||||
#else
|
||||
|
||||
|
||||
#if os(iOS)
|
||||
// Hide the default compass that only appears when you are not going north and instead always show the compass in the bottom right corner of the map
|
||||
mapView.showsCompass = false
|
||||
|
|
@ -89,26 +91,26 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
compassButton.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -5).isActive = true
|
||||
compassButton.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -25).isActive = true
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
mapView.delegate = context.coordinator
|
||||
return mapView
|
||||
}
|
||||
|
||||
|
||||
func updateUIView(_ mapView: MKMapView, context: Context) {
|
||||
mapView.mapType = mapViewType
|
||||
|
||||
|
||||
if self.customMapOverlay != self.presentCustomMapOverlayHash || self.loadedLastUpdatedLocalMapFile != self.lastUpdatedLocalMapFile {
|
||||
mapView.removeOverlays(mapView.overlays)
|
||||
if self.customMapOverlay != nil {
|
||||
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let tilePath = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false).path
|
||||
if fileManager.fileExists(atPath: tilePath) {
|
||||
print("Loading local map file")
|
||||
if let overlay = LocalMBTileOverlay(mbTilePath: tilePath) {
|
||||
overlay.canReplaceMapContent = false//customMapOverlay.canReplaceMapContent
|
||||
overlay.canReplaceMapContent = false// customMapOverlay.canReplaceMapContent
|
||||
mapView.addOverlay(overlay)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -120,9 +122,9 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
self.loadedLastUpdatedLocalMapFile = self.lastUpdatedLocalMapFile
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
|
||||
let annotationCount = waypoints.count + positions.count
|
||||
if annotationCount != mapView.annotations.count {
|
||||
mapView.removeAnnotations(mapView.annotations)
|
||||
|
|
@ -149,17 +151,17 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func makeCoordinator() -> MapCoordinator {
|
||||
return Coordinator(self)
|
||||
}
|
||||
|
||||
|
||||
final class MapCoordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
|
||||
|
||||
|
||||
var parent: MapViewSwiftUI
|
||||
var longPressRecognizer = UILongPressGestureRecognizer()
|
||||
var overlays: [Overlay] = []
|
||||
|
||||
|
||||
init(_ parent: MapViewSwiftUI) {
|
||||
self.parent = parent
|
||||
super.init()
|
||||
|
|
@ -170,22 +172,21 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
self.parent.mapView.addGestureRecognizer(longPressRecognizer)
|
||||
self.overlays = []
|
||||
}
|
||||
|
||||
|
||||
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
|
||||
|
||||
|
||||
switch annotation {
|
||||
case let positionAnnotation as PositionEntity:
|
||||
let reuseID = String(positionAnnotation.nodePosition?.num ?? 0) + "-" + String(positionAnnotation.time?.timeIntervalSince1970 ?? 0)
|
||||
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "node") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: reuseID )
|
||||
annotationView.tag = -1
|
||||
annotationView.canShowCallout = true
|
||||
|
||||
|
||||
if positionAnnotation.latest {
|
||||
annotationView.markerTintColor = .systemRed
|
||||
annotationView.displayPriority = .required
|
||||
annotationView.titleVisibility = .visible
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
annotationView.markerTintColor = UIColor(.indigo)
|
||||
annotationView.displayPriority = .defaultHigh
|
||||
annotationView.titleVisibility = .adaptive
|
||||
|
|
@ -203,10 +204,10 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
let distanceFormatter = MKDistanceFormatter()
|
||||
subtitle.text! += "Altitude: \(distanceFormatter.string(fromDistance: Double(positionAnnotation.altitude))) \n"
|
||||
if positionAnnotation.nodePosition?.metadata != nil {
|
||||
|
||||
|
||||
if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.client ||
|
||||
DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.clientMute ||
|
||||
DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.routerClient{
|
||||
DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.routerClient {
|
||||
annotationView.glyphImage = UIImage(systemName: "flipphone")
|
||||
} else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.repeater {
|
||||
annotationView.glyphImage = UIImage(systemName: "repeat")
|
||||
|
|
@ -217,7 +218,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
} else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.sensor {
|
||||
annotationView.glyphImage = UIImage(systemName: "sensor")
|
||||
}
|
||||
|
||||
|
||||
let pf = PositionFlags(rawValue: Int(positionAnnotation.nodePosition?.metadata?.positionFlags ?? 3))
|
||||
if pf.contains(.Satsinview) {
|
||||
subtitle.text! += "Sats in view: \(String(positionAnnotation.satsInView)) \n"
|
||||
|
|
@ -225,13 +226,8 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
if pf.contains(.SeqNo) {
|
||||
subtitle.text! += "Sequence: \(String(positionAnnotation.seqNo)) \n"
|
||||
}
|
||||
if pf.contains(.Speed) {
|
||||
let formatter = MeasurementFormatter()
|
||||
formatter.locale = Locale.current
|
||||
subtitle.text! += "Speed: \(formatter.string(from: Measurement(value: Double(positionAnnotation.speed), unit: UnitSpeed.kilometersPerHour))) \n"
|
||||
}
|
||||
if pf.contains(.Heading){
|
||||
|
||||
if pf.contains(.Heading) {
|
||||
|
||||
if parent.userTrackingMode != MKUserTrackingMode.followWithHeading {
|
||||
annotationView.glyphImage = UIImage(systemName: "location.north.fill")?.rotate(radians: Float(degreesToRadians(Double(positionAnnotation.heading))))
|
||||
subtitle.text! += "Heading: \(String(positionAnnotation.heading)) \n"
|
||||
|
|
@ -239,9 +235,23 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
annotationView.glyphImage = UIImage(systemName: "flipphone")
|
||||
}
|
||||
}
|
||||
if pf.contains(.Speed) {
|
||||
let formatter = MeasurementFormatter()
|
||||
formatter.locale = Locale.current
|
||||
if positionAnnotation.speed <= 1 {
|
||||
annotationView.glyphImage = UIImage(systemName: "hexagon")
|
||||
}
|
||||
subtitle.text! += "Speed: \(formatter.string(from: Measurement(value: Double(positionAnnotation.speed), unit: UnitSpeed.kilometersPerHour))) \n"
|
||||
}
|
||||
|
||||
} else {
|
||||
// node metadata is nil
|
||||
annotationView.glyphImage = UIImage(systemName: "flipphone")
|
||||
}
|
||||
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
|
||||
let metersAway = positionAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
|
||||
subtitle.text! += NSLocalizedString("distance", comment: "") + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
|
||||
}
|
||||
subtitle.text! += positionAnnotation.time?.formatted() ?? "Unknown \n"
|
||||
subtitle.numberOfLines = 0
|
||||
annotationView.detailCalloutAccessoryView = subtitle
|
||||
|
|
@ -268,10 +278,14 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
let subtitle = UILabel()
|
||||
if waypointAnnotation.longDescription?.count ?? 0 > 0 {
|
||||
subtitle.text = (waypointAnnotation.longDescription ?? "") + "\n"
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
subtitle.text = ""
|
||||
}
|
||||
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
|
||||
let metersAway = waypointAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
|
||||
let distanceFormatter = MKDistanceFormatter()
|
||||
subtitle.text! += NSLocalizedString("distance", comment: "") + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
|
||||
}
|
||||
if waypointAnnotation.created != nil {
|
||||
subtitle.text! += "Created: \(waypointAnnotation.created?.formatted() ?? "Unknown") \n"
|
||||
}
|
||||
|
|
@ -290,23 +304,23 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
|
||||
// Only Allow Edit for waypoint annotations with a id
|
||||
if view.tag > 0 {
|
||||
parent.onWaypointEdit(view.tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func longPressHandler(_ gesture: UILongPressGestureRecognizer) {
|
||||
|
||||
|
||||
if gesture.state != UIGestureRecognizer.State.ended {
|
||||
return
|
||||
} else if gesture.state != UIGestureRecognizer.State.began {
|
||||
|
||||
|
||||
// Screen Position - CGPoint
|
||||
let location = longPressRecognizer.location(in: self.parent.mapView)
|
||||
|
||||
|
||||
// Map Coordinate - CLLocationCoordinate2D
|
||||
let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
|
||||
let annotation = MKPointAnnotation()
|
||||
|
|
@ -317,11 +331,11 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
parent.onLongPress(coordinate)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
|
||||
|
||||
|
||||
if let index = self.overlays.firstIndex(where: { overlay_ in overlay_.shape.hash == overlay.hash }) {
|
||||
|
||||
|
||||
let unwrappedOverlay = self.overlays[index]
|
||||
if let circleOverlay = unwrappedOverlay.shape as? MKCircle {
|
||||
let renderer = MKCircleRenderer(circle: circleOverlay)
|
||||
|
|
@ -363,18 +377,18 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// is supposed to be located in the folder with the map name
|
||||
public struct DefaultTile: Hashable {
|
||||
let tileName: String
|
||||
let tileType: String
|
||||
|
||||
|
||||
public init(tileName: String, tileType: String) {
|
||||
self.tileName = tileName
|
||||
self.tileType = tileType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct CustomMapOverlay: Equatable, Hashable {
|
||||
let mapName: String
|
||||
let tileType: String
|
||||
|
|
@ -382,7 +396,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
var minimumZoomLevel: Int?
|
||||
var maximumZoomLevel: Int?
|
||||
let defaultTile: DefaultTile?
|
||||
|
||||
|
||||
public init(
|
||||
mapName: String,
|
||||
tileType: String,
|
||||
|
|
@ -398,7 +412,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
self.maximumZoomLevel = maximumZoomLevel
|
||||
self.defaultTile = defaultTile
|
||||
}
|
||||
|
||||
|
||||
public init?(
|
||||
mapName: String?,
|
||||
tileType: String,
|
||||
|
|
@ -407,7 +421,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
maximumZoomLevel: Int? = nil,
|
||||
defaultTile: DefaultTile? = nil
|
||||
) {
|
||||
if (mapName == nil || mapName! == "") {
|
||||
if mapName == nil || mapName! == "" {
|
||||
return nil
|
||||
}
|
||||
self.mapName = mapName!
|
||||
|
|
@ -418,15 +432,15 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
self.defaultTile = defaultTile
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class CustomMapOverlaySource: MKTileOverlay {
|
||||
|
||||
|
||||
// requires folder: tiles/{mapName}/z/y/y,{tileType}
|
||||
private var parent: MapViewSwiftUI
|
||||
private let mapName: String
|
||||
private let tileType: String
|
||||
private let defaultTile: DefaultTile?
|
||||
|
||||
|
||||
public init(
|
||||
parent: MapViewSwiftUI,
|
||||
mapName: String,
|
||||
|
|
@ -439,7 +453,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
self.defaultTile = defaultTile
|
||||
super.init(urlTemplate: "")
|
||||
}
|
||||
|
||||
|
||||
public override func url(forTilePath path: MKTileOverlayPath) -> URL {
|
||||
if let tileUrl = Bundle.main.url(
|
||||
forResource: "\(path.y)",
|
||||
|
|
@ -461,21 +475,21 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct Overlay {
|
||||
|
||||
|
||||
public static func == (lhs: MapViewSwiftUI.Overlay, rhs: MapViewSwiftUI.Overlay) -> Bool {
|
||||
// maybe to use in the future for comparison of full array
|
||||
lhs.shape.coordinate.latitude == rhs.shape.coordinate.latitude &&
|
||||
lhs.shape.coordinate.longitude == rhs.shape.coordinate.longitude &&
|
||||
lhs.fillColor == rhs.fillColor
|
||||
}
|
||||
|
||||
|
||||
var shape: MKOverlay
|
||||
var fillColor: UIColor?
|
||||
var strokeColor: UIColor?
|
||||
var lineWidth: CGFloat
|
||||
|
||||
|
||||
public init(
|
||||
shape: MKOverlay,
|
||||
fillColor: UIColor? = nil,
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
////
|
||||
//// PositionAnnotationView.swift
|
||||
//// MeshtasticApple
|
||||
////
|
||||
//// Created by Joshua Pirihi on 24/12/21.
|
||||
////
|
||||
//
|
||||
//import UIKit
|
||||
//import MapKit
|
||||
//import SwiftUI
|
||||
//
|
||||
//// a simple circle annotation, with a string in it
|
||||
//class PositionAnnotation: NSObject, MKAnnotation {
|
||||
//
|
||||
// // This property must be key-value observable, which the `@objc dynamic` attributes provide.
|
||||
// @objc dynamic var coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0)
|
||||
//
|
||||
// // Required if you set the annotation view's `canShowCallout` property to `true`
|
||||
// // this string fills the callout label when you tap an annotation
|
||||
// var title: String?
|
||||
//
|
||||
// // the text to appear inside the little circle
|
||||
// var shortName: String?
|
||||
//
|
||||
//}
|
||||
//
|
||||
//class PositionAnnotationView: MKAnnotationView {
|
||||
//
|
||||
// private let annotationFrame = CGRect(x: 0, y: 0, width: 40, height: 40)
|
||||
// private let label: UILabel
|
||||
//
|
||||
// override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
|
||||
// self.label = UILabel(frame: annotationFrame.offsetBy(dx: 0, dy: 0))
|
||||
// super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
|
||||
// self.frame = annotationFrame
|
||||
// self.label.font = UIFont.preferredFont(forTextStyle: .caption2)
|
||||
// self.label.textColor = .white
|
||||
// self.label.textAlignment = .center
|
||||
// self.backgroundColor = .clear
|
||||
// self.addSubview(label)
|
||||
// }
|
||||
//
|
||||
// required init?(coder aDecoder: NSCoder) {
|
||||
// fatalError("init(coder:) not implemented!")
|
||||
// }
|
||||
//
|
||||
// public var name: String = "" {
|
||||
// didSet {
|
||||
// self.label.text = name
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override func draw(_ rect: CGRect) {
|
||||
// guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
//
|
||||
// let circleRect = CGRect(x: 1, y: 1, width: 38, height: 38)
|
||||
// context.setFillColor(Color.accentColor.cgColor ?? CGColor(red: 0, green: 0.5, blue: 1.0, alpha: 1.0))
|
||||
// context.fillEllipse(in: circleRect)
|
||||
// }
|
||||
//}
|
||||
|
|
@ -9,14 +9,14 @@ import SwiftUI
|
|||
import CoreLocation
|
||||
|
||||
struct WaypointFormView: View {
|
||||
|
||||
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State var coordinate: CLLocationCoordinate2D
|
||||
@State var waypointId : Int = 0
|
||||
|
||||
@State var waypointId: Int = 0
|
||||
|
||||
@FocusState private var iconIsFocused: Bool
|
||||
|
||||
|
||||
@State private var name: String = ""
|
||||
@State private var description: String = ""
|
||||
@State private var icon: String = "📍"
|
||||
|
|
@ -26,9 +26,9 @@ struct WaypointFormView: View {
|
|||
@State private var expire: Date = Date() // = Date.now.addingTimeInterval(60 * 120) // 1 minute * 120 = 2 Hours
|
||||
@State private var locked: Bool = false
|
||||
@State private var lockedTo: Int64 = 0
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
Form {
|
||||
let distance = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude).distance(from: CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude))
|
||||
Section(header: Text((waypointId > 0) ? "Editing Waypoint" : "Create Waypoint")) {
|
||||
|
|
@ -52,7 +52,7 @@ struct WaypointFormView: View {
|
|||
axis: .vertical
|
||||
)
|
||||
.foregroundColor(Color.gray)
|
||||
.onChange(of: name, perform: { value in
|
||||
.onChange(of: name, perform: { _ in
|
||||
let totalBytes = name.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
|
|
@ -73,7 +73,7 @@ struct WaypointFormView: View {
|
|||
axis: .vertical
|
||||
)
|
||||
.foregroundColor(Color.gray)
|
||||
.onChange(of: description, perform: { value in
|
||||
.onChange(of: description, perform: { _ in
|
||||
let totalBytes = description.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 100 {
|
||||
|
|
@ -92,14 +92,14 @@ struct WaypointFormView: View {
|
|||
.font(.title)
|
||||
.focused($iconIsFocused)
|
||||
.onChange(of: icon) { value in
|
||||
|
||||
|
||||
// If you have anything other than emojis in your string make it empty
|
||||
if !value.onlyEmojis() {
|
||||
icon = ""
|
||||
}
|
||||
// If a second emoji is entered delete the first one
|
||||
if value.count >= 1 {
|
||||
|
||||
|
||||
if value.count > 1 {
|
||||
let index = value.index(value.startIndex, offsetBy: 1)
|
||||
icon = String(value[index])
|
||||
|
|
@ -107,7 +107,7 @@ struct WaypointFormView: View {
|
|||
iconIsFocused = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Toggle(isOn: $expires) {
|
||||
Label("Expires", systemImage: "clock.badge.xmark")
|
||||
|
|
@ -126,9 +126,9 @@ struct WaypointFormView: View {
|
|||
}
|
||||
HStack {
|
||||
Button {
|
||||
|
||||
|
||||
var newWaypoint = Waypoint()
|
||||
|
||||
|
||||
if waypointId > 0 {
|
||||
newWaypoint.id = UInt32(waypointId)
|
||||
} else {
|
||||
|
|
@ -144,7 +144,7 @@ struct WaypointFormView: View {
|
|||
let unicode = unicodeScalers[unicodeScalers.startIndex].value
|
||||
newWaypoint.icon = unicode
|
||||
if locked {
|
||||
|
||||
|
||||
if lockedTo == 0 {
|
||||
newWaypoint.lockedTo = UInt32(bleManager.connectedPeripheral!.num)
|
||||
} else {
|
||||
|
|
@ -172,8 +172,8 @@ struct WaypointFormView: View {
|
|||
.controlSize(.large)
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
.padding(.bottom)
|
||||
|
||||
Button(role:.cancel) {
|
||||
|
||||
Button(role: .cancel) {
|
||||
dismiss()
|
||||
} label: {
|
||||
Label("cancel", systemImage: "x.circle")
|
||||
|
|
@ -184,7 +184,7 @@ struct WaypointFormView: View {
|
|||
.padding(.bottom)
|
||||
|
||||
if waypointId > 0 {
|
||||
|
||||
|
||||
Menu {
|
||||
Button("For me", action: {
|
||||
let waypoint = getWaypoint(id: Int64(waypointId), context: bleManager.context!)
|
||||
|
|
@ -197,10 +197,10 @@ struct WaypointFormView: View {
|
|||
dismiss() })
|
||||
Button("For everyone", action: {
|
||||
var newWaypoint = Waypoint()
|
||||
|
||||
|
||||
if waypointId > 0 {
|
||||
newWaypoint.id = UInt32(waypointId)
|
||||
}
|
||||
}
|
||||
newWaypoint.name = name.count > 0 ? name : "Dropped Pin"
|
||||
newWaypoint.description_p = description
|
||||
newWaypoint.latitudeI = Int32(coordinate.latitude * 1e7)
|
||||
|
|
@ -211,7 +211,7 @@ struct WaypointFormView: View {
|
|||
let unicode = unicodeScalers[unicodeScalers.startIndex].value
|
||||
newWaypoint.icon = unicode
|
||||
if locked {
|
||||
|
||||
|
||||
if lockedTo == 0 {
|
||||
newWaypoint.lockedTo = UInt32(bleManager.connectedPeripheral!.num)
|
||||
} else {
|
||||
|
|
@ -241,7 +241,7 @@ struct WaypointFormView: View {
|
|||
}
|
||||
.onChange(of: waypointId) { newId in
|
||||
print(newId)
|
||||
|
||||
|
||||
}
|
||||
.onAppear {
|
||||
if waypointId > 0 {
|
||||
|
|
|
|||
|
|
@ -9,27 +9,27 @@ import SwiftUI
|
|||
import CoreData
|
||||
|
||||
struct ChannelMessageList: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
|
||||
enum Field: Hashable {
|
||||
case messageText
|
||||
}
|
||||
|
||||
|
||||
// Keyboard State
|
||||
@State var typingMessage: String = ""
|
||||
@State private var totalBytes = 0
|
||||
var maxbytes = 228
|
||||
@FocusState var focusedField: Field?
|
||||
|
||||
|
||||
@ObservedObject var channel: ChannelEntity
|
||||
@State var showDeleteMessageAlert = false
|
||||
@State private var deleteMessageId: Int64 = 0
|
||||
@State private var replyMessageId: Int64 = 0
|
||||
@State private var sendPositionWithMessage: Bool = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmssa", options: 0, locale: Locale.current)
|
||||
|
|
@ -54,8 +54,8 @@ struct ChannelMessageList: View {
|
|||
.padding(.trailing)
|
||||
}
|
||||
}
|
||||
HStack (alignment: .top) {
|
||||
if currentUser { Spacer(minLength:50) }
|
||||
HStack(alignment: .top) {
|
||||
if currentUser { Spacer(minLength: 50) }
|
||||
if !currentUser {
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.gray), circleSize: 44, fontSize: 14)
|
||||
.padding(.all, 5)
|
||||
|
|
@ -71,7 +71,7 @@ struct ChannelMessageList: View {
|
|||
.background(currentUser ? .accentColor : Color(.gray))
|
||||
.cornerRadius(15)
|
||||
.contextMenu {
|
||||
VStack{
|
||||
VStack {
|
||||
Text("channel")+Text(": \(message.channel)")
|
||||
}
|
||||
Menu("tapback") {
|
||||
|
|
@ -81,7 +81,7 @@ struct ChannelMessageList: View {
|
|||
print("Sent \(tb.emojiString) Tapback")
|
||||
self.context.refresh(channel, mergeChanges: true)
|
||||
} else { print("\(tb.emojiString) Tapback Failed") }
|
||||
|
||||
|
||||
}) {
|
||||
Text(tb.description)
|
||||
let image = tb.emojiString.image()
|
||||
|
|
@ -152,11 +152,10 @@ struct ChannelMessageList: View {
|
|||
Image(systemName: "trash")
|
||||
}
|
||||
}
|
||||
|
||||
let tapbacks = message.value(forKey: "tapbacks") as! [MessageEntity]
|
||||
let tapbacks = message.value(forKey: "tapbacks") as? [MessageEntity] ?? []
|
||||
if tapbacks.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
HStack {
|
||||
VStack(alignment: .trailing) {
|
||||
HStack {
|
||||
ForEach( tapbacks ) { (tapback: MessageEntity) in
|
||||
VStack {
|
||||
let image = tapback.messagePayload!.image(fontSize: 20)
|
||||
|
|
@ -193,7 +192,7 @@ struct ChannelMessageList: View {
|
|||
.padding(.bottom)
|
||||
.id(channel.allPrivateMessages.firstIndex(of: message))
|
||||
if !currentUser {
|
||||
Spacer(minLength:50)
|
||||
Spacer(minLength: 50)
|
||||
}
|
||||
}
|
||||
.padding([.leading, .trailing])
|
||||
|
|
@ -225,7 +224,7 @@ struct ChannelMessageList: View {
|
|||
scrollView.scrollTo(channel.allPrivateMessages.last!.messageId)
|
||||
}
|
||||
})
|
||||
.onChange(of: channel.allPrivateMessages, perform: { messages in
|
||||
.onChange(of: channel.allPrivateMessages, perform: { _ in
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
scrollView.scrollTo(channel.allPrivateMessages.last!.messageId)
|
||||
}
|
||||
|
|
@ -238,14 +237,14 @@ struct ChannelMessageList: View {
|
|||
let userLongName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown"
|
||||
sendPositionWithMessage = true
|
||||
if userSettings.meshtasticUsername.count > 0 {
|
||||
|
||||
|
||||
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with you from node " + userLongName
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
typingMessage = "📍 " + userLongName + " has shared their position with you."
|
||||
}
|
||||
|
||||
|
||||
} label: {
|
||||
Text("share.position")
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
|
|
@ -261,7 +260,7 @@ struct ChannelMessageList: View {
|
|||
}
|
||||
#endif
|
||||
HStack(alignment: .top) {
|
||||
|
||||
|
||||
ZStack {
|
||||
let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0)
|
||||
TextField("message", text: $typingMessage, axis: .vertical)
|
||||
|
|
@ -290,20 +289,20 @@ struct ChannelMessageList: View {
|
|||
let userLongName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown"
|
||||
sendPositionWithMessage = true
|
||||
if userSettings.meshtasticUsername.count > 0 {
|
||||
|
||||
|
||||
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with you from node " + userLongName
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
typingMessage = "📍 " + userLongName + " has shared their position with you."
|
||||
}
|
||||
|
||||
|
||||
} label: {
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.accentColor)
|
||||
}
|
||||
|
||||
|
||||
ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
|
||||
.frame(width: 130)
|
||||
.padding(5)
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@ struct Contacts: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@ObservedObject private var userSettings: UserSettings = UserSettings()
|
||||
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)],
|
||||
animation: .default)
|
||||
|
||||
|
||||
private var users: FetchedResults<UserEntity>
|
||||
@State var node: NodeInfoEntity? = nil
|
||||
@State private var userSelection: UserEntity? = nil // Nothing selected by default.
|
||||
@State private var channelSelection: ChannelEntity? = nil // Nothing selected by default.
|
||||
@State var node: NodeInfoEntity?
|
||||
@State private var userSelection: UserEntity? // Nothing selected by default.
|
||||
@State private var channelSelection: ChannelEntity? // Nothing selected by default.
|
||||
@State private var isPresentingDeleteChannelMessagesConfirm: Bool = false
|
||||
@State private var isPresentingDeleteUserMessagesConfirm: Bool = false
|
||||
@State private var isPresentingTraceRouteSentAlert = false
|
||||
|
|
@ -38,7 +38,7 @@ struct Contacts: View {
|
|||
ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in
|
||||
if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" {
|
||||
NavigationLink(destination: ChannelMessageList(channel: channel)) {
|
||||
|
||||
|
||||
let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index })
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
|
||||
let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0
|
||||
|
|
@ -60,7 +60,7 @@ struct Contacts: View {
|
|||
}
|
||||
Spacer()
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
VStack(alignment: .trailing) {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.subheadline)
|
||||
|
|
@ -102,7 +102,7 @@ struct Contacts: View {
|
|||
// Would rather not do this but the merge changes on
|
||||
// A single object is only working on mac GVH
|
||||
context.refreshAllObjects()
|
||||
//context.refresh(channel, mergeChanges: true)
|
||||
// context.refresh(channel, mergeChanges: true)
|
||||
} catch {
|
||||
context.rollback()
|
||||
print("💥 Save Channel Mute Error")
|
||||
|
|
@ -136,7 +136,7 @@ struct Contacts: View {
|
|||
}
|
||||
}
|
||||
.padding([.top, .bottom])
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Section(header: Text("direct.messages")) {
|
||||
|
|
@ -157,7 +157,7 @@ struct Contacts: View {
|
|||
Text(user.longName ?? NSLocalizedString("unknown", comment: "Unknown")).font(.headline)
|
||||
Spacer()
|
||||
if user.messageList.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
VStack(alignment: .trailing) {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.subheadline)
|
||||
|
|
@ -218,8 +218,7 @@ struct Contacts: View {
|
|||
.alert(
|
||||
"Trace Route Sent",
|
||||
isPresented: $isPresentingTraceRouteSentAlert
|
||||
)
|
||||
{
|
||||
) {
|
||||
Button("OK", role: .cancel) { }
|
||||
}
|
||||
message: {
|
||||
|
|
@ -258,21 +257,23 @@ struct Contacts: View {
|
|||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(bleManager.connectedPeripheral?.num ?? -1))
|
||||
do {
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
// Found a node, check it for a region
|
||||
if !fetchedNode.isEmpty {
|
||||
node = fetchedNode[0]
|
||||
}
|
||||
} catch {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
detail: {
|
||||
if let user = userSelection {
|
||||
UserMessageList(user:user)
|
||||
|
||||
UserMessageList(user: user)
|
||||
|
||||
} else {
|
||||
Text("select.contact")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import SwiftUI
|
|||
import CoreData
|
||||
|
||||
struct UserMessageList: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
|
||||
enum Field: Hashable {
|
||||
case messageText
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ struct UserMessageList: View {
|
|||
@State private var deleteMessageId: Int64 = 0
|
||||
@State private var replyMessageId: Int64 = 0
|
||||
@State private var sendPositionWithMessage: Bool = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmss", options: 0, locale: Locale.current)
|
||||
|
|
@ -39,7 +39,7 @@ struct UserMessageList: View {
|
|||
ForEach( user.messageList ) { (message: MessageEntity) in
|
||||
if user.num != bleManager.connectedPeripheral?.num ?? -1 {
|
||||
let currentUser: Bool = (bleManager.connectedPeripheral?.num ?? 0 == message.fromUser?.num ?? -1 ? true : false)
|
||||
|
||||
|
||||
if message.replyID > 0 {
|
||||
let messageReply = user.messageList.first(where: { $0.messageId == message.replyID })
|
||||
HStack {
|
||||
|
|
@ -55,8 +55,8 @@ struct UserMessageList: View {
|
|||
.padding(.trailing)
|
||||
}
|
||||
}
|
||||
HStack (alignment: .top) {
|
||||
if currentUser { Spacer(minLength:50) }
|
||||
HStack(alignment: .top) {
|
||||
if currentUser { Spacer(minLength: 50) }
|
||||
if !currentUser {
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.gray), circleSize: 44, fontSize: 14)
|
||||
.padding(.all, 5)
|
||||
|
|
@ -64,7 +64,7 @@ struct UserMessageList: View {
|
|||
}
|
||||
VStack(alignment: currentUser ? .trailing : .leading) {
|
||||
let markdownText: LocalizedStringKey = LocalizedStringKey.init(message.messagePayloadMarkdown ?? (message.messagePayload ?? "EMPTY MESSAGE"))
|
||||
|
||||
|
||||
let linkBlue = Color(red: 0.4627, green: 0.8392, blue: 1) /* #76d6ff */
|
||||
Text(markdownText)
|
||||
.tint(linkBlue)
|
||||
|
|
@ -73,7 +73,7 @@ struct UserMessageList: View {
|
|||
.background(currentUser ? .accentColor : Color(.gray))
|
||||
.cornerRadius(15)
|
||||
.contextMenu {
|
||||
VStack{
|
||||
VStack {
|
||||
Text("channel")+Text(": \(message.channel)")
|
||||
}
|
||||
Menu("tapback") {
|
||||
|
|
@ -83,7 +83,7 @@ struct UserMessageList: View {
|
|||
print("Sent \(tb.emojiString) Tapback")
|
||||
self.context.refresh(user, mergeChanges: true)
|
||||
} else { print("\(tb.emojiString) Tapback Failed") }
|
||||
|
||||
|
||||
}) {
|
||||
Text(tb.description)
|
||||
let image = tb.emojiString.image()
|
||||
|
|
@ -157,11 +157,11 @@ struct UserMessageList: View {
|
|||
Image(systemName: "trash")
|
||||
}
|
||||
}
|
||||
|
||||
let tapbacks = message.value(forKey: "tapbacks") as! [MessageEntity]
|
||||
|
||||
let tapbacks = message.value(forKey: "tapbacks") as? [MessageEntity] ?? []
|
||||
if tapbacks.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
HStack {
|
||||
VStack(alignment: .trailing) {
|
||||
HStack {
|
||||
ForEach( tapbacks ) { (tapback: MessageEntity) in
|
||||
VStack {
|
||||
let image = tapback.messagePayload!.image(fontSize: 20)
|
||||
|
|
@ -198,7 +198,7 @@ struct UserMessageList: View {
|
|||
.padding(.bottom)
|
||||
.id(user.messageList.firstIndex(of: message))
|
||||
if !currentUser {
|
||||
Spacer(minLength:50)
|
||||
Spacer(minLength: 50)
|
||||
}
|
||||
}
|
||||
.padding([.leading, .trailing])
|
||||
|
|
@ -231,7 +231,7 @@ struct UserMessageList: View {
|
|||
scrollView.scrollTo(user.messageList.last!.messageId)
|
||||
}
|
||||
})
|
||||
.onChange(of: user.messageList, perform: { messages in
|
||||
.onChange(of: user.messageList, perform: { _ in
|
||||
if user.messageList.count > 0 {
|
||||
scrollView.scrollTo(user.messageList.last!.messageId)
|
||||
}
|
||||
|
|
@ -243,13 +243,13 @@ struct UserMessageList: View {
|
|||
Button {
|
||||
let userLongName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown"
|
||||
sendPositionWithMessage = true
|
||||
|
||||
|
||||
if userSettings.meshtasticUsername.count > 0 {
|
||||
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with you from node " + userLongName + " and requested a response with your position."
|
||||
} else {
|
||||
typingMessage = "📍 " + userLongName + " has shared their position and requested a response with your position."
|
||||
}
|
||||
|
||||
|
||||
} label: {
|
||||
Text("share.position")
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
|
|
@ -264,7 +264,7 @@ struct UserMessageList: View {
|
|||
.padding(.trailing)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
HStack(alignment: .top) {
|
||||
ZStack {
|
||||
let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0)
|
||||
|
|
@ -293,13 +293,13 @@ struct UserMessageList: View {
|
|||
Button {
|
||||
let userLongName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown"
|
||||
sendPositionWithMessage = true
|
||||
|
||||
|
||||
if userSettings.meshtasticUsername.count > 0 {
|
||||
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with you from node " + userLongName + " and requested a response with your position."
|
||||
} else {
|
||||
typingMessage = "📍 " + userLongName + " has shared their position and requested a response with your position."
|
||||
}
|
||||
|
||||
|
||||
} label: {
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@ import SwiftUI
|
|||
import Charts
|
||||
|
||||
struct DeviceMetricsLog: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
||||
@State private var isPresentingClearLogConfirm: Bool = false
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
var node: NodeInfoEntity
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
let oneDayAgo = Calendar.current.date(byAdding: .day, value: -3, to: Date())
|
||||
let data = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0 && time !=nil && time >= %@", oneDayAgo! as CVarArg)) ?? []
|
||||
if data.count > 0 {
|
||||
GroupBox(label: Label("battery.level.trend", systemImage: "battery.100")) {
|
||||
GroupBox(label: Label("battery.level.trend", systemImage: "battery.100")) {
|
||||
Chart(data.array as! [TelemetryEntity], id: \.self) {
|
||||
LineMark(
|
||||
x: .value("Hour", $0.time!.formattedDate(format: "ha")),
|
||||
|
|
@ -39,15 +39,15 @@ struct DeviceMetricsLog: View {
|
|||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//Add a table for mac and ipad
|
||||
// Add a table for mac and ipad
|
||||
Table(node.telemetries!.reversed() as! [TelemetryEntity]) {
|
||||
|
||||
|
||||
TableColumn("battery.level") { dm in
|
||||
if dm.metricsType == 0 {
|
||||
if dm.batteryLevel == 0 {
|
||||
Text("Powered")
|
||||
} else {
|
||||
|
||||
|
||||
Text("\(String(dm.batteryLevel))%")
|
||||
}
|
||||
}
|
||||
|
|
@ -73,10 +73,10 @@ struct DeviceMetricsLog: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
ScrollView {
|
||||
|
||||
|
||||
let columns = [
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
GridItem(.flexible(minimum: 30, maximum: 60), spacing: 0.1),
|
||||
|
|
@ -118,7 +118,7 @@ struct DeviceMetricsLog: View {
|
|||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", dm.airUtilTx))%")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Text(dm.time?.formattedDate(format: dateFormatString) ?? "Unknown time")
|
||||
.font(.caption2)
|
||||
}
|
||||
|
|
@ -154,7 +154,7 @@ struct DeviceMetricsLog: View {
|
|||
}
|
||||
}
|
||||
Button {
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 0)
|
||||
exportString = telemetryToCsvFile(telemetry: node.telemetries!.array as? [TelemetryEntity] ?? [], metricsType: 0)
|
||||
isExporting = true
|
||||
} label: {
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
|
|
@ -182,7 +182,7 @@ struct DeviceMetricsLog: View {
|
|||
if case .success = result {
|
||||
print("Device metrics log download succeeded.")
|
||||
self.isExporting = false
|
||||
|
||||
|
||||
} else {
|
||||
print("Device metrics log download failed: \(result).")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,24 +7,24 @@
|
|||
import SwiftUI
|
||||
|
||||
struct EnvironmentMetricsLog: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
||||
@State private var isPresentingClearLogConfirm: Bool = false
|
||||
|
||||
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
NavigationStack {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//Add a table for mac and ipad
|
||||
// Add a table for mac and ipad
|
||||
Table(node.telemetries!.reversed() as! [TelemetryEntity]) {
|
||||
TableColumn("Temperature") { em in
|
||||
if em.metricsType == 1 {
|
||||
|
|
@ -72,7 +72,7 @@ struct EnvironmentMetricsLog: View {
|
|||
GridItem(spacing: 0)
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1) {
|
||||
|
||||
|
||||
GridRow {
|
||||
Text("Temp")
|
||||
.font(.caption)
|
||||
|
|
@ -91,11 +91,11 @@ struct EnvironmentMetricsLog: View {
|
|||
.fontWeight(.bold)
|
||||
}
|
||||
ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (em: TelemetryEntity) in
|
||||
|
||||
|
||||
if em.metricsType == 1 {
|
||||
|
||||
|
||||
GridRow {
|
||||
|
||||
|
||||
Text(em.temperature.formattedTemperature())
|
||||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", em.relativeHumidity))")
|
||||
|
|
@ -116,7 +116,7 @@ struct EnvironmentMetricsLog: View {
|
|||
}
|
||||
}
|
||||
HStack {
|
||||
|
||||
|
||||
Button(role: .destructive) {
|
||||
isPresentingClearLogConfirm = true
|
||||
} label: {
|
||||
|
|
@ -134,11 +134,11 @@ struct EnvironmentMetricsLog: View {
|
|||
Button("Delete all environment metrics?", role: .destructive) {
|
||||
if clearTelemetry(destNum: node.num, metricsType: 1, context: context) {
|
||||
print("Clear Environment Metrics Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 1)
|
||||
exportString = telemetryToCsvFile(telemetry: node.telemetries!.array as? [TelemetryEntity] ?? [], metricsType: 1)
|
||||
isExporting = true
|
||||
} label: {
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import MapKit
|
|||
import CoreLocation
|
||||
|
||||
struct NodeDetail: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
|
|
@ -29,35 +29,34 @@ struct NodeDetail: View {
|
|||
tileType: "png",
|
||||
canReplaceMapContent: true
|
||||
)
|
||||
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
predicate: NSPredicate(
|
||||
format: "expire == nil || expire >= %@", Date() as NSDate
|
||||
), animation: .none)
|
||||
private var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
|
||||
/// The current weather condition for the city.
|
||||
@State private var condition: WeatherCondition?
|
||||
@State private var temperature: Measurement<UnitTemperature>?
|
||||
@State private var humidity: Int?
|
||||
@State private var symbolName: String = "cloud.fill"
|
||||
|
||||
|
||||
@State private var attributionLink: URL?
|
||||
@State private var attributionLogo: URL?
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
let hwModelString = node.user?.hwModel ?? "UNSET"
|
||||
|
||||
|
||||
NavigationStack {
|
||||
GeometryReader { bounds in
|
||||
VStack {
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
// let mostRecent = node.positions?.lastObject as! PositionEntity
|
||||
ZStack {
|
||||
let annotations = node.positions?.array as! [PositionEntity]
|
||||
let annotations = node.positions?.array as? [PositionEntity] ?? []
|
||||
ZStack {
|
||||
MapViewSwiftUI(onLongPress: { coord in
|
||||
waypointCoordinate = coord
|
||||
|
|
@ -75,12 +74,12 @@ struct NodeDetail: View {
|
|||
centerOnPositionsOnly: true,
|
||||
customMapOverlay: self.customMapOverlay,
|
||||
overlays: self.overlays
|
||||
|
||||
|
||||
)
|
||||
VStack (alignment: .leading) {
|
||||
VStack(alignment: .leading) {
|
||||
Spacer()
|
||||
HStack (alignment: .bottom, spacing: 1) {
|
||||
|
||||
HStack(alignment: .bottom, spacing: 1) {
|
||||
|
||||
Picker("Map Type", selection: $mapType) {
|
||||
ForEach(MeshMapType.allCases) { map in
|
||||
Text(map.description).tag(map.MKMapTypeValue())
|
||||
|
|
@ -92,8 +91,7 @@ struct NodeDetail: View {
|
|||
VStack {
|
||||
Label(temperature?.formatted(.measurement(width: .narrow)) ?? "??", systemImage: symbolName)
|
||||
.font(.caption)
|
||||
|
||||
|
||||
|
||||
Label("\(humidity ?? 0)%", systemImage: "humidity")
|
||||
.font(.caption2)
|
||||
}
|
||||
|
|
@ -122,7 +120,7 @@ struct NodeDetail: View {
|
|||
#endif
|
||||
.gesture(
|
||||
LongPressGesture(minimumDuration: 0.5)
|
||||
.onEnded { value in
|
||||
.onEnded { _ in
|
||||
showingForecast = true
|
||||
}
|
||||
)
|
||||
|
|
@ -137,7 +135,7 @@ struct NodeDetail: View {
|
|||
}
|
||||
.padding([.top], 20)
|
||||
}
|
||||
|
||||
|
||||
ScrollView {
|
||||
Divider()
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
|
|
@ -153,17 +151,17 @@ struct NodeDetail: View {
|
|||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 100, height: 100)
|
||||
.cornerRadius(5)
|
||||
|
||||
|
||||
Text(String(hwModelString))
|
||||
.foregroundColor(.gray)
|
||||
.font(.largeTitle).fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if node.snr > 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
|
||||
Image(systemName: "waveform.path")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
|
|
@ -176,15 +174,15 @@ struct NodeDetail: View {
|
|||
.fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if node.telemetries?.count ?? 0 >= 1 {
|
||||
|
||||
|
||||
let mostRecent = node.telemetries?.lastObject as! TelemetryEntity
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
BatteryGauge(batteryLevel: Double(mostRecent.batteryLevel))
|
||||
if mostRecent.voltage > 0 {
|
||||
|
||||
|
||||
Text(String(format: "%.2f", mostRecent.voltage) + " V")
|
||||
.font(.title)
|
||||
.foregroundColor(.gray)
|
||||
|
|
@ -195,10 +193,10 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
|
||||
Divider()
|
||||
HStack(alignment: .center) {
|
||||
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "person")
|
||||
|
|
@ -207,7 +205,7 @@ struct NodeDetail: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
Text("user").font(.title)+Text(":").font(.title)
|
||||
}
|
||||
Text("!\(String(format:"%02x", node.num))")
|
||||
Text("!\(String(format: "%02x", node.num))")
|
||||
.font(.title).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
|
|
@ -222,28 +220,28 @@ struct NodeDetail: View {
|
|||
Text(String(node.num)).font(.title).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack{
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "globe")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("MAC Address: ").font(.title)
|
||||
|
||||
|
||||
}
|
||||
Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address"))
|
||||
.font(.title)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack{
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("heard.last").font(.title)+Text(":").font(.title)
|
||||
|
||||
|
||||
}
|
||||
DateTimeText(dateTime: node.lastHeard)
|
||||
.font(.title3)
|
||||
|
|
@ -251,11 +249,11 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
Divider()
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
HStack {
|
||||
|
||||
|
||||
VStack(alignment: .center) {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .accentColor)
|
||||
}
|
||||
|
|
@ -270,11 +268,11 @@ struct NodeDetail: View {
|
|||
.font(.callout).fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if node.snr > 0 {
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
|
||||
Image(systemName: "waveform.path")
|
||||
.font(.title)
|
||||
.foregroundColor(.accentColor)
|
||||
|
|
@ -286,14 +284,14 @@ struct NodeDetail: View {
|
|||
.fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if node.telemetries?.count ?? 0 >= 1 {
|
||||
let mostRecent = node.telemetries?.lastObject as! TelemetryEntity
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
BatteryGauge(batteryLevel: Double(mostRecent.batteryLevel))
|
||||
if mostRecent.voltage > 0 {
|
||||
|
||||
|
||||
Text(String(format: "%.2f", mostRecent.voltage) + " V")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
|
|
@ -338,36 +336,36 @@ struct NodeDetail: View {
|
|||
.padding([.bottom], 10)
|
||||
Divider()
|
||||
}
|
||||
|
||||
|
||||
VStack {
|
||||
|
||||
|
||||
if (node.positions?.count ?? 0) > 0 {
|
||||
|
||||
|
||||
NavigationLink {
|
||||
PositionLog(node: node)
|
||||
} label: {
|
||||
|
||||
|
||||
Image(systemName: "building.columns")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
|
||||
Text("Position Log")
|
||||
.font(.title3)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Divider()
|
||||
}
|
||||
|
||||
|
||||
if (node.telemetries?.count ?? 0) > 0 {
|
||||
|
||||
|
||||
NavigationLink {
|
||||
DeviceMetricsLog(node: node)
|
||||
} label: {
|
||||
|
||||
|
||||
Image(systemName: "flipphone")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
|
||||
Text("Device Metrics Log")
|
||||
.font(.title3)
|
||||
}
|
||||
|
|
@ -375,28 +373,28 @@ struct NodeDetail: View {
|
|||
NavigationLink {
|
||||
EnvironmentMetricsLog(node: node)
|
||||
} label: {
|
||||
|
||||
|
||||
Image(systemName: "chart.xyaxis.line")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
|
||||
Text("Environment Metrics Log")
|
||||
.font(.title3)
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
|
||||
if (self.bleManager.connectedPeripheral != nil && node.metadata != nil) {
|
||||
|
||||
|
||||
if self.bleManager.connectedPeripheral != nil && node.metadata != nil {
|
||||
|
||||
HStack {
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
|
||||
if node.metadata?.canShutdown ?? false || hwModelString == "RAK4631" {//node.metadata?.hwModel ?? "UNSET" == "RAK4631" {
|
||||
|
||||
if node.metadata?.canShutdown ?? false || hwModelString == "RAK4631" {// node.metadata?.hwModel ?? "UNSET" == "RAK4631" {
|
||||
|
||||
Button(action: {
|
||||
showingShutdownConfirm = true
|
||||
}) {
|
||||
|
||||
|
||||
Label("Power Off", systemImage: "power")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -414,7 +412,7 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button(action: {
|
||||
showingRebootConfirm = true
|
||||
}) {
|
||||
|
|
@ -448,7 +446,7 @@ struct NodeDetail: View {
|
|||
.controlSize(.mini)
|
||||
}
|
||||
.frame(height: 15)
|
||||
|
||||
|
||||
Link("Other data sources", destination: attributionLink ?? URL(string: "https://weather-data.apple.com/legal-attribution.html")!)
|
||||
}
|
||||
.font(.footnote)
|
||||
|
|
@ -456,7 +454,7 @@ struct NodeDetail: View {
|
|||
}
|
||||
}
|
||||
.edgesIgnoringSafeArea([.leading, .trailing])
|
||||
.sheet(isPresented: $presentingWaypointForm ) {//, onDismiss: didDismissSheet) {
|
||||
.sheet(isPresented: $presentingWaypointForm ) {// , onDismiss: didDismissSheet) {
|
||||
WaypointFormView(coordinate: waypointCoordinate ?? LocationHelper.DefaultLocation, waypointId: editingWaypoint)
|
||||
.presentationDetents([.medium, .large])
|
||||
.presentationDragIndicator(.automatic)
|
||||
|
|
@ -472,41 +470,35 @@ struct NodeDetail: View {
|
|||
.onAppear {
|
||||
self.bleManager.context = context
|
||||
switch meshMapType {
|
||||
case "standard":
|
||||
mapType = .standard
|
||||
break
|
||||
case "mutedStandard":
|
||||
mapType = .mutedStandard
|
||||
break
|
||||
case "hybrid":
|
||||
mapType = .hybrid
|
||||
break
|
||||
case "hybridFlyover":
|
||||
mapType = .hybridFlyover
|
||||
break
|
||||
case "satellite":
|
||||
mapType = .satellite
|
||||
break
|
||||
case "satelliteFlyover":
|
||||
mapType = .satelliteFlyover
|
||||
break
|
||||
default:
|
||||
mapType = .hybridFlyover
|
||||
case "standard":
|
||||
mapType = .standard
|
||||
case "mutedStandard":
|
||||
mapType = .mutedStandard
|
||||
case "hybrid":
|
||||
mapType = .hybrid
|
||||
case "hybridFlyover":
|
||||
mapType = .hybridFlyover
|
||||
case "satellite":
|
||||
mapType = .satellite
|
||||
case "satelliteFlyover":
|
||||
mapType = .satelliteFlyover
|
||||
default:
|
||||
mapType = .hybridFlyover
|
||||
}
|
||||
}
|
||||
.task(id: node.num) {
|
||||
do {
|
||||
|
||||
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
|
||||
|
||||
let mostRecent = node.positions?.lastObject as! PositionEntity
|
||||
|
||||
|
||||
let weather = try await WeatherService.shared.weather(for: mostRecent.nodeLocation!)
|
||||
condition = weather.currentWeather.condition
|
||||
temperature = weather.currentWeather.temperature
|
||||
humidity = Int(weather.currentWeather.humidity * 100)
|
||||
symbolName = weather.currentWeather.symbolName
|
||||
|
||||
|
||||
let attribution = try await WeatherService.shared.attribution
|
||||
attributionLink = attribution.legalPageURL
|
||||
attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ struct NodeList: View {
|
|||
|
||||
private var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
||||
@State private var selection: NodeInfoEntity? = nil // Nothing selected by default.
|
||||
@State private var selection: NodeInfoEntity? // Nothing selected by default.
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
NavigationSplitView {
|
||||
List (nodes, id: \.self, selection: $selection) { node in
|
||||
List(nodes, id: \.self, selection: $selection) { node in
|
||||
if nodes.count == 0 {
|
||||
Text("no.nodes").font(.title)
|
||||
} else {
|
||||
|
|
@ -53,13 +53,13 @@ struct NodeList: View {
|
|||
HStack(alignment: .bottom) {
|
||||
let lastPostion = node.positions!.reversed()[0] as! PositionEntity
|
||||
let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)
|
||||
if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationHelper.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationHelper.DefaultLocation.latitude {
|
||||
if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationHelper.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationHelper.DefaultLocation.latitude {
|
||||
let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude)
|
||||
let metersAway = nodeCoord.distance(from: myCoord)
|
||||
Image(systemName: "lines.measurement.horizontal")
|
||||
.font(.title3)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
|
||||
DistanceText(meters: metersAway).font(.subheadline)
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ struct NodeList: View {
|
|||
}
|
||||
} detail: {
|
||||
if let node = selection {
|
||||
NodeDetail(node:node)
|
||||
NodeDetail(node: node)
|
||||
} else {
|
||||
Text("select.node")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ struct NodeMap: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
|
||||
@AppStorage("meshMapCustomTileServer") var customTileServer: String = "" {
|
||||
didSet {
|
||||
if customTileServer == "" {
|
||||
|
|
@ -32,18 +32,18 @@ struct NodeMap: View {
|
|||
@AppStorage("meshMapType") private var meshMapType = "hybridFlyover"
|
||||
@AppStorage("meshMapUserTrackingMode") private var meshMapUserTrackingMode = 0
|
||||
@AppStorage("meshMapCenteringMode") private var meshMapCenteringMode = 0
|
||||
|
||||
//&& nodePosition != nil
|
||||
|
||||
// && nodePosition != nil
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)],
|
||||
predicate: NSPredicate(format: "time >= %@ && nodePosition != nil", Calendar.current.startOfDay(for: Date()) as NSDate), animation: .none)
|
||||
private var positions: FetchedResults<PositionEntity>
|
||||
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
predicate: NSPredicate(
|
||||
format: "expire == nil || expire >= %@", Date() as NSDate
|
||||
), animation: .none)
|
||||
private var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
|
||||
@State private var mapType: MKMapType = .standard
|
||||
@State private var userTrackingMode: MKUserTrackingMode = .none
|
||||
@State private var mapCenteringMode: CenteringMode = .allAnnotations
|
||||
|
|
@ -56,12 +56,12 @@ struct NodeMap: View {
|
|||
canReplaceMapContent: true
|
||||
)
|
||||
@State private var overlays: [MapViewSwiftUI.Overlay] = []
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
|
||||
|
||||
MapViewSwiftUI(onLongPress: { coord in
|
||||
waypointCoordinate = coord
|
||||
editingWaypoint = 0
|
||||
|
|
@ -98,11 +98,11 @@ struct NodeMap: View {
|
|||
}
|
||||
.ignoresSafeArea(.all, edges: [.top, .leading, .trailing])
|
||||
.frame(maxHeight: .infinity)
|
||||
.sheet(isPresented: $presentingWaypointForm ) {//, onDismiss: didDismissSheet) {
|
||||
.sheet(isPresented: $presentingWaypointForm ) {// , onDismiss: didDismissSheet) {
|
||||
WaypointFormView(coordinate: waypointCoordinate, waypointId: editingWaypoint)
|
||||
.presentationDetents([.medium, .large])
|
||||
.presentationDragIndicator(.automatic)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
.navigationBarItems(leading:
|
||||
|
|
@ -123,27 +123,21 @@ struct NodeMap: View {
|
|||
switch meshMapType {
|
||||
case "standard":
|
||||
mapType = .standard
|
||||
break
|
||||
case "mutedStandard":
|
||||
mapType = .mutedStandard
|
||||
break
|
||||
case "hybrid":
|
||||
mapType = .hybrid
|
||||
break
|
||||
case "hybridFlyover":
|
||||
mapType = .hybridFlyover
|
||||
break
|
||||
case "satellite":
|
||||
mapType = .satellite
|
||||
break
|
||||
case "satelliteFlyover":
|
||||
mapType = .satelliteFlyover
|
||||
break
|
||||
default:
|
||||
mapType = .hybridFlyover
|
||||
}
|
||||
})
|
||||
.onDisappear (perform: {
|
||||
.onDisappear(perform: {
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,25 +7,25 @@
|
|||
import SwiftUI
|
||||
|
||||
struct PositionLog: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
|
||||
@State private var isPresentingClearLogConfirm = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
NavigationStack {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//Add a table for mac and ipad
|
||||
// Add a table for mac and ipad
|
||||
Table(node.positions!.reversed() as! [PositionEntity]) {
|
||||
TableColumn("SeqNo") { position in
|
||||
Text(String(position.seqNo))
|
||||
|
|
@ -55,9 +55,9 @@ struct PositionLog: View {
|
|||
Text(position.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
ScrollView {
|
||||
// Use a grid on iOS as a table only shows a single column
|
||||
let columns = [
|
||||
|
|
@ -68,9 +68,9 @@ struct PositionLog: View {
|
|||
GridItem(spacing: 0)
|
||||
]
|
||||
LazyVGrid(columns: columns, alignment: .leading, spacing: 1) {
|
||||
|
||||
|
||||
GridRow {
|
||||
|
||||
|
||||
Text("Latitude")
|
||||
.font(.caption2)
|
||||
.fontWeight(.bold)
|
||||
|
|
@ -125,20 +125,20 @@ struct PositionLog: View {
|
|||
Button("Delete all positions?", role: .destructive) {
|
||||
if clearPositions(destNum: node.num, context: context) {
|
||||
print("Successfully Cleared Position Log")
|
||||
|
||||
|
||||
} else {
|
||||
print("Clear Position Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
|
||||
exportString = PositionToCsvFile(positions: node.positions!.array as! [PositionEntity])
|
||||
|
||||
exportString = positionToCsvFile(positions: node.positions!.array as? [PositionEntity] ?? [])
|
||||
isExporting = true
|
||||
|
||||
|
||||
} label: {
|
||||
|
||||
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -154,12 +154,12 @@ struct PositionLog: View {
|
|||
onCompletion: { result in
|
||||
|
||||
if case .success = result {
|
||||
|
||||
|
||||
print("Position log download succeeded.")
|
||||
self.isExporting = false
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
print("Position log download failed: \(result).")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@ import SwiftUI
|
|||
import StoreKit
|
||||
|
||||
struct AboutMeshtastic: View {
|
||||
|
||||
|
||||
let locale = Locale.current
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack{
|
||||
|
||||
|
||||
VStack {
|
||||
|
||||
List {
|
||||
Section(header: Text("What is Meshtastic?")) {
|
||||
Text("An open source, off-grid, decentralized, mesh network built to run on affordable, low-power devices.")
|
||||
.font(.title3)
|
||||
|
||||
|
||||
}
|
||||
Section(header: Text("Apple Apps")) {
|
||||
Button("Review the app") {
|
||||
|
|
|
|||
|
|
@ -14,38 +14,38 @@ import MapKit
|
|||
import CoreLocation
|
||||
|
||||
struct AdminMessageList: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
||||
var user: UserEntity?
|
||||
|
||||
|
||||
var body: some View {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmssa", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mm:ss a")
|
||||
List {
|
||||
if user != nil {
|
||||
|
||||
ForEach ( user!.adminMessageList.reversed() ) { am in
|
||||
|
||||
VStack (alignment: .leading) {
|
||||
|
||||
|
||||
ForEach( user!.adminMessageList.reversed() ) { am in
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text("\(am.adminDescription ?? NSLocalizedString("unknown", comment: "Unknown"))")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Text("Sent \(Date(timeIntervalSince1970: TimeInterval(am.messageTimestamp)).formattedDate(format: dateFormatString))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption2)
|
||||
|
||||
HStack (spacing: 0) {
|
||||
|
||||
HStack(spacing: 0) {
|
||||
let ackErrorVal = RoutingError(rawValue: Int(am.ackError))
|
||||
|
||||
|
||||
if am.ackTimestamp > 0 {
|
||||
Text(ackErrorVal?.display ?? "Empty Ack Error")
|
||||
.foregroundColor(am.receivedACK ? .gray : .red)
|
||||
.font(.caption2)
|
||||
}
|
||||
|
||||
|
||||
if am.receivedACK && am.ackTimestamp > 0 {
|
||||
Text(" \(Date(timeIntervalSince1970: TimeInterval(am.ackTimestamp)).formattedDate(format: "h:mm:ss a"))")
|
||||
.foregroundColor(.gray)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct AppSettings: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
|
||||
@State private var isPresentingCoreDataResetConfirm = false
|
||||
@State private var preferredDeviceConnected = false
|
||||
|
||||
|
|
@ -28,32 +28,32 @@ struct AppSettings: View {
|
|||
.listRowSeparator(.visible)
|
||||
}
|
||||
Section(header: Text("options")) {
|
||||
|
||||
|
||||
Picker("keyboard.type", selection: $userSettings.keyboardType) {
|
||||
ForEach(KeyboardType.allCases) { kb in
|
||||
Text(kb.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Section(header: Text("phone.gps")) {
|
||||
|
||||
|
||||
Toggle(isOn: $userSettings.provideLocation) {
|
||||
|
||||
Label("provide.location", systemImage: "location.circle.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
if userSettings.provideLocation {
|
||||
|
||||
|
||||
Picker("update.interval", selection: $userSettings.provideLocationInterval) {
|
||||
ForEach(LocationUpdateInterval.allCases) { lu in
|
||||
Text(lu.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
Text("phone.gps.interval.description")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
|
|
@ -68,27 +68,27 @@ struct AppSettings: View {
|
|||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
|
||||
Section(header: Text("map options")) {
|
||||
|
||||
|
||||
Picker("map.type", selection: $userSettings.meshMapType) {
|
||||
ForEach(MeshMapType.allCases) { map in
|
||||
Text(map.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
if userSettings.meshMapUserTrackingMode == 0 {
|
||||
|
||||
|
||||
Picker("map.centering", selection: $userSettings.meshMapCenteringMode) {
|
||||
ForEach(CenteringMode.allCases) { cm in
|
||||
Text(cm.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
Toggle(isOn: $userSettings.meshMapRecentering) {
|
||||
|
||||
|
||||
Label("map.recentering", systemImage: "camera.metering.center.weighted")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
|
@ -126,8 +126,8 @@ struct AppSettings: View {
|
|||
.onAppear {
|
||||
self.bleManager.context = context
|
||||
}
|
||||
.onChange(of: userSettings.provideLocation) { newProvideLocation in
|
||||
|
||||
.onChange(of: userSettings.provideLocation) { _ in
|
||||
|
||||
if bleManager.connectedPeripheral != nil {
|
||||
self.bleManager.sendWantConfig()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,14 @@ func generateChannelKey(size: Int) -> String {
|
|||
}
|
||||
|
||||
struct Channels: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
@Environment(\.sizeCategory) var sizeCategory
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State var hasChanges = false
|
||||
@State private var isPresentingEditView = false
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
|
|
@ -35,14 +34,14 @@ struct Channels: View {
|
|||
@State private var channelRole = 0
|
||||
@State private var uplink = false
|
||||
@State private var downlink = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
NavigationStack {
|
||||
List {
|
||||
if node != nil && node?.myInfo != nil {
|
||||
ForEach(node!.myInfo!.channels?.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in
|
||||
Button(action: {
|
||||
Button(action: {
|
||||
channelIndex = channel.index
|
||||
channelRole = Int(channel.role)
|
||||
channelKey = channel.psk?.base64EncodedString() ?? ""
|
||||
|
|
@ -87,7 +86,7 @@ struct Channels: View {
|
|||
}
|
||||
}
|
||||
if node?.myInfo?.channels?.array.count ?? 0 < 8 && node != nil {
|
||||
|
||||
|
||||
Button {
|
||||
let key = generateChannelKey(size: 32)
|
||||
channelName = ""
|
||||
|
|
@ -98,7 +97,7 @@ struct Channels: View {
|
|||
downlink = false
|
||||
hasChanges = false
|
||||
isPresentingEditView = true
|
||||
|
||||
|
||||
} label: {
|
||||
Label("Add Channel", systemImage: "plus.square")
|
||||
}
|
||||
|
|
@ -107,7 +106,7 @@ struct Channels: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.sheet(isPresented: $isPresentingEditView) {
|
||||
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
Text("channel")
|
||||
.font(.largeTitle)
|
||||
|
|
@ -125,7 +124,7 @@ struct Channels: View {
|
|||
.keyboardType(.alphabet)
|
||||
.foregroundColor(Color.gray)
|
||||
.disabled(channelRole == 1 && channelName.count > 0)
|
||||
.onChange(of: channelName, perform: { value in
|
||||
.onChange(of: channelName, perform: { _ in
|
||||
channelName = channelName.replacing(" ", with: "")
|
||||
let totalBytes = channelName.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
|
|
@ -165,23 +164,23 @@ struct Channels: View {
|
|||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.small)
|
||||
}
|
||||
HStack (alignment: .top) {
|
||||
HStack(alignment: .top) {
|
||||
Text("Key")
|
||||
Spacer()
|
||||
TextField (
|
||||
TextField(
|
||||
"",
|
||||
text: $channelKey,
|
||||
axis: .vertical
|
||||
)
|
||||
.foregroundColor(Color.gray)
|
||||
.disabled(true)
|
||||
|
||||
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
Picker("Channel Role", selection: $channelRole) {
|
||||
if channelRole == 1 {
|
||||
Text("Primary").tag(1)
|
||||
} else{
|
||||
} else {
|
||||
Text("Disabled").tag(0)
|
||||
Text("Secondary").tag(2)
|
||||
}
|
||||
|
|
@ -193,13 +192,13 @@ struct Channels: View {
|
|||
Toggle("Downlink Enabled", isOn: $downlink)
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
//.onSubmit {
|
||||
//validate(name: channelName)
|
||||
//}
|
||||
.onChange(of: channelName) { newName in
|
||||
// .onSubmit {
|
||||
// validate(name: channelName)
|
||||
// }
|
||||
.onChange(of: channelName) { _ in
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: channelKeySize) { newKeySize in
|
||||
.onChange(of: channelKeySize) { _ in
|
||||
if channelKeySize == -1 {
|
||||
channelKey = "AQ=="
|
||||
} else {
|
||||
|
|
@ -208,16 +207,16 @@ struct Channels: View {
|
|||
}
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: channelKey) { newKey in
|
||||
.onChange(of: channelKey) { _ in
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: channelRole) { newRole in
|
||||
.onChange(of: channelRole) { _ in
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: uplink) { newUplink in
|
||||
.onChange(of: uplink) { _ in
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: downlink) { newDownlink in
|
||||
.onChange(of: downlink) { _ in
|
||||
hasChanges = true
|
||||
}
|
||||
HStack {
|
||||
|
|
@ -231,10 +230,11 @@ struct Channels: View {
|
|||
channel.settings.psk = Data(base64Encoded: channelKey) ?? Data()
|
||||
channel.settings.uplinkEnabled = uplink
|
||||
channel.settings.downlinkEnabled = downlink
|
||||
|
||||
} else {
|
||||
if channelIndex <= node!.myInfo!.channels?.count ?? 0 {
|
||||
let channelEntity = node!.myInfo!.channels?[Int(channelIndex)] as! ChannelEntity
|
||||
guard let channelEntity = node!.myInfo!.channels?[Int(channelIndex)] as? ChannelEntity else {
|
||||
return
|
||||
}
|
||||
context.delete(channelEntity)
|
||||
do {
|
||||
try context.save()
|
||||
|
|
@ -246,14 +246,14 @@ struct Channels: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let adminMessageId = bleManager.saveChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!)
|
||||
|
||||
|
||||
if adminMessageId > 0 {
|
||||
self.isPresentingEditView = false
|
||||
channelName = ""
|
||||
hasChanges = false
|
||||
bleManager.getChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!)
|
||||
_ = bleManager.getChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!)
|
||||
}
|
||||
} label: {
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
|
|
|
|||
|
|
@ -8,13 +8,10 @@
|
|||
import SwiftUI
|
||||
|
||||
struct BluetoothConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
@State var enabled = true
|
||||
|
|
@ -22,43 +19,35 @@ struct BluetoothConfig: View {
|
|||
@State var fixedPin = "123456"
|
||||
@State var shortPin = false
|
||||
var pinLength: Int = 6
|
||||
|
||||
let numberFormatter: NumberFormatter = {
|
||||
|
||||
let formatter = NumberFormatter()
|
||||
formatter.numberStyle = .none
|
||||
|
||||
return formatter
|
||||
}()
|
||||
|
||||
var body: some View {
|
||||
|
||||
Form {
|
||||
Section(header: Text("options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
Label("enabled", systemImage: "antenna.radiowaves.left.and.right")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Picker("bluetooth.pairingmode", selection: $mode ) {
|
||||
ForEach(BluetoothModes.allCases) { bm in
|
||||
Text(bm.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
if mode == 1 {
|
||||
HStack {
|
||||
Label("bluetooth.mode.fixedpin", systemImage: "wallet.pass")
|
||||
TextField("bluetooth.mode.fixedpin", text: $fixedPin)
|
||||
.foregroundColor(.gray)
|
||||
.onChange(of: fixedPin, perform: { value in
|
||||
.onChange(of: fixedPin, perform: { _ in
|
||||
// Don't let the first character be 0 because it will get stripped when saving a UInt32
|
||||
if fixedPin.first == "0" {
|
||||
fixedPin = fixedPin.replacing("0", with: "")
|
||||
}
|
||||
//Require that pin is no more than 6 numbers and no less than 6 numbers
|
||||
// Require that pin is no more than 6 numbers and no less than 6 numbers
|
||||
if fixedPin.utf8.count == pinLength {
|
||||
shortPin = false
|
||||
} else if fixedPin.utf8.count > pinLength {
|
||||
|
|
@ -80,7 +69,6 @@ struct BluetoothConfig: View {
|
|||
}
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.bluetoothConfig == nil)
|
||||
|
||||
Button {
|
||||
isPresentingSaveConfirm = true
|
||||
} label: {
|
||||
|
|
@ -128,12 +116,11 @@ struct BluetoothConfig: View {
|
|||
self.mode = Int(node?.bluetoothConfig?.mode ?? 0)
|
||||
self.fixedPin = String(node?.bluetoothConfig?.fixedPin ?? 123456)
|
||||
self.hasChanges = false
|
||||
|
||||
// Need to request a BluetoothConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.bluetoothConfig == nil {
|
||||
print("empty bluetooth config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestBluetoothConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,32 +7,33 @@
|
|||
import SwiftUI
|
||||
|
||||
struct DeviceConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingNodeDBResetConfirm = false
|
||||
@State private var isPresentingFactoryResetConfirm = false
|
||||
@State private var isPresentingSaveConfirm = false
|
||||
@State var hasChanges = false
|
||||
|
||||
|
||||
@State var deviceRole = 0
|
||||
@State var buzzerGPIO = 0
|
||||
@State var buttonGPIO = 0
|
||||
@State var serialEnabled = true
|
||||
@State var debugLogEnabled = false
|
||||
|
||||
@State var rebroadcastMode = 0
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
|
||||
Form {
|
||||
|
||||
|
||||
Section(header: Text("options")) {
|
||||
|
||||
|
||||
Picker("Device Role", selection: $deviceRole ) {
|
||||
ForEach(DeviceRoles.allCases) { dr in
|
||||
Text(dr.name)
|
||||
|
|
@ -43,25 +44,36 @@ struct DeviceConfig: View {
|
|||
Text(DeviceRoles(rawValue: deviceRole)?.description ?? "")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
Picker("Rebroadcast Mode", selection: $rebroadcastMode ) {
|
||||
ForEach(RebroadcastModes.allCases) { rm in
|
||||
Text(rm.name)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
.padding(.top, 10)
|
||||
Text(RebroadcastModes(rawValue: rebroadcastMode)?.description ?? "")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
|
||||
Section(header: Text("Debug")) {
|
||||
|
||||
|
||||
Toggle(isOn: $serialEnabled) {
|
||||
|
||||
Label("Serial Console", systemImage: "terminal")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
Toggle(isOn: $debugLogEnabled) {
|
||||
|
||||
Label("Debug Log", systemImage: "ant.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
|
||||
|
||||
Section(header: Text("GPIO")) {
|
||||
|
||||
|
||||
Picker("Button GPIO", selection: $buttonGPIO) {
|
||||
ForEach(0..<40) {
|
||||
if $0 == 0 {
|
||||
|
|
@ -83,14 +95,14 @@ struct DeviceConfig: View {
|
|||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.deviceConfig == nil)
|
||||
|
||||
|
||||
// Only show these buttons for the BLE connected node
|
||||
if bleManager.connectedPeripheral != nil && node?.num ?? -1 == bleManager.connectedPeripheral.num {
|
||||
HStack {
|
||||
|
||||
|
||||
Button("Reset NodeDB", role: .destructive) {
|
||||
isPresentingNodeDBResetConfirm = true
|
||||
}
|
||||
|
|
@ -105,7 +117,7 @@ struct DeviceConfig: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Erase all device and app data?", role: .destructive) {
|
||||
|
||||
|
||||
if bleManager.sendNodeDBReset(fromUser: node!.user!, toUser: node!.user!) {
|
||||
bleManager.disconnectPeripheral()
|
||||
clearCoreDataDatabase(context: context)
|
||||
|
|
@ -128,23 +140,23 @@ struct DeviceConfig: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Factory reset your device and app? ", role: .destructive) {
|
||||
|
||||
|
||||
if bleManager.sendFactoryReset(fromUser: node!.user!, toUser: node!.user!) {
|
||||
bleManager.disconnectPeripheral()
|
||||
clearCoreDataDatabase(context: context)
|
||||
} else {
|
||||
print("Factory Reset Failed")
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
|
||||
|
||||
Button {
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
|
||||
} label: {
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
|
|
@ -154,7 +166,7 @@ struct DeviceConfig: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
|
||||
"are.you.sure",
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
|
|
@ -170,7 +182,7 @@ struct DeviceConfig: View {
|
|||
dc.debugLogEnabled = debugLogEnabled
|
||||
dc.buttonGpio = UInt32(buttonGPIO)
|
||||
dc.buzzerGpio = UInt32(buzzerGPIO)
|
||||
|
||||
|
||||
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -200,48 +212,48 @@ struct DeviceConfig: View {
|
|||
self.buttonGPIO = Int(node?.deviceConfig?.buttonGpio ?? 0)
|
||||
self.buzzerGPIO = Int(node?.deviceConfig?.buzzerGpio ?? 0)
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a LoRaConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.deviceConfig == nil {
|
||||
print("empty device config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestDeviceConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceRole) { newRole in
|
||||
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
|
||||
if newRole != node!.deviceConfig!.role { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: serialEnabled) { newSerial in
|
||||
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
|
||||
if newSerial != node!.deviceConfig!.serialEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: debugLogEnabled) { newDebugLog in
|
||||
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
|
||||
if newDebugLog != node!.deviceConfig!.debugLogEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: buttonGPIO) { newButtonGPIO in
|
||||
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
|
||||
if newButtonGPIO != node!.deviceConfig!.buttonGpio { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: buzzerGPIO) { newBuzzerGPIO in
|
||||
|
||||
|
||||
if node != nil && node!.deviceConfig != nil {
|
||||
|
||||
|
||||
if newBuzzerGPIO != node!.deviceConfig!.buttonGpio { hasChanges = true }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
import SwiftUI
|
||||
|
||||
struct DisplayConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
|
||||
|
|
@ -25,12 +25,12 @@ struct DisplayConfig: View {
|
|||
@State var flipScreen = false
|
||||
@State var oledType = 0
|
||||
@State var displayMode = 0
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
Form {
|
||||
Section(header: Text("Device Screen")) {
|
||||
|
||||
|
||||
Picker("Display Mode", selection: $displayMode ) {
|
||||
ForEach(DisplayModes.allCases) { dm in
|
||||
Text(dm.description)
|
||||
|
|
@ -39,7 +39,7 @@ struct DisplayConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Override automatic OLED screen detection.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Toggle(isOn: $compassNorthTop) {
|
||||
|
||||
Label("Always point north", systemImage: "location.north.circle")
|
||||
|
|
@ -47,7 +47,7 @@ struct DisplayConfig: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("The compass heading on the screen outside of the circle will always point north.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Toggle(isOn: $flipScreen) {
|
||||
|
||||
Label("Flip Screen", systemImage: "pip.swap")
|
||||
|
|
@ -55,7 +55,7 @@ struct DisplayConfig: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("Flip screen vertically")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Picker("OLED Type", selection: $oledType ) {
|
||||
ForEach(OledTypes.allCases) { ot in
|
||||
Text(ot.description)
|
||||
|
|
@ -64,7 +64,7 @@ struct DisplayConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Override automatic OLED screen detection.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
}
|
||||
Section(header: Text("Timing & Format")) {
|
||||
Picker("Screen on for", selection: $screenOnSeconds ) {
|
||||
|
|
@ -75,7 +75,7 @@ struct DisplayConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("How long the screen remains on after the user button is pressed or messages are received.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Picker("Carousel Interval", selection: $screenCarouselInterval ) {
|
||||
ForEach(ScreenCarouselIntervals.allCases) { sci in
|
||||
Text(sci.description)
|
||||
|
|
@ -84,27 +84,27 @@ struct DisplayConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Picker("GPS Format", selection: $gpsFormat ) {
|
||||
ForEach(GpsFormats.allCases) { lu in
|
||||
Text(lu.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
Text("The format used to display GPS coordinates on the device screen.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.displayConfig == nil)
|
||||
|
||||
|
||||
Button {
|
||||
|
||||
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
|
||||
} label: {
|
||||
|
||||
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
|
|
@ -129,10 +129,10 @@ struct DisplayConfig: View {
|
|||
dc.flipScreen = flipScreen
|
||||
dc.oled = OledTypes(rawValue: oledType)!.protoEnumValue()
|
||||
dc.displaymode = DisplayModes(rawValue: displayMode)!.protoEnumValue()
|
||||
|
||||
|
||||
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
|
||||
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
|
@ -159,12 +159,12 @@ struct DisplayConfig: View {
|
|||
self.oledType = Int(node?.displayConfig?.oledType ?? 0)
|
||||
self.displayMode = Int(node?.displayConfig?.displayMode ?? 0)
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a LoRaConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.displayConfig == nil {
|
||||
print("empty display config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestDisplayConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,62 +6,148 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
|
||||
struct LoRaConfig: View {
|
||||
|
||||
|
||||
enum Field: Hashable {
|
||||
case channelNum
|
||||
}
|
||||
|
||||
let formatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
formatter.numberStyle = .decimal
|
||||
formatter.groupingSeparator = ""
|
||||
return formatter
|
||||
}()
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
@FocusState var focusedField: Field?
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State var isPresentingSaveConfirm = false
|
||||
@State var hasChanges = false
|
||||
@State var region = 0
|
||||
@State var modemPreset = 0
|
||||
@State var hopLimit = 0
|
||||
@State var txPower = 0
|
||||
@State var modemPreset = 0
|
||||
@State var hopLimit = 0
|
||||
@State var txPower = 0
|
||||
@State var txEnabled = true
|
||||
@State var usePreset = true
|
||||
|
||||
@State var channelNum = 0
|
||||
@State var bandwidth = 0
|
||||
@State var spreadFactor = 0
|
||||
@State var codingRate = 0
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
Form {
|
||||
Section(header: Text("Region")) {
|
||||
Section(header: Text("Options")) {
|
||||
|
||||
Picker("Region", selection: $region ) {
|
||||
ForEach(RegionCodes.allCases) { r in
|
||||
Text(r.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
.fixedSize()
|
||||
|
||||
Text("The region where you will be using your radios.")
|
||||
.font(.caption)
|
||||
}
|
||||
Section(header: Text("Modem")) {
|
||||
Picker("Presets", selection: $modemPreset ) {
|
||||
ForEach(ModemPresets.allCases) { m in
|
||||
Text(m.description)
|
||||
}
|
||||
|
||||
Toggle(isOn: $usePreset) {
|
||||
Label("Use Preset", systemImage: "list.bullet.rectangle")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
if usePreset {
|
||||
Picker("Presets", selection: $modemPreset ) {
|
||||
ForEach(ModemPresets.allCases) { m in
|
||||
Text(m.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
.fixedSize()
|
||||
Text("Available modem presets, default is Long Fast.")
|
||||
.font(.caption)
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Available modem presets, default is Long Fast.")
|
||||
.font(.caption)
|
||||
}
|
||||
Section(header: Text("Mesh Options")) {
|
||||
Picker("Number of hops", selection: $hopLimit) {
|
||||
Text("Please Select")
|
||||
.tag(0)
|
||||
ForEach(HopValues.allCases) { hop in
|
||||
Text(hop.description)
|
||||
Section(header: Text("Advanced")) {
|
||||
|
||||
Toggle(isOn: $txEnabled) {
|
||||
Label("Transmit Enabled", systemImage: "waveform.path")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
if !usePreset {
|
||||
HStack {
|
||||
Picker("Bandwidth", selection: $spreadFactor) {
|
||||
Text("31 kHz")
|
||||
.tag(31)
|
||||
Text("62 kHz")
|
||||
.tag(62)
|
||||
Text("125 kHz")
|
||||
.tag(125)
|
||||
Text("250 kHz")
|
||||
.tag(0)
|
||||
Text("500 kHz")
|
||||
.tag(500)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Picker("Spread Factor", selection: $spreadFactor) {
|
||||
ForEach(7..<13) {
|
||||
Text("\($0)")
|
||||
.tag($0 == 12 ? 0 : $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Picker("Coding Rate", selection: $codingRate) {
|
||||
ForEach(5..<9) {
|
||||
Text("\($0)")
|
||||
.tag($0 == 8 ? 0 : $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Picker("Number of hops", selection: $codingRate) {
|
||||
ForEach(1..<8) {
|
||||
Text("\($0)")
|
||||
.tag($0 == 3 ? 0 : $0)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Sets the maximum number of hops, default is 3. Increasing hops also increases air time utilization and should be used carefully.")
|
||||
.font(.caption)
|
||||
|
||||
HStack {
|
||||
Text("LoRa Channel Number")
|
||||
.fixedSize()
|
||||
TextField("Channel Number", value: $channelNum, formatter: formatter)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .keyboard) {
|
||||
Button("dismiss.keyboard") {
|
||||
focusedField = nil
|
||||
}
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.keyboardType(.decimalPad)
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.focused($focusedField, equals: .channelNum)
|
||||
}
|
||||
Text("A hash of the primary channel's name sets the LoRa channel number, this determines the actual frequency you are transmitting on in the band. To ensure devices with different primary channel names transmit on the same frequency, you must explicitly set the LoRa channel number.")
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.loRaConfig == nil)
|
||||
|
||||
Button {
|
||||
isPresentingSaveConfirm = true
|
||||
} label: {
|
||||
|
|
@ -86,8 +172,12 @@ struct LoRaConfig: View {
|
|||
lc.hopLimit = UInt32(hopLimit)
|
||||
lc.region = RegionCodes(rawValue: region)!.protoEnumValue()
|
||||
lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue()
|
||||
lc.usePreset = true
|
||||
lc.txEnabled = true
|
||||
lc.usePreset = usePreset
|
||||
lc.txEnabled = txEnabled
|
||||
lc.channelNum = UInt32(channelNum)
|
||||
lc.bandwidth = UInt32(bandwidth)
|
||||
lc.codingRate = UInt32(codingRate)
|
||||
lc.spreadFactor = UInt32(spreadFactor)
|
||||
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -103,11 +193,11 @@ struct LoRaConfig: View {
|
|||
}
|
||||
.navigationTitle("lora.config")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
||||
self.bleManager.context = context
|
||||
self.hopLimit = Int(node?.loRaConfig?.hopLimit ?? 3)
|
||||
self.region = Int(node?.loRaConfig?.regionCode ?? 0)
|
||||
|
|
@ -115,13 +205,19 @@ struct LoRaConfig: View {
|
|||
self.modemPreset = Int(node?.loRaConfig?.modemPreset ?? 0)
|
||||
self.txEnabled = node?.loRaConfig?.txEnabled ?? true
|
||||
self.txPower = Int(node?.loRaConfig?.txPower ?? 0)
|
||||
self.channelNum = Int(node?.loRaConfig?.channelNum ?? 0)
|
||||
self.bandwidth = Int(node?.loRaConfig?.bandwidth ?? 0)
|
||||
self.codingRate = Int(node?.loRaConfig?.codingRate ?? 0)
|
||||
self.spreadFactor = Int(node?.loRaConfig?.spreadFactor ?? 0)
|
||||
print("Spreadum: \(self.spreadFactor)")
|
||||
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a LoRaConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.loRaConfig == nil {
|
||||
print("empty lora config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestLoRaConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +227,11 @@ struct LoRaConfig: View {
|
|||
if newRegion != node!.loRaConfig!.regionCode { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: usePreset) { newUsePreset in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newUsePreset != node!.loRaConfig!.usePreset { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: modemPreset) { newModemPreset in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newModemPreset != node!.loRaConfig!.modemPreset { hasChanges = true }
|
||||
|
|
@ -141,5 +242,25 @@ struct LoRaConfig: View {
|
|||
if newHopLimit != node!.loRaConfig!.hopLimit { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: channelNum) { newChannelNum in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newChannelNum != node!.loRaConfig!.channelNum { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: bandwidth) { newBandwidth in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newBandwidth != node!.loRaConfig!.bandwidth { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: codingRate) { newCodingRate in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newCodingRate != node!.loRaConfig!.codingRate { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: spreadFactor) { newSpreadFactor in
|
||||
if node != nil && node!.loRaConfig != nil {
|
||||
if newSpreadFactor != node!.loRaConfig!.spreadFactor { hasChanges = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,14 @@
|
|||
import SwiftUI
|
||||
|
||||
struct CannedMessagesConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
@State var hasMessagesChanges = false
|
||||
@State var configPreset = 0
|
||||
|
||||
@State var enabled = false
|
||||
/// CannedMessageModule will sends a bell character with the messages.
|
||||
@State var sendBell: Bool = false
|
||||
|
|
@ -38,29 +34,22 @@ struct CannedMessagesConfig: View {
|
|||
@State var inputbrokerEventCcw = 0
|
||||
/// Generate input event on Press of this kind.
|
||||
@State var inputbrokerEventPress = 0
|
||||
|
||||
@State var messages = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
|
||||
Form {
|
||||
|
||||
Section(header: Text("options")) {
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
|
||||
Label("enabled", systemImage: "list.bullet.rectangle.fill")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $sendBell) {
|
||||
|
||||
Label("Send Bell", systemImage: "bell")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Picker("Configuration Presets", selection: $configPreset ) {
|
||||
ForEach(ConfigPresets.allCases) { cp in
|
||||
Text(cp.description)
|
||||
|
|
@ -69,26 +58,21 @@ struct CannedMessagesConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
}
|
||||
|
||||
HStack {
|
||||
Label("Messages", systemImage: "message.fill")
|
||||
TextField("Messages separate with |", text: $messages, axis: .vertical)
|
||||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: messages, perform: { value in
|
||||
.onChange(of: messages, perform: { _ in
|
||||
|
||||
let totalBytes = messages.utf8.count
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 198 {
|
||||
|
||||
let firstNBytes = Data(messages.utf8.prefix(198))
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
messages = maxBytesString
|
||||
}
|
||||
|
|
@ -98,35 +82,27 @@ struct CannedMessagesConfig: View {
|
|||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.default)
|
||||
|
||||
Section(header: Text("Control Type")) {
|
||||
|
||||
|
||||
Toggle(isOn: $rotary1Enabled) {
|
||||
|
||||
Label("Rotary 1", systemImage: "dial.min")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.disabled(updown1Enabled)
|
||||
|
||||
Toggle(isOn: $updown1Enabled) {
|
||||
|
||||
Label("Up Down 1", systemImage: "arrow.up.arrow.down")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.disabled(rotary1Enabled)
|
||||
|
||||
}
|
||||
.disabled(configPreset > 0)
|
||||
Section(header: Text("Inputs")) {
|
||||
|
||||
Picker("Pin A", selection: $inputbrokerPinA) {
|
||||
ForEach(0..<40) {
|
||||
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
||||
Text("Pin \($0)")
|
||||
}
|
||||
}
|
||||
|
|
@ -134,14 +110,11 @@ struct CannedMessagesConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("GPIO pin for rotary encoder A port.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("Pin B", selection: $inputbrokerPinB) {
|
||||
ForEach(0..<40) {
|
||||
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
||||
Text("Pin \($0)")
|
||||
}
|
||||
}
|
||||
|
|
@ -149,14 +122,11 @@ struct CannedMessagesConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("GPIO pin for rotary encoder B port.")
|
||||
.font(.caption)
|
||||
|
||||
Picker("Press Pin", selection: $inputbrokerPinPress) {
|
||||
ForEach(0..<40) {
|
||||
|
||||
if $0 == 0 {
|
||||
Text("unset")
|
||||
} else {
|
||||
|
||||
Text("Pin \($0)")
|
||||
}
|
||||
}
|
||||
|
|
@ -164,12 +134,9 @@ struct CannedMessagesConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("GPIO pin for rotary encoder Press port.")
|
||||
.font(.caption)
|
||||
|
||||
}
|
||||
.disabled(configPreset > 0)
|
||||
|
||||
Section(header: Text("Key Mapping")) {
|
||||
|
||||
Picker("Clockwise Rotary Event", selection: $inputbrokerEventCw ) {
|
||||
ForEach(InputEventChars.allCases) { iec in
|
||||
Text(iec.description)
|
||||
|
|
@ -178,7 +145,6 @@ struct CannedMessagesConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Picker("Counter Clockwise Rotary Event", selection: $inputbrokerEventCcw ) {
|
||||
ForEach(InputEventChars.allCases) { iec in
|
||||
Text(iec.description)
|
||||
|
|
@ -187,7 +153,6 @@ struct CannedMessagesConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Picker("Encoder Press Event", selection: $inputbrokerEventPress ) {
|
||||
ForEach(InputEventChars.allCases) { iec in
|
||||
Text(iec.description)
|
||||
|
|
@ -201,10 +166,8 @@ struct CannedMessagesConfig: View {
|
|||
}
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.cannedMessageConfig == nil)
|
||||
|
||||
Button {
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
} label: {
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
|
|
@ -287,20 +250,20 @@ struct CannedMessagesConfig: View {
|
|||
self.messages = node?.cannedMessageConfig?.messages ?? ""
|
||||
self.hasChanges = false
|
||||
self.hasMessagesChanges = false
|
||||
|
||||
|
||||
// Need to request a CannedMessagesModuleConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.cannedMessageConfig == nil {
|
||||
print("empty canned messages module config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: configPreset) { newPreset in
|
||||
|
||||
|
||||
if newPreset == 1 {
|
||||
|
||||
|
||||
// RAK Rotary Encoder
|
||||
updown1Enabled = true
|
||||
rotary1Enabled = false
|
||||
|
|
@ -310,9 +273,9 @@ struct CannedMessagesConfig: View {
|
|||
inputbrokerEventCw = InputEventChars.down.rawValue
|
||||
inputbrokerEventCcw = InputEventChars.up.rawValue
|
||||
inputbrokerEventPress = InputEventChars.select.rawValue
|
||||
|
||||
|
||||
} else if newPreset == 2 {
|
||||
|
||||
|
||||
// CardKB / RAK Keypad
|
||||
updown1Enabled = false
|
||||
rotary1Enabled = false
|
||||
|
|
@ -323,7 +286,7 @@ struct CannedMessagesConfig: View {
|
|||
inputbrokerEventCcw = InputEventChars.none.rawValue
|
||||
inputbrokerEventPress = InputEventChars.none.rawValue
|
||||
}
|
||||
|
||||
|
||||
hasChanges = true
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ExternalNotificationConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
@State var enabled = false
|
||||
|
|
@ -30,9 +30,9 @@ struct ExternalNotificationConfig: View {
|
|||
@State var outputVibra = 0
|
||||
@State var outputMilliseconds = 0
|
||||
@State var nagTimeout = 0
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
Form {
|
||||
Section(header: Text("options")) {
|
||||
Toggle(isOn: $enabled) {
|
||||
|
|
@ -58,8 +58,7 @@ struct ExternalNotificationConfig: View {
|
|||
Section(header: Text("Primary GPIO")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
.textCase(.uppercase))
|
||||
{
|
||||
.textCase(.uppercase)) {
|
||||
Toggle(isOn: $active) {
|
||||
Label("Active", systemImage: "togglepower")
|
||||
}
|
||||
|
|
@ -93,12 +92,11 @@ struct ExternalNotificationConfig: View {
|
|||
Text("Specifies how long the monitored GPIO should output.")
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
|
||||
Section(header: Text("Optional GPIO")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
.textCase(.uppercase))
|
||||
{
|
||||
.textCase(.uppercase)) {
|
||||
Toggle(isOn: $alertBellBuzzer) {
|
||||
Label("Alert GPIO buzzer when receiving a bell", systemImage: "bell")
|
||||
}
|
||||
|
|
@ -174,7 +172,7 @@ struct ExternalNotificationConfig: View {
|
|||
enc.outputMs = UInt32(outputMilliseconds)
|
||||
enc.usePwm = usePWM
|
||||
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0{
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
hasChanges = false
|
||||
|
|
@ -208,12 +206,12 @@ struct ExternalNotificationConfig: View {
|
|||
self.nagTimeout = Int(node?.externalNotificationConfig?.nagTimeout ?? 0)
|
||||
self.usePWM = node?.externalNotificationConfig?.usePWM ?? false
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a TelemetryModuleConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.externalNotificationConfig == nil {
|
||||
print("empty external notification module config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import SwiftUI
|
||||
|
||||
struct MQTTConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
|
@ -20,9 +20,9 @@ struct MQTTConfig: View {
|
|||
@State var password = ""
|
||||
@State var encryptionEnabled = false
|
||||
@State var jsonEnabled = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
Form {
|
||||
Section(header: Text("options")) {
|
||||
Toggle(isOn: $enabled) {
|
||||
|
|
@ -30,7 +30,7 @@ struct MQTTConfig: View {
|
|||
Label("enabled", systemImage: "dot.radiowaves.right")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
Toggle(isOn: $encryptionEnabled) {
|
||||
|
||||
Label("Encryption Enabled", systemImage: "lock.icloud")
|
||||
|
|
@ -50,7 +50,7 @@ struct MQTTConfig: View {
|
|||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: address, perform: { value in
|
||||
.onChange(of: address, perform: { _ in
|
||||
let totalBytes = address.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 30 {
|
||||
|
|
@ -66,24 +66,24 @@ struct MQTTConfig: View {
|
|||
.keyboardType(.default)
|
||||
}
|
||||
.autocorrectionDisabled()
|
||||
|
||||
|
||||
HStack {
|
||||
Label("mqtt.username", systemImage: "person.text.rectangle")
|
||||
TextField("mqtt.username", text: $username)
|
||||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: username, perform: { value in
|
||||
.onChange(of: username, perform: { _ in
|
||||
|
||||
let totalBytes = username.utf8.count
|
||||
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 62 {
|
||||
|
||||
let firstNBytes = Data(username.utf8.prefix(62))
|
||||
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
username = maxBytesString
|
||||
}
|
||||
|
|
@ -100,17 +100,17 @@ struct MQTTConfig: View {
|
|||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: password, perform: { value in
|
||||
.onChange(of: password, perform: { _ in
|
||||
|
||||
let totalBytes = password.utf8.count
|
||||
|
||||
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 62 {
|
||||
|
||||
let firstNBytes = Data(password.utf8.prefix(62))
|
||||
|
||||
|
||||
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
||||
|
||||
|
||||
// Set the shortName back to the last place where it was the right size
|
||||
password = maxBytesString
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ struct MQTTConfig: View {
|
|||
}
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.mqttConfig == nil)
|
||||
|
||||
|
||||
Button {
|
||||
isPresentingSaveConfirm = true
|
||||
} label: {
|
||||
|
|
@ -182,12 +182,12 @@ struct MQTTConfig: View {
|
|||
self.encryptionEnabled = (node?.mqttConfig?.encryptionEnabled ?? false)
|
||||
self.jsonEnabled = (node?.mqttConfig?.jsonEnabled ?? false)
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a TelemetryModuleConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.telemetryConfig == nil {
|
||||
print("empty mqtt module config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestMqttModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,19 @@
|
|||
import SwiftUI
|
||||
|
||||
struct RangeTestConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
@State var enabled = false
|
||||
@State var sender = 0
|
||||
@State var save = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Form {
|
||||
|
|
@ -65,7 +65,7 @@ struct RangeTestConfig: View {
|
|||
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
|
||||
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
|
||||
Button(buttonText) {
|
||||
|
||||
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil {
|
||||
var rtc = ModuleConfig.RangeTestConfig()
|
||||
|
|
@ -96,12 +96,12 @@ struct RangeTestConfig: View {
|
|||
self.save = node?.rangeTestConfig?.save ?? false
|
||||
self.sender = Int(node?.rangeTestConfig?.sender ?? 0)
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a RangeTestModule Config from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.rangeTestConfig == nil {
|
||||
print("empty range test module config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@
|
|||
import SwiftUI
|
||||
|
||||
struct SerialConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
|
||||
|
||||
@State var enabled = false
|
||||
@State var echo = false
|
||||
@State var rxd = 0
|
||||
|
|
@ -24,21 +24,21 @@ struct SerialConfig: View {
|
|||
@State var baudRate = 0
|
||||
@State var timeout = 0
|
||||
@State var mode = 0
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
|
||||
Form {
|
||||
|
||||
|
||||
Section(header: Text("options")) {
|
||||
|
||||
|
||||
Toggle(isOn: $enabled) {
|
||||
|
||||
Label("enabled", systemImage: "terminal")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
Toggle(isOn: $echo) {
|
||||
|
||||
Label("echo", systemImage: "repeat")
|
||||
|
|
@ -46,14 +46,14 @@ struct SerialConfig: View {
|
|||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("If set, any packets you send will be echoed back to your device.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Picker("Baud", selection: $baudRate ) {
|
||||
ForEach(SerialBaudRates.allCases) { sbr in
|
||||
Text(sbr.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
Picker("timeout", selection: $timeout ) {
|
||||
ForEach(SerialTimeoutIntervals.allCases) { sti in
|
||||
Text(sti.description)
|
||||
|
|
@ -62,7 +62,7 @@ struct SerialConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("The amount of time to wait before we consider your packet as done.")
|
||||
.font(.caption)
|
||||
|
||||
|
||||
Picker("mode", selection: $mode ) {
|
||||
ForEach(SerialModeTypes.allCases) { smt in
|
||||
Text(smt.description)
|
||||
|
|
@ -71,7 +71,7 @@ struct SerialConfig: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
}
|
||||
Section(header: Text("GPIO")) {
|
||||
|
||||
|
||||
Picker("Receive data (rxd) GPIO pin", selection: $rxd) {
|
||||
ForEach(0..<40) {
|
||||
if $0 == 0 {
|
||||
|
|
@ -98,13 +98,13 @@ struct SerialConfig: View {
|
|||
}
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.serialConfig == nil)
|
||||
|
||||
|
||||
Button {
|
||||
|
||||
|
||||
isPresentingSaveConfirm = true
|
||||
|
||||
|
||||
} label: {
|
||||
|
||||
|
||||
Label("save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
|
|
@ -113,7 +113,7 @@ struct SerialConfig: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
.confirmationDialog(
|
||||
|
||||
|
||||
"are.you.sure",
|
||||
isPresented: $isPresentingSaveConfirm,
|
||||
titleVisibility: .visible
|
||||
|
|
@ -131,9 +131,9 @@ struct SerialConfig: View {
|
|||
sc.baud = SerialBaudRates(rawValue: baudRate)!.protoEnumValue()
|
||||
sc.timeout = UInt32(timeout)
|
||||
sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue()
|
||||
|
||||
|
||||
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
|
||||
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -153,7 +153,7 @@ struct SerialConfig: View {
|
|||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
||||
self.bleManager.context = context
|
||||
self.enabled = node?.serialConfig?.enabled ?? false
|
||||
self.echo = node?.serialConfig?.echo ?? false
|
||||
|
|
@ -163,63 +163,63 @@ struct SerialConfig: View {
|
|||
self.timeout = Int(node?.serialConfig?.timeout ?? 0)
|
||||
self.mode = Int(node?.serialConfig?.mode ?? 0)
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a SerialModuleConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.serialConfig == nil {
|
||||
print("empty serial module config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestSerialModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
|
||||
if newEnabled != node!.serialConfig!.enabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: echo) { newEcho in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
|
||||
if newEcho != node!.serialConfig!.echo { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: rxd) { newRxd in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
|
||||
if newRxd != node!.serialConfig!.rxd { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: txd) { newTxd in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
if newTxd != node!.serialConfig!.txd { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: baudRate) { newBaud in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
|
||||
if newBaud != node!.serialConfig!.baudRate { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: timeout) { newTimeout in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
|
||||
if newTimeout != node!.serialConfig!.timeout { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: mode) { newMode in
|
||||
|
||||
|
||||
if node != nil && node!.serialConfig != nil {
|
||||
|
||||
|
||||
if newMode != node!.serialConfig!.mode { hasChanges = true }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@
|
|||
import SwiftUI
|
||||
|
||||
struct TelemetryConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
@State var deviceUpdateInterval = 0
|
||||
|
|
@ -21,9 +21,9 @@ struct TelemetryConfig: View {
|
|||
@State var environmentMeasurementEnabled = false
|
||||
@State var environmentScreenEnabled = false
|
||||
@State var environmentDisplayFahrenheit = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
Form {
|
||||
Section(header: Text("update.interval")) {
|
||||
|
|
@ -88,7 +88,7 @@ struct TelemetryConfig: View {
|
|||
tc.environmentMeasurementEnabled = environmentMeasurementEnabled
|
||||
tc.environmentScreenEnabled = environmentScreenEnabled
|
||||
tc.environmentDisplayFahrenheit = environmentDisplayFahrenheit
|
||||
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!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -114,12 +114,12 @@ struct TelemetryConfig: View {
|
|||
self.environmentScreenEnabled = node?.telemetryConfig?.environmentScreenEnabled ?? false
|
||||
self.environmentDisplayFahrenheit = node?.telemetryConfig?.environmentDisplayFahrenheit ?? false
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a TelemetryModuleConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.telemetryConfig == nil {
|
||||
print("empty telemetry module config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
import SwiftUI
|
||||
|
||||
struct NetworkConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges: Bool = false
|
||||
@State var wifiEnabled = false
|
||||
|
|
@ -24,13 +24,13 @@ struct NetworkConfig: View {
|
|||
@State var ntpServer = ""
|
||||
@State var ethEnabled = false
|
||||
@State var ethMode = 0
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
Form {
|
||||
Section(header: Text("WiFi Options (ESP32 Only)")) {
|
||||
|
||||
|
||||
Toggle(isOn: $wifiEnabled) {
|
||||
Label("enabled", systemImage: "wifi")
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ struct NetworkConfig: View {
|
|||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: wifiSsid, perform: { value in
|
||||
.onChange(of: wifiSsid, perform: { _ in
|
||||
let totalBytes = wifiSsid.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 32 {
|
||||
|
|
@ -51,7 +51,7 @@ struct NetworkConfig: View {
|
|||
wifiSsid = maxBytesString
|
||||
}
|
||||
}
|
||||
hasChanges = true
|
||||
hasChanges = true
|
||||
})
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ struct NetworkConfig: View {
|
|||
.foregroundColor(.gray)
|
||||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.onChange(of: wifiPsk, perform: { value in
|
||||
.onChange(of: wifiPsk, perform: { _ in
|
||||
let totalBytes = wifiPsk.utf8.count
|
||||
// Only mess with the value if it is too big
|
||||
if totalBytes > 63 {
|
||||
|
|
@ -117,8 +117,8 @@ struct NetworkConfig: View {
|
|||
network.wifiSsid = self.wifiSsid
|
||||
network.wifiPsk = self.wifiPsk
|
||||
network.ethEnabled = self.ethEnabled
|
||||
//network.addressMode = Config.NetworkConfig.AddressMode.dhcp
|
||||
|
||||
// network.addressMode = Config.NetworkConfig.AddressMode.dhcp
|
||||
|
||||
let adminMessageId = bleManager.saveNetworkConfig(config: network, fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: node?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
|
|
@ -145,12 +145,12 @@ struct NetworkConfig: View {
|
|||
self.wifiMode = Int(node?.networkConfig?.wifiMode ?? 0)
|
||||
self.ethEnabled = node?.networkConfig?.ethEnabled ?? false
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a NetworkConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.positionConfig == nil {
|
||||
print("empty network config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestNetworkConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
struct PositionFlags: OptionSet
|
||||
{
|
||||
struct PositionFlags: OptionSet {
|
||||
let rawValue: Int
|
||||
|
||||
|
||||
static let Altitude = PositionFlags(rawValue: 1)
|
||||
static let AltitudeMsl = PositionFlags(rawValue: 2)
|
||||
static let GeoidalSeparation = PositionFlags(rawValue: 4)
|
||||
|
|
@ -24,17 +23,17 @@ struct PositionFlags: OptionSet
|
|||
}
|
||||
|
||||
struct PositionConfig: View {
|
||||
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.dismiss) private var goBack
|
||||
|
||||
|
||||
var node: NodeInfoEntity?
|
||||
|
||||
|
||||
@State private var isPresentingSaveConfirm: Bool = false
|
||||
@State var hasChanges = false
|
||||
@State var hasFlagChanges = false
|
||||
|
||||
|
||||
@State var smartPositionEnabled = true
|
||||
@State var deviceGpsEnabled = true
|
||||
@State var fixedPosition = false
|
||||
|
|
@ -42,7 +41,7 @@ struct PositionConfig: View {
|
|||
@State var gpsAttemptTime = 0
|
||||
@State var positionBroadcastSeconds = 0
|
||||
@State var positionFlags = 3
|
||||
|
||||
|
||||
/// Position Flags
|
||||
/// Altitude value - 1
|
||||
@State var includeAltitude = false
|
||||
|
|
@ -68,9 +67,9 @@ struct PositionConfig: View {
|
|||
/// Intended for use with vehicle not walking speeds
|
||||
/// walking speeds are likely to be error prone like the compass
|
||||
@State var includeHeading = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
VStack {
|
||||
Form {
|
||||
Section(header: Text("Device GPS")) {
|
||||
|
|
@ -103,36 +102,36 @@ struct PositionConfig: View {
|
|||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Section(header: Text("Position Packet")) {
|
||||
|
||||
|
||||
Toggle(isOn: $smartPositionEnabled) {
|
||||
|
||||
Label("Smart Position Broadcast", systemImage: "location.fill.viewfinder")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
Picker("Position Broadcast Interval", selection: $positionBroadcastSeconds) {
|
||||
ForEach(UpdateIntervals.allCases) { at in
|
||||
Text(at.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
||||
|
||||
Text("We should send our position this often (but only if it has changed significantly)")
|
||||
.font(.caption)
|
||||
}
|
||||
Section(header: Text("Position Flags")) {
|
||||
|
||||
|
||||
Text("Optional fields to include when assembling position messages. the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
|
||||
Toggle(isOn: $includeAltitude) {
|
||||
Label("Altitude", systemImage: "arrow.up")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
if includeAltitude {
|
||||
Toggle(isOn: $includeAltitudeMsl) {
|
||||
Label("Altitude is Mean Sea Level", systemImage: "arrow.up.to.line.compact")
|
||||
|
|
@ -143,40 +142,40 @@ struct PositionConfig: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
|
||||
|
||||
Toggle(isOn: $includeSatsinview) {
|
||||
Label("Number of satellites", systemImage: "skew")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $includeSeqNo) { //64
|
||||
|
||||
Toggle(isOn: $includeSeqNo) { // 64
|
||||
Label("Sequence number", systemImage: "number")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $includeTimestamp) { //128
|
||||
|
||||
Toggle(isOn: $includeTimestamp) { // 128
|
||||
Label("timestamp", systemImage: "clock")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $includeHeading) { //128
|
||||
|
||||
Toggle(isOn: $includeHeading) { // 128
|
||||
Label("Vehicle heading", systemImage: "location.circle")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $includeSpeed) { //128
|
||||
|
||||
Toggle(isOn: $includeSpeed) { // 128
|
||||
|
||||
Label("Vehicle speed", systemImage: "speedometer")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
Section(header: Text("Advanced Position Flags")) {
|
||||
|
||||
|
||||
Toggle(isOn: $includeDop) {
|
||||
Text("Dilution of precision (DOP) PDOP used by default")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
|
||||
if includeDop {
|
||||
Toggle(isOn: $includeHvdop) {
|
||||
Text("If DOP is set use, HDOP / VDOP values instead of PDOP")
|
||||
|
|
@ -186,7 +185,7 @@ struct PositionConfig: View {
|
|||
}
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.positionConfig == nil)
|
||||
|
||||
|
||||
Button {
|
||||
isPresentingSaveConfirm = true
|
||||
} label: {
|
||||
|
|
@ -205,12 +204,12 @@ struct PositionConfig: View {
|
|||
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
|
||||
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
|
||||
Button(buttonText) {
|
||||
|
||||
|
||||
if fixedPosition {
|
||||
_ = bleManager.sendPosition(destNum: node!.num, wantResponse: true)
|
||||
}
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
|
||||
|
||||
if connectedNode != nil {
|
||||
var pc = Config.PositionConfig()
|
||||
pc.positionBroadcastSmartEnabled = smartPositionEnabled
|
||||
|
|
@ -219,7 +218,7 @@ struct PositionConfig: View {
|
|||
pc.gpsUpdateInterval = UInt32(gpsUpdateInterval)
|
||||
pc.gpsAttemptTime = UInt32(gpsAttemptTime)
|
||||
pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds)
|
||||
var pf : PositionFlags = []
|
||||
var pf: PositionFlags = []
|
||||
if includeAltitude { pf.insert(.Altitude) }
|
||||
if includeAltitudeMsl { pf.insert(.AltitudeMsl) }
|
||||
if includeGeoidalSeparation { pf.insert(.GeoidalSeparation) }
|
||||
|
|
@ -253,7 +252,7 @@ struct PositionConfig: View {
|
|||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
|
||||
self.bleManager.context = context
|
||||
self.smartPositionEnabled = node?.positionConfig?.smartPositionEnabled ?? true
|
||||
self.deviceGpsEnabled = node?.positionConfig?.deviceGpsEnabled ?? true
|
||||
|
|
@ -262,9 +261,9 @@ struct PositionConfig: View {
|
|||
self.gpsAttemptTime = Int(node?.positionConfig?.gpsAttemptTime ?? 30)
|
||||
self.positionBroadcastSeconds = Int(node?.positionConfig?.positionBroadcastSeconds ?? 900)
|
||||
self.positionFlags = Int(node?.positionConfig?.positionFlags ?? 3)
|
||||
|
||||
|
||||
let pf = PositionFlags(rawValue: self.positionFlags)
|
||||
|
||||
|
||||
if pf.contains(.Altitude) { self.includeAltitude = true } else { self.includeAltitude = false }
|
||||
if pf.contains(.AltitudeMsl) { self.includeAltitudeMsl = true } else { self.includeAltitudeMsl = false }
|
||||
if pf.contains(.GeoidalSeparation) { self.includeGeoidalSeparation = true } else { self.includeGeoidalSeparation = false }
|
||||
|
|
@ -275,14 +274,14 @@ struct PositionConfig: View {
|
|||
if pf.contains(.Timestamp) { self.includeTimestamp = true } else { self.includeTimestamp = false }
|
||||
if pf.contains(.Speed) { self.includeSpeed = true } else { self.includeSpeed = false }
|
||||
if pf.contains(.Heading) { self.includeHeading = true } else { self.includeHeading = false }
|
||||
|
||||
|
||||
self.hasChanges = false
|
||||
|
||||
|
||||
// Need to request a PositionConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.positionConfig == nil {
|
||||
print("empty position config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil && connectedNode!.num > 0 {
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestPositionConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ struct MeshLog: View {
|
|||
@State private var document: LogDocument = LogDocument(logFile: "MESHTASTIC MESH ACTIVITY LOG\n")
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
List(logs, id: \.self, rowContent: Text.init)
|
||||
.task {
|
||||
do {
|
||||
|
|
@ -55,7 +55,7 @@ struct MeshLog: View {
|
|||
)
|
||||
.textSelection(.enabled)
|
||||
.font(.caption)
|
||||
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Spacer()
|
||||
Button(role: .destructive) {
|
||||
|
|
@ -74,7 +74,7 @@ struct MeshLog: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
Spacer()
|
||||
|
||||
|
||||
Button {
|
||||
isExporting = true
|
||||
} label: {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue