Merge branch 'main' into debugging/channel-settings

This commit is contained in:
Garth Vander Houwen 2022-08-25 14:51:42 -07:00 committed by GitHub
commit e5825e8154
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 3373 additions and 1480 deletions

View file

@ -22,6 +22,7 @@
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; };
DD3501892852FC3B000FC853 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3501882852FC3B000FC853 /* Settings.swift */; };
DD35018B2852FC79000FC853 /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD35018A2852FC79000FC853 /* UserSettings.swift */; };
DD4033C228B286B70096A444 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4033C128B286B70096A444 /* Onboarding.swift */; };
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582528582E9B009B0E59 /* DeviceConfig.swift */; };
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; };
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582928585C32009B0E59 /* RangeTestConfig.swift */; };
@ -51,6 +52,8 @@
DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */; };
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; };
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; };
DD8ED9C52898D51F00B3B0AB /* WiFiConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */; };
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */; };
DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; };
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; };
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
@ -67,6 +70,15 @@
DDAF8C6926ED0D070058C060 /* deviceonly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6826ED0D070058C060 /* deviceonly.pb.swift */; };
DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C6D26ED19040058C060 /* Extensions.swift */; };
DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */; };
DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */; };
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 */; };
DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */; };
DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */; };
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */; };
DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */; };
DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; };
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; };
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; };
@ -115,6 +127,8 @@
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = "<group>"; };
DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
DD35018A2852FC79000FC853 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = "<group>"; };
DD4033C128B286B70096A444 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = "<group>"; };
DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 8.xcdatamodel"; sourceTree = "<group>"; };
DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = "<group>"; };
DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = "<group>"; };
DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = "<group>"; };
@ -145,6 +159,10 @@
DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCsvFile.swift; sourceTree = "<group>"; };
DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = "<group>"; };
DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 5.xcdatamodel"; sourceTree = "<group>"; };
DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiConfig.swift; sourceTree = "<group>"; };
DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingError.swift; sourceTree = "<group>"; };
DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 6.xcdatamodel"; sourceTree = "<group>"; };
DD90860A26F645B700DC5189 /* Meshtastic.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meshtastic.entitlements; sourceTree = "<group>"; };
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = "<group>"; };
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
@ -163,6 +181,16 @@
DDAF8C6D26ED19040058C060 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
DDB2CC6D27F3EB47009C5FCC /* telemetry.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = telemetry.pb.swift; sourceTree = "<group>"; };
DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 3.xcdatamodel"; sourceTree = "<group>"; };
DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = device_metadata.pb.swift; sourceTree = "<group>"; };
DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothConfig.swift; sourceTree = "<group>"; };
DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModel v 7.xcdatamodel"; 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>"; };
DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfigEnums.swift; sourceTree = "<group>"; };
DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenIntervals.swift; sourceTree = "<group>"; };
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = "<group>"; };
DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiFiModes.swift; sourceTree = "<group>"; };
DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; };
DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = "<group>"; };
DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = "<group>"; };
@ -270,10 +298,12 @@
DD61937A2863876A00E59241 /* Config */ = {
isa = PBXGroup;
children = (
DDB6ABD528AE742000384BA1 /* BluetoothConfig.swift */,
DD41582528582E9B009B0E59 /* DeviceConfig.swift */,
DD8EBF42285058FA00426DCA /* DisplayConfig.swift */,
DD2553562855B02500E55709 /* LoRaConfig.swift */,
DD2553582855B52700E55709 /* PositionConfig.swift */,
DD8ED9C42898D51F00B3B0AB /* WiFiConfig.swift */,
DD61937B2863877A00E59241 /* Module */,
);
path = Config;
@ -301,6 +331,20 @@
path = Export;
sourceTree = "<group>";
};
DD8ED9C6289CE4A100B3B0AB /* Enums */ = {
isa = PBXGroup;
children = (
DDB6ABD828B0A4BA00384BA1 /* BluetoothModes.swift */,
DDB6ABDF28B13AC700384BA1 /* DeviceRoles.swift */,
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */,
DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */,
DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */,
DDB6ABE328B13FFF00384BA1 /* ScreenIntervals.swift */,
DDB6ABE728B141AF00384BA1 /* WiFiModes.swift */,
);
path = Enums;
sourceTree = "<group>";
};
DD8EDE9226F97A2B00A5A10B /* Frameworks */ = {
isa = PBXGroup;
children = (
@ -311,6 +355,7 @@
DDAF8C5626ED07740058C060 /* Protobufs */ = {
isa = PBXGroup;
children = (
DDB3107128A6224100F1DE3D /* device_metadata.pb.swift */,
DDCFF600285453A7005FA625 /* localonly.pb.swift */,
DD4DED8F27AD2975004BA27E /* cannedmessages.pb.swift */,
DDAF8C6126ED0A230058C060 /* admin.pb.swift */,
@ -355,6 +400,7 @@
DDC2E15626CE248E0042C5E4 /* Meshtastic */ = {
isa = PBXGroup;
children = (
DD8ED9C6289CE4A100B3B0AB /* Enums */,
DD90860A26F645B700DC5189 /* Meshtastic.entitlements */,
DDC4D5662754996200A4208E /* Persistence */,
DDAF8C5626ED07740058C060 /* Protobufs */,
@ -404,8 +450,9 @@
DD47E3D726F2F21A00029299 /* Bluetooth */,
DD47E3CA26F0E50300029299 /* Nodes */,
DDC2E18B26CE25A70042C5E4 /* Messages */,
DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */,
DD4A911C2708C57100501B7E /* Settings */,
DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */,
DD4033C128B286B70096A444 /* Onboarding.swift */,
);
path = Views;
sourceTree = "<group>";
@ -447,6 +494,7 @@
DDC3B273283F411B00AC321C /* LastHeardText.swift */,
DDA6B2EA28420A7B003E8C16 /* NodeAnnotation.swift */,
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */,
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -643,8 +691,10 @@
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */,
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */,
DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */,
DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */,
DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */,
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */,
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */,
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
DDC4D568275499A500A4208E /* Persistence.swift in Sources */,
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */,
@ -661,8 +711,10 @@
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */,
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */,
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */,
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */,
DDB2CC6E27F3EB47009C5FCC /* telemetry.pb.swift in Sources */,
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */,
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
@ -671,14 +723,19 @@
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */,
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 */,
DDB3107228A6224100F1DE3D /* device_metadata.pb.swift in Sources */,
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */,
DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */,
DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */,
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
C9483F6D2773017500998F6B /* MapView.swift in Sources */,
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */,
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */,
DD8ED9C52898D51F00B3B0AB /* WiFiConfig.swift in Sources */,
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */,
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */,
DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */,
@ -686,11 +743,14 @@
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */,
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */,
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */,
DD4033C228B286B70096A444 /* Onboarding.swift in Sources */,
DDB6ABE428B13FFF00384BA1 /* ScreenIntervals.swift in Sources */,
DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */,
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */,
C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */,
DD4C158E2824AA7E0032668E /* config.pb.swift in Sources */,
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */,
DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */,
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */,
DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */,
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */,
@ -875,7 +935,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.3.27;
MARKETING_VERSION = 1.3.39;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -907,7 +967,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.3.27;
MARKETING_VERSION = 1.3.39;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1080,12 +1140,16 @@
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */,
DDB6ABD728AE8F5D00384BA1 /* MeshtasticDataModel v 7.xcdatamodel */,
DD8ED9C9289EA77E00B3B0AB /* MeshtasticDataModel v 6.xcdatamodel */,
DD8ED9C328978D9D00B3B0AB /* MeshtasticDataModel v 5.xcdatamodel */,
DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */,
DDB2CC6F27F3F0AC009C5FCC /* MeshtasticDataModel v 3.xcdatamodel */,
DD45C77427BD4EF80011784F /* MeshtasticDataModel v2.xcdatamodel */,
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */,
);
currentVersion = DD619373285CC7D600E59241 /* MeshtasticDataModel v 4.xcdatamodel */;
currentVersion = DD4033C328B405A60096A444 /* MeshtasticDataModel v 8.xcdatamodel */;
name = Meshtastic.xcdatamodeld;
path = Meshtastic/Meshtastic.xcdatamodeld;
sourceTree = "<group>";

View file

@ -0,0 +1,39 @@
//
// BluetoothModes.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/19/22.
//
enum BluetoothModes: Int, CaseIterable, Identifiable {
case randomPin = 0
case fixedPin = 1
case noPin = 2
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .randomPin:
return "Random PIN"
case .fixedPin:
return "Fixed PIN"
case .noPin:
return "No PIN (Just Works)"
}
}
}
func protoEnumValue() -> Config.BluetoothConfig.PairingMode {
switch self {
case .randomPin:
return Config.BluetoothConfig.PairingMode.randomPin
case .fixedPin:
return Config.BluetoothConfig.PairingMode.fixedPin
case .noPin:
return Config.BluetoothConfig.PairingMode.noPin
}
}
}

View file

@ -0,0 +1,48 @@
//
// 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
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .client:
return "Client (default) - App connected client."
case .clientMute:
return "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 "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."
case .routerClient:
return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client."
}
}
}
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
}
}
}

View file

@ -0,0 +1,181 @@
//
// LoraConfig.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/20/22.
//
import Foundation
enum RegionCodes : Int, CaseIterable, Identifiable {
case unset = 0
case us = 1
case eu433 = 2
case eu868 = 3
case cn = 4
case jp = 5
case anz = 6
case kr = 7
case tw = 8
case ru = 9
case `in` = 10
case nz865 = 11
case th = 12
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"
}
}
}
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
}
}
}
enum ModemPresets : Int, CaseIterable, Identifiable {
case LongFast = 0
case LongSlow = 1
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 .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 .VLongSlow:
return Config.LoRaConfig.ModemPreset.vlongSlow
case .MedSlow:
return Config.LoRaConfig.ModemPreset.medSlow
case .MedFast:
return Config.LoRaConfig.ModemPreset.medFast
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 = 0
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"
}
}
}
}

View file

@ -0,0 +1,132 @@
//
// GpsFormats.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/20/22.
//
import Foundation
enum GpsFormats: Int, CaseIterable, Identifiable {
case gpsFormatDec = 0
case gpsFormatDms = 1
case gpsFormatUtm = 2
case gpsFormatMgrs = 3
case gpsFormatOlc = 4
case gpsFormatOsgr = 5
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .gpsFormatDec:
return "Decimal Degrees Format"
case .gpsFormatDms:
return "Degrees Minutes Seconds"
case .gpsFormatUtm:
return "Universal Transverse Mercator"
case .gpsFormatMgrs:
return "Military Grid Reference System"
case .gpsFormatOlc:
return "Open Location Code (aka Plus Codes)"
case .gpsFormatOsgr:
return "Ordnance Survey Grid Reference"
}
}
}
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
switch self {
case .gpsFormatDec:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec
case .gpsFormatDms:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms
case .gpsFormatUtm:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm
case .gpsFormatMgrs:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs
case .gpsFormatOlc:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc
case .gpsFormatOsgr:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr
}
}
}
enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
case fiveSeconds = 5
case tenSeconds = 10
case fifteenSeconds = 15
case thirtySeconds = 0
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
case thirtyMinutes = 1800
case oneHour = 3600
case maxInt32 = 2147483647
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .fiveSeconds:
return "Five Seconds"
case .tenSeconds:
return "Ten Seconds"
case .fifteenSeconds:
return "fifteenSeconds"
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
case .thirtyMinutes:
return "Thirty Minutes"
case .oneHour:
return "One Hour"
case .maxInt32:
return "On Boot Only"
}
}
}
}
enum GpsAttemptTimes: Int, CaseIterable, Identifiable {
case thirtySeconds = 0
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
}
}
}
}

View file

@ -0,0 +1,109 @@
//
// RoutingError.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/4/22.
//
enum RoutingError: Int, CaseIterable, Identifiable {
case none = 0
case noRoute = 1
case gotNak = 2
case timeout = 3
case noInterface = 4
case maxRetransmit = 5
case noChannel = 6
case tooLarge = 7
case noResponse = 8
case badRequest = 32
case notAuthorized = 33
var id: Int { self.rawValue }
var display: String {
get {
switch self {
case .none:
return "No Error."
case .noRoute:
return "No Route"
case .gotNak:
return "Received a nak"
case .timeout:
return "Timeout"
case .noInterface:
return "No Interface"
case .maxRetransmit:
return "Max Retransmission Reached"
case .noChannel:
return "No Channel"
case .tooLarge:
return "The packet is too large"
case .noResponse:
return "No Response"
case .badRequest:
return "Bad Request"
case .notAuthorized:
return "Not Authorized"
}
}
}
var description: String {
get {
switch self {
case .none:
return "This message is not a failure."
case .noRoute:
return "Our node doesn't have a route to the requested destination anymore."
case .gotNak:
return "We received a nak while trying to forward on your behalf."
case .timeout:
return "We timed out while attempting to route this packet."
case .noInterface:
return "No suitable interface could be found for delivering this packet."
case .maxRetransmit:
return "We reached the max retransmission count (Hop Limit) and have received no responses."
case .noChannel:
return "No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)."
case .tooLarge:
return "The packet was too big for sending (exceeds interface MTU after encoding)."
case .noResponse:
return "The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions)."
case .badRequest:
return "The application layer service on the remote node received your request, but considered your request somehow invalid."
case .notAuthorized:
return "The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel)."
}
}
}
func protoEnumValue() -> Routing.Error {
switch self {
case .none:
return Routing.Error.none
case .noRoute:
return Routing.Error.noRoute
case .gotNak:
return Routing.Error.gotNak
case .timeout:
return Routing.Error.timeout
case .noInterface:
return Routing.Error.noInterface
case .maxRetransmit:
return Routing.Error.maxRetransmit
case .noChannel:
return Routing.Error.noChannel
case .tooLarge:
return Routing.Error.tooLarge
case .noResponse:
return Routing.Error.noResponse
case .badRequest:
return Routing.Error.badRequest
case .notAuthorized:
return Routing.Error.notAuthorized
}
}
}

View file

@ -0,0 +1,73 @@
//
// ScreenIntervals.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/20/22.
//
import Foundation
// Default of 0 is One Minute
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 0
case fifteenMinutes = 900
case thirtyMinutes = 1800
case oneHour = 3600
case max = 31536000 // One Year
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
case .thirtyMinutes:
return "Thirty Minutes"
case .oneHour:
return "One Hour"
case .max:
return "Always On"
}
}
}
}
// Default of 0 is off
enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
case off = 0
case thirtySeconds = 30
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .off:
return "Off"
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
}
}
}
}

View file

@ -0,0 +1,42 @@
//
// WiFiModes.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/20/22.
//
import Foundation
enum WiFiModes: Int, CaseIterable, Identifiable {
case client = 0
case accessPoint = 1
case accessPointHidden = 2
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .client:
return "Client"
case .accessPoint:
return "Software Access Point"
case .accessPointHidden:
return "Software Access Point (Hidden)"
}
}
}
func protoEnumValue() -> Config.WiFiConfig.WiFiMode {
switch self {
case .client:
return Config.WiFiConfig.WiFiMode.client
case .accessPoint:
return Config.WiFiConfig.WiFiMode.accessPoint
case .accessPointHidden:
return Config.WiFiConfig.WiFiMode.accessPointHidden
}
}
}

View file

@ -35,8 +35,30 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
}
} else {
// Create Device Telemetry Header
csvString = "Battery Level, Voltage, Channel Utilization, Airtime, Timestamp"
// Create Environment Telemetry Header
csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, Voltage, Current"
for dm in telemetry{
if dm.metricsType == 0 {
csvString += "\n"
csvString += String("\(dm.temperature)°")
csvString += ", "
csvString += String(dm.relativeHumidity)
csvString += ", "
csvString += String(dm.barometricPressure)
csvString += ", "
csvString += String(dm.gasResistance)
csvString += ", "
csvString += String(dm.voltage)
csvString += ", "
csvString += String(dm.current)
csvString += ", "
csvString += dm.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? "Unknown Age"
}
}
}
return csvString

View file

@ -29,7 +29,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
@Published var connectedPeripheral: Peripheral!
@Published var lastConnectionError: String
@Published var lastConnnectionVersion: String
@Published var connectedVersion: String
@Published var isSwitchedOn: Bool = false
@Published var isScanning: Bool = false
@ -77,7 +77,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
//self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
self.lastConnectionError = ""
self.lastConnnectionVersion = "0.0.0"
self.connectedVersion = "0.0.0"
super.init()
// let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager")
centralManager = CBCentralManager(delegate: self, queue: nil)
@ -131,7 +131,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
self.timeoutTimerCount += 1
if timeoutTimerCount == 5 {
if timeoutTimerCount == 10 {
if connectedPeripheral != nil {
@ -145,7 +145,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if meshLoggingEnabled { MeshLogger.log(self.lastConnectionError + " This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer.") }
self.timeoutTimerCount = 0
self.timeoutTimer?.invalidate()
if self.timeoutTimer != nil {
self.timeoutTimer!.invalidate()
}
} else {
@ -159,19 +162,27 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") }
stopScanning()
if self.connectedPeripheral != nil {
if meshLoggingEnabled { MeshLogger.log(" BLE Disconnecting from: \(self.connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")") }
self.disconnectPeripheral()
}
self.connectedVersion = "0.0.0"
self.centralManager?.connect(peripheral)
// Invalidate any existing timer
if self.timeoutTimer != nil {
self.timeoutTimer!.invalidate()
}
// Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent
// the timer from running on the main UI thread
let context = ["name": "@\(peripheral.name ?? "Unknown")"]
self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true)
self.timeoutTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true)
RunLoop.current.add(self.timeoutTimer!, forMode: .common)
}
@ -223,8 +234,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// Invalidate and reset connection timer count, remove any connection errors
self.lastConnectionError = ""
self.timeoutTimer!.invalidate()
self.timeoutTimerCount = 0
if self.timeoutTimer != nil {
self.timeoutTimer!.invalidate()
}
// Map the peripheral to the connectedNode and connectedPeripheral ObservedObjects
connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first
@ -259,7 +273,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// Happens when device is manually reset / powered off
// We will try and re-connect to this device
lastConnectionError = "🚨 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds."
lastConnectionError = "🚨 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within one minute."
if peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
if meshLoggingEnabled { MeshLogger.log(" BLE Reconnecting: \(peripheral.name ?? "Unknown")") }
self.connectTo(peripheral: peripheral)
@ -460,15 +474,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// MyInfo
if decodedInfo.myInfo.isInitialized && decodedInfo.myInfo.myNodeNum > 0 {
let lastDotIndex = decodedInfo.myInfo.firmwareVersion.lastIndex(of: ".")
let version = decodedInfo.myInfo.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.myInfo.firmwareVersion))]
nowKnown = true
connectedVersion = String(version)
let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!)
if myInfo != nil {
self.connectedPeripheral.bitrate = myInfo!.bitrate
self.connectedPeripheral.num = myInfo!.myNodeNum
lastConnnectionVersion = myInfo?.firmwareVersion ?? myInfo!.firmwareVersion ?? "Unknown"
self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown"
self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown"
self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown"
@ -584,6 +603,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if userSettings?.provideLocation ?? false {
if self.positionTimer != nil {
self.positionTimer!.invalidate()
}
let context = ["name": "@\(peripheral.name ?? "Unknown")"]
@ -734,7 +754,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return success
}
public func sendPosition(destNum: Int64, wantResponse: Bool) -> Bool {
public func sendLocation(destNum: Int64, wantAck: Bool) -> Bool {
var success = false
@ -744,58 +764,91 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
var waypointPacket = Waypoint()
waypointPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
waypointPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
let fetchNode: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNode.predicate = NSPredicate(format: "num == %lld", fromNodeNum)
let oneWeekFromNow = Calendar.current.date(byAdding: .day, value: 7, to: Date())
waypointPacket.expire = UInt32(oneWeekFromNow!.timeIntervalSince1970)
waypointPacket.name = "Test Waypoint"
var meshPacket = MeshPacket()
meshPacket.to = UInt32(destNum)
meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num
meshPacket.wantAck = true//wantAck
var dataMessage = DataMessage()
dataMessage.payload = try! waypointPacket.serializedData()
dataMessage.portnum = PortNum.waypointApp
meshPacket.decoded = dataMessage
do {
let fetchedNode = try context?.fetch(fetchNode) as! [NodeInfoEntity]
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if meshLoggingEnabled { MeshLogger.log("📍 Sent a Location Packet from the Apple device GPS to node: \(fromNodeNum)") }
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
if fetchedNode.count == 1 {
var positionPacket = Position()
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
// Get Errors without some speed
if LocationHelper.currentSpeed >= 5 {
positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed)
positionPacket.groundTrack = UInt32(LocationHelper.currentHeading)
}
var meshPacket = MeshPacket()
meshPacket.to = UInt32(destNum)
meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num
meshPacket.wantAck = wantResponse
var dataMessage = DataMessage()
dataMessage.payload = try! positionPacket.serializedData()
dataMessage.portnum = PortNum.positionApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet from the Apple device GPS to node: \(fromNodeNum)") }
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
}
}
}
return success
}
public func sendPosition(destNum: Int64, wantAck: Bool) -> Bool {
var success = false
let fromNodeNum = connectedPeripheral.num
if fromNodeNum <= 0 || (LocationHelper.currentLocation.latitude == LocationHelper.DefaultLocation.latitude && LocationHelper.currentLocation.longitude == LocationHelper.DefaultLocation.longitude) {
} catch {
success = false
return false
}
var positionPacket = Position()
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
// Get Errors without some speed
if LocationHelper.currentSpeed >= 5 {
positionPacket.groundSpeed = UInt32(LocationHelper.currentSpeed)
positionPacket.groundTrack = UInt32(LocationHelper.currentHeading)
}
var meshPacket = MeshPacket()
meshPacket.to = UInt32(destNum)
meshPacket.from = 0 // Send 0 as from from phone to device to avoid warning about client trying to set node num
meshPacket.wantAck = wantAck
var dataMessage = DataMessage()
dataMessage.payload = try! positionPacket.serializedData()
dataMessage.portnum = PortNum.positionApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if meshLoggingEnabled { MeshLogger.log("📍 Sent a Position Packet from the Apple device GPS to node: \(fromNodeNum)") }
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
}
return success
@ -809,7 +862,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
// Send a position out to the mesh if "share location with the mesh" is enabled in settings
if userSettings!.provideLocation {
let success = sendPosition(destNum: connectedPeripheral.num, wantResponse: false)
let success = sendPosition(destNum: connectedPeripheral.num, wantAck: false)
if !success {
print("Failed to send positon to device")
@ -819,7 +872,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
}
public func sendShutdown(destNum: Int64, wantResponse: Bool) -> Bool {
public func sendShutdown(destNum: Int64) -> Bool {
var adminPacket = AdminMessage()
adminPacket.shutdownSeconds = 10
@ -829,7 +882,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
@ -867,7 +920,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func sendReboot(destNum: Int64, wantResponse: Bool) -> Bool {
public func sendReboot(destNum: Int64) -> Bool {
var adminPacket = AdminMessage()
adminPacket.rebootSeconds = 10
@ -877,7 +930,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
@ -916,7 +969,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func sendFactoryReset(destNum: Int64, wantResponse: Bool) -> Bool {
public func sendFactoryReset(destNum: Int64) -> Bool {
var deviceConfig = Config.DeviceConfig()
deviceConfig.factoryReset = true
@ -929,7 +982,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
@ -966,7 +1019,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setOwner = config
@ -976,16 +1029,16 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Position Config for \(toUser.longName ?? "Unknown")"
let messageDescription = "Saved User Config for \(toUser.longName ?? "Unknown")"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
@ -995,7 +1048,36 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveDeviceConfig(config: Config.DeviceConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveBluetoothConfig(config: Config.BluetoothConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.bluetooth = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Bluetooth Config for \(toUser.longName ?? "Unknown")"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
}
public func saveDeviceConfig(config: Config.DeviceConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.device = config
@ -1005,7 +1087,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
@ -1024,7 +1106,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveDisplayConfig(config: Config.DisplayConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveDisplayConfig(config: Config.DisplayConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.display = config
@ -1034,7 +1116,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
@ -1053,7 +1135,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveLoRaConfig(config: Config.LoRaConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.lora = config
@ -1064,7 +1146,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
@ -1083,7 +1165,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func savePositionConfig(config: Config.PositionConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func savePositionConfig(config: Config.PositionConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.position = config
@ -1093,7 +1175,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
@ -1112,7 +1194,36 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveWiFiConfig(config: Config.WiFiConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setConfig.wifi = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved WiFi Config for \(toUser.longName ?? "Unknown")"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
return 0
}
public func saveCannedMessageModuleConfig(config: ModuleConfig.CannedMessageConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.cannedMessage = config
@ -1122,7 +1233,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
@ -1143,19 +1254,19 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
public func saveCannedMessageModuleMessages(messages: String, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setCannedMessageModulePart1 = messages
adminPacket.setCannedMessageModuleMessages = messages
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(toUser.num)
meshPacket.from = 0 //UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = wantResponse
meshPacket.decoded = dataMessage
@ -1221,20 +1332,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
public func getCannedMessageModuleMessages(destNum: Int64, wantResponse: Bool) -> Bool {
var adminPacket = AdminMessage()
adminPacket.getCannedMessageModulePart1Request = true
//adminPacket.getOwnerRequest = true
adminPacket.getCannedMessageModuleMessagesRequest = true
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.decoded.wantResponse = wantResponse
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = wantResponse
meshPacket.decoded = dataMessage
@ -1267,7 +1378,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func saveExternalNotificationModuleConfig(config: ModuleConfig.ExternalNotificationConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveExternalNotificationModuleConfig(config: ModuleConfig.ExternalNotificationConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.externalNotification = config
@ -1277,7 +1388,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
@ -1295,7 +1406,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveRangeTestModuleConfig(config: ModuleConfig.RangeTestConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.rangeTest = config
@ -1305,7 +1416,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
@ -1323,7 +1434,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveSerialModuleConfig(config: ModuleConfig.SerialConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveSerialModuleConfig(config: ModuleConfig.SerialConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.serial = config
@ -1333,7 +1444,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
@ -1352,7 +1463,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return 0
}
public func saveTelemetryModuleConfig(config: ModuleConfig.TelemetryConfig, fromUser: UserEntity, toUser: UserEntity, wantResponse: Bool) -> Int64 {
public func saveTelemetryModuleConfig(config: ModuleConfig.TelemetryConfig, fromUser: UserEntity, toUser: UserEntity) -> Int64 {
var adminPacket = AdminMessage()
adminPacket.setModuleConfig.telemetry = config
@ -1362,7 +1473,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
meshPacket.from = 0 //UInt32(fromUser.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()

View file

@ -23,6 +23,17 @@ extension Date {
}
}
extension Int {
func numberOfDigits() -> Int {
if abs(self) < 10 {
return 1
} else {
return 1 + (self/10).numberOfDigits()
}
}
}
extension String {
/// Create `Data` from hexadecimal string representation

View file

@ -21,6 +21,10 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
isDefault = true
print("📟 Default Device config")
} else {
print("📟 Custom Device config")
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -88,6 +92,91 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
}
}
if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) {
var isDefault = false
if (try! config.bluetooth.jsonString()) == "{}" {
isDefault = true
print("📶 Default Bluetooth config")
if meshlogging { MeshLogger.log("🖥️ Default Bluetooth config \(String(nodeNum))") }
} else {
if meshlogging { MeshLogger.log("🖥️ Custom Bluetooth config \(String(nodeNum))") }
print("📶 Custom Bluetooth config")
}
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]
// Found a node, save Device Config
if !fetchedNode.isEmpty {
if fetchedNode[0].bluetoothConfig == nil {
let newBluetoothConfig = BluetoothConfigEntity(context: context)
if isDefault {
newBluetoothConfig.enabled = true
newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue)
newBluetoothConfig.fixedPin = Int32("123456") ?? 123456
} else {
newBluetoothConfig.enabled = config.bluetooth.enabled
newBluetoothConfig.mode = Int32(config.bluetooth.mode.rawValue)
newBluetoothConfig.fixedPin = Int32(config.bluetooth.fixedPin)
}
fetchedNode[0].bluetoothConfig = newBluetoothConfig
} else {
if isDefault {
fetchedNode[0].bluetoothConfig?.enabled = true
fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue)
fetchedNode[0].bluetoothConfig?.fixedPin = Int32("123456") ?? 123456
} else {
fetchedNode[0].bluetoothConfig?.enabled = config.bluetooth.enabled
fetchedNode[0].bluetoothConfig?.mode = Int32(config.bluetooth.mode.rawValue)
fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.bluetooth.fixedPin)
}
}
do {
try context.save()
if meshlogging { MeshLogger.log("💾 Updated Bluetooth Config for node number: \(String(nodeNum))") }
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Updating Core Data BluetoothConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Bluetooth Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data BluetoothConfigEntity failed: \(nsError)")
}
}
if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) {
var isDefault = false
@ -95,7 +184,12 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
if (try! config.display.jsonString()) == "{}" {
isDefault = true
print("🖥️ Default Display config")
if meshlogging { MeshLogger.log("🖥️ Default Display config \(String(nodeNum))") }
} else {
if meshlogging { MeshLogger.log("🖥️ Custom Display config \(String(nodeNum))") }
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -116,12 +210,14 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
newDisplayConfig.screenOnSeconds = 0
newDisplayConfig.screenCarouselInterval = 0
newDisplayConfig.gpsFormat = 0
newDisplayConfig.compassNorthTop = false
} else {
newDisplayConfig.gpsFormat = Int32(config.display.gpsFormat.rawValue)
newDisplayConfig.screenOnSeconds = Int32(config.display.screenOnSecs)
newDisplayConfig.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs)
newDisplayConfig.compassNorthTop = config.display.compassNorthTop
}
fetchedNode[0].displayConfig = newDisplayConfig
@ -132,12 +228,14 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
fetchedNode[0].displayConfig?.screenOnSeconds = 0
fetchedNode[0].displayConfig?.screenCarouselInterval = 0
fetchedNode[0].displayConfig?.gpsFormat = 0
fetchedNode[0].displayConfig?.compassNorthTop = false
} else {
fetchedNode[0].displayConfig?.gpsFormat = Int32(config.display.gpsFormat.rawValue)
fetchedNode[0].displayConfig?.screenOnSeconds = Int32(config.display.screenOnSecs)
fetchedNode[0].displayConfig?.screenCarouselInterval = Int32(config.display.autoScreenCarouselSecs)
fetchedNode[0].displayConfig?.compassNorthTop = config.display.compassNorthTop
}
}
@ -153,10 +251,15 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
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)")
}
}
@ -243,10 +346,16 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
let nsError = error as NSError
print("💥 Error Updating Core Data LoRaConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Lora Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data LoRaConfigEntity failed: \(nsError)")
}
}
@ -257,6 +366,11 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
if (try! config.position.jsonString()) == "{}" {
isDefault = true
if meshlogging { MeshLogger.log("🗺️ Default Position config received \(String(nodeNum))") }
} else {
if meshlogging { MeshLogger.log("🗺️ Custom Position config received \(String(nodeNum))") }
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -328,10 +442,97 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
let nsError = error as NSError
print("💥 Error Updating Core Data PositionConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Position Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data PositionConfigEntity failed: \(nsError)")
}
}
if config.payloadVariant == Config.OneOf_PayloadVariant.wifi(config.wifi) {
var isDefault = false
if (try! config.wifi.jsonString()) == "{}" {
isDefault = true
if meshlogging { MeshLogger.log("📶 Default WiFi config received \(String(nodeNum))") }
} else {
if meshlogging { MeshLogger.log("📶 Custom WiFi config received \(String(nodeNum))") }
}
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]
// Found a node, save WiFi Config
if !fetchedNode.isEmpty {
if fetchedNode[0].wiFiConfig == nil {
let newWiFiConfig = WiFiConfigEntity(context: context)
if isDefault {
newWiFiConfig.ssid = ""
newWiFiConfig.password = ""
newWiFiConfig.mode = 0
} else {
newWiFiConfig.ssid = config.wifi.ssid
newWiFiConfig.password = config.wifi.psk
newWiFiConfig.mode = Int32(config.wifi.mode.rawValue)
}
newWiFiConfig.num = fetchedNode[0].num
fetchedNode[0].wiFiConfig = newWiFiConfig
} else {
if isDefault {
fetchedNode[0].wiFiConfig?.ssid = ""
fetchedNode[0].wiFiConfig?.password = ""
fetchedNode[0].wiFiConfig?.mode = 0
} else {
fetchedNode[0].wiFiConfig?.ssid = config.wifi.ssid
fetchedNode[0].wiFiConfig?.password = config.wifi.psk
fetchedNode[0].wiFiConfig?.mode = Int32(config.wifi.mode.rawValue)
}
}
do {
try context.save()
if meshlogging { MeshLogger.log("💾 Updated WiFi Config for node number: \(String(nodeNum))") }
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Updating Core Data WiFiConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save WiFi Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data WiFiConfigEntity failed: \(nsError)")
}
}
}
@ -347,6 +548,9 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
isDefault = true
print("🥫 Default Canned Message Module config")
} else {
print("🥫 Custom Canned Message Module config")
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -388,7 +592,8 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
newCannedMessageConfig.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue)
newCannedMessageConfig.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue)
}
newCannedMessageConfig.num = nodeNum
fetchedNode[0].cannedMessageConfig = newCannedMessageConfig
} else {
@ -419,13 +624,14 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
fetchedNode[0].cannedMessageConfig?.inputbrokerEventCcw = Int32(config.cannedMessage.inputbrokerEventCcw.rawValue)
fetchedNode[0].cannedMessageConfig?.inputbrokerEventPress = Int32(config.cannedMessage.inputbrokerEventPress.rawValue)
}
fetchedNode[0].cannedMessageConfig?.num = nodeNum
}
do {
try context.save()
if meshlogging { MeshLogger.log("💾 Updated Canned Message Module Config for node number: \(String(nodeNum))") }
print(try config.cannedMessage.jsonString())
} catch {
@ -434,10 +640,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
let nsError = error as NSError
print("💥 Error Updating Core Data CannedMessageConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Canned Message Module Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data CannedMessageConfigEntity failed: \(nsError)")
}
}
@ -449,6 +660,10 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
isDefault = true
print("🚨 Default External Notifiation Module config")
} else {
print("🚨 Custom External Notifiation Module config")
}
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -485,6 +700,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
newExternalNotificationConfig.outputMilliseconds = Int32(config.externalNotification.outputMs)
}
newExternalNotificationConfig.num = nodeNum
fetchedNode[0].externalNotificationConfig = newExternalNotificationConfig
} else {
@ -507,6 +723,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
fetchedNode[0].externalNotificationConfig?.output = Int32(config.externalNotification.output)
fetchedNode[0].externalNotificationConfig?.outputMilliseconds = Int32(config.externalNotification.outputMs)
}
fetchedNode[0].externalNotificationConfig?.num = nodeNum
}
do {
@ -521,10 +738,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
let nsError = error as NSError
print("💥 Error Updating Core Data ExternalNotificationConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save External Notifiation Module Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data ExternalNotificationConfigEntity failed: \(nsError)")
}
}
@ -563,7 +785,7 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
newRangeTestConfig.enabled = config.rangeTest.enabled
newRangeTestConfig.save = config.rangeTest.save
}
newRangeTestConfig.num = nodeNum
fetchedNode[0].rangeTestConfig = newRangeTestConfig
} else {
@ -594,10 +816,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
let nsError = error as NSError
print("💥 Error Updating Core Data RangeTestConfigEntity: \(nsError)")
}
} else {
print("💥 No Nodes found in local database matching node number \(nodeNum) unable to save Range Test Module Config")
}
} catch {
let nsError = error as NSError
print("💥 Fetching node for core data RangeTestConfigEntity failed: \(nsError)")
}
}
@ -688,10 +915,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
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)")
}
}
@ -723,25 +955,17 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
newTelemetryConfig.deviceUpdateInterval = 0
newTelemetryConfig.environmentUpdateInterval = 0
newTelemetryConfig.environmentMeasurementEnabled = false
newTelemetryConfig.environmentSensorType = 0
newTelemetryConfig.environmentScreenEnabled = false
newTelemetryConfig.environmentDisplayFahrenheit = false
newTelemetryConfig.environmentRecoveryInterval = 0
newTelemetryConfig.environmentReadErrorCountThreshold = 0
} else {
newTelemetryConfig.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval)
newTelemetryConfig.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval)
newTelemetryConfig.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled
newTelemetryConfig.environmentSensorType = Int32(config.telemetry.environmentSensorType.rawValue)
newTelemetryConfig.environmentScreenEnabled = config.telemetry.environmentScreenEnabled
newTelemetryConfig.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit
newTelemetryConfig.environmentRecoveryInterval = Int32(config.telemetry.environmentRecoveryInterval)
newTelemetryConfig.environmentReadErrorCountThreshold = Int32(config.telemetry.environmentReadErrorCountThreshold)
}
fetchedNode[0].telemetryConfig = newTelemetryConfig
} else {
@ -751,23 +975,16 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
fetchedNode[0].telemetryConfig?.deviceUpdateInterval = 0
fetchedNode[0].telemetryConfig?.environmentUpdateInterval = 0
fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = false
fetchedNode[0].telemetryConfig?.environmentSensorType = 0
fetchedNode[0].telemetryConfig?.environmentScreenEnabled = false
fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = false
fetchedNode[0].telemetryConfig?.environmentRecoveryInterval = 0
fetchedNode[0].telemetryConfig?.environmentReadErrorCountThreshold = 0
} else {
fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.telemetry.deviceUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.telemetry.environmentUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.telemetry.environmentMeasurementEnabled
fetchedNode[0].telemetryConfig?.environmentSensorType = Int32(config.telemetry.environmentSensorType.rawValue)
fetchedNode[0].telemetryConfig?.environmentScreenEnabled = config.telemetry.environmentScreenEnabled
fetchedNode[0].telemetryConfig?.environmentDisplayFahrenheit = config.telemetry.environmentDisplayFahrenheit
fetchedNode[0].telemetryConfig?.environmentRecoveryInterval = Int32(config.telemetry.environmentRecoveryInterval)
fetchedNode[0].telemetryConfig?.environmentReadErrorCountThreshold = Int32(config.telemetry.environmentReadErrorCountThreshold)
}
}
@ -783,10 +1000,15 @@ func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObj
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)")
}
}
}
@ -1138,6 +1360,7 @@ func adminAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb
print("channel settings:", channel.settings)
}
if meshLogging { MeshLogger.log(" MESH PACKET received for Admin App UNHANDLED \(try! packet.jsonString())") }
//PowerConfig
@ -1175,7 +1398,7 @@ func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb
fetchedNode[0].id = Int64(packet.from)
fetchedNode[0].num = Int64(packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
fetchedNode[0].snr = packet.rxSnr
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
@ -1195,6 +1418,7 @@ func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedOb
print("💥 Error Saving NodeInfoEntity from POSITION_APP \(nsError)")
}
}
} else {
print("💥 Empty POSITION_APP Packet")
@ -1245,43 +1469,47 @@ func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObj
if meshLogging { MeshLogger.log("🕸️ ROUTING PACKET received for RequestID: \(packet.decoded.requestID) Error: \(errorExplanation)") }
if routingMessage.errorReason == Routing.Error.none {
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
fetchMessageRequest.predicate = NSPredicate(format: "messageId == %lld", Int64(packet.decoded.requestID))
do {
do {
let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity]
let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity]
if fetchedMessage?.count ?? 0 > 0 {
if fetchedMessage?.count ?? 0 > 0 {
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)
fetchedMessage![0].objectWillChange.send()
fetchedMessage![0].fromUser?.objectWillChange.send()
fetchedMessage![0].toUser?.objectWillChange.send()
} else {
return
}
fetchedMessage![0].ackSNR = packet.rxSnr
fetchedMessage![0].ackTimestamp = Int32(packet.rxTime)
fetchedMessage![0].objectWillChange.send()
fetchedMessage![0].fromUser?.objectWillChange.send()
fetchedMessage![0].toUser?.objectWillChange.send()
try context.save()
if meshLogging {
MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)")
}
} else {
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)")
return
}
try context.save()
if meshLogging {
MeshLogger.log("💾 ACK Received and saved for MessageID \(packet.decoded.requestID)")
}
} catch {
context.rollback()
let nsError = error as NSError
print("💥 Error Saving ACK for message MessageID \(packet.id) Error: \(nsError)")
}
}
}
@ -1355,7 +1583,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging:
if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) {
if meshLogging { MeshLogger.log("💬 Message received for text message app \(messageText)") }
if meshLogging { MeshLogger.log("💬 Message received for text message app") }
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
@ -1406,6 +1634,28 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging:
messageSaved = true
if messageSaved { //&& (newMessage.toUser != nil && newMessage.toUser!.num == broadcastNodeNum || connectedNode == newMessage.toUser!.num) {
if newMessage.fromUser != nil {
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? "Unknown")",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown")") }
}
}
} catch {
context.rollback()
@ -1413,28 +1663,6 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, meshLogging:
let nsError = error as NSError
print("💥 Failed to save new MessageEntity \(nsError)")
}
do {
if messageSaved && (newMessage.toUser != nil && newMessage.toUser!.num == broadcastNodeNum || connectedNode == newMessage.toUser!.num) {
// Create an iOS Notification for the received message and schedule it immediately
let manager = LocalNotificationManager()
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? "Unknown")",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
if meshLogging { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
}
} catch {
}
} catch {

View file

@ -4,7 +4,7 @@
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:meshtastic.org/*</string>
<string>applinks:meshtastic.org/e/*</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>

View file

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>MeshtasticDataModel v 4.xcdatamodel</string>
<string>MeshtasticDataModel v 8.xcdatamodel</string>
</dict>
</plist>

View file

@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21G72" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<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="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" 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="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
<relationship name="toUser" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
<fetchedProperty name="tapbacks" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="messageId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" attributeType="String"/>
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="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="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<relationship name="wiFiConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="WiFiConfigEntity" inverseName="wiFiConfigNode" inverseEntity="WiFiConfigEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 32" defaultValueString="0" 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="num" attributeType="Integer 64" 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"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwModel" attributeType="String"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longName" attributeType="String"/>
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="team" optional="YES" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
<fetchedProperty name="adminMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(toUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
</fetchedProperty>
<fetchedProperty name="allMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isEmoji == false AND admin = false"/>
</fetchedProperty>
</entity>
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="apHidden" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="apMode" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="wiFiConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="wiFiConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<elements>
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="209"/>
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="314"/>
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="134"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="134"/>
</elements>
</model>

View file

@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="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="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" 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="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
<relationship name="toUser" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
<fetchedProperty name="tapbacks" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="messageId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" attributeType="String"/>
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="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="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<relationship name="wiFiConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="WiFiConfigEntity" inverseName="wiFiConfigNode" inverseEntity="WiFiConfigEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 32" defaultValueString="0" 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="num" attributeType="Integer 64" 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"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwModel" attributeType="String"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longName" attributeType="String"/>
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="team" optional="YES" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
<fetchedProperty name="adminMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(toUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
</fetchedProperty>
<fetchedProperty name="allMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isEmoji == false AND admin = false"/>
</fetchedProperty>
</entity>
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="wiFiConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="wiFiConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<elements>
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="209"/>
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="119"/>
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="314"/>
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="134"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
</elements>
</model>

View file

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" 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="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
<relationship name="toUser" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
<fetchedProperty name="tapbacks" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="messageId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" attributeType="String"/>
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<relationship name="wiFiConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="WiFiConfigEntity" inverseName="wiFiConfigNode" inverseEntity="WiFiConfigEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 32" defaultValueString="0" 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="num" attributeType="Integer 64" 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"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwModel" attributeType="String"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longName" attributeType="String"/>
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="team" optional="YES" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
<fetchedProperty name="adminMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(toUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
</fetchedProperty>
<fetchedProperty name="allMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isEmoji == false AND admin = false"/>
</fetchedProperty>
</entity>
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="wiFiConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="wiFiConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<elements>
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="209"/>
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="119"/>
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="329"/>
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="134"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
<element name="BluetoothConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
</elements>
</model>

View file

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" 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="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="fromUser" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
<relationship name="toUser" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
<fetchedProperty name="tapbacks" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="messageId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" attributeType="String"/>
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<relationship name="wiFiConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="WiFiConfigEntity" inverseName="wiFiConfigNode" inverseEntity="WiFiConfigEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" 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="num" attributeType="Integer 64" 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"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwModel" attributeType="String"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longName" attributeType="String"/>
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="team" optional="YES" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
<fetchedProperty name="adminMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(toUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
</fetchedProperty>
<fetchedProperty name="allMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND isEmoji == false AND admin = false"/>
</fetchedProperty>
</entity>
<entity name="WiFiConfigEntity" representedClassName="WiFiConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="password" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="ssid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="wiFiConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="wiFiConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<elements>
<element name="BluetoothConfigEntity" positionX="54" positionY="153" width="128" height="104"/>
<element name="CannedMessageConfigEntity" positionX="45" positionY="144" width="128" height="209"/>
<element name="DeviceConfigEntity" positionX="45" positionY="144" width="128" height="104"/>
<element name="DisplayConfigEntity" positionX="54" positionY="153" width="128" height="119"/>
<element name="ExternalNotificationConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="LoRaConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="245"/>
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="209"/>
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="329"/>
<element name="PositionConfigEntity" positionX="63" positionY="162" width="128" height="149"/>
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
<element name="RangeTestConfigEntity" positionX="72" positionY="171" width="128" height="104"/>
<element name="SerialConfigEntity" positionX="54" positionY="153" width="128" height="164"/>
<element name="TelemetryConfigEntity" positionX="72" positionY="171" width="128" height="134"/>
<element name="TelemetryEntity" positionX="160" positionY="192" width="128" height="209"/>
<element name="UserEntity" positionX="0" positionY="144" width="128" height="230"/>
<element name="WiFiConfigEntity" positionX="45" positionY="144" width="128" height="119"/>
</elements>
</model>

View file

@ -178,6 +178,16 @@ struct AdminMessage {
set {variant = .confirmSetModuleConfig(newValue)}
}
///
/// Send all channels in the response to this message
var getAllChannelRequest: Bool {
get {
if case .getAllChannelRequest(let v)? = variant {return v}
return false
}
set {variant = .getAllChannelRequest(newValue)}
}
///
/// Setting channels/radio config remotely carries the risk that you might send an invalid config and the radio never talks to your mesh again.
/// Therefore if setting either of these properties remotely, you must send a confirm_xxx message within 10 minutes.
@ -223,123 +233,33 @@ struct AdminMessage {
}
///
/// Get the Canned Message Module message part1 in the response to this message.
var getCannedMessageModulePart1Request: Bool {
/// Get the Canned Message Module messages in the response to this message.
var getCannedMessageModuleMessagesRequest: Bool {
get {
if case .getCannedMessageModulePart1Request(let v)? = variant {return v}
if case .getCannedMessageModuleMessagesRequest(let v)? = variant {return v}
return false
}
set {variant = .getCannedMessageModulePart1Request(newValue)}
set {variant = .getCannedMessageModuleMessagesRequest(newValue)}
}
///
/// TODO: REPLACE
var getCannedMessageModulePart1Response: String {
/// Get the Canned Message Module messages in the response to this message.
var getCannedMessageModuleMessagesResponse: String {
get {
if case .getCannedMessageModulePart1Response(let v)? = variant {return v}
if case .getCannedMessageModuleMessagesResponse(let v)? = variant {return v}
return String()
}
set {variant = .getCannedMessageModulePart1Response(newValue)}
set {variant = .getCannedMessageModuleMessagesResponse(newValue)}
}
///
/// Get the Canned Message Module message part2 in the response to this message.
var getCannedMessageModulePart2Request: Bool {
/// Set the Canned Message Module messages text.
var setCannedMessageModuleMessages: String {
get {
if case .getCannedMessageModulePart2Request(let v)? = variant {return v}
return false
}
set {variant = .getCannedMessageModulePart2Request(newValue)}
}
///
/// TODO: REPLACE
var getCannedMessageModulePart2Response: String {
get {
if case .getCannedMessageModulePart2Response(let v)? = variant {return v}
if case .setCannedMessageModuleMessages(let v)? = variant {return v}
return String()
}
set {variant = .getCannedMessageModulePart2Response(newValue)}
}
///
/// Get the Canned Message Module message part3 in the response to this message.
var getCannedMessageModulePart3Request: Bool {
get {
if case .getCannedMessageModulePart3Request(let v)? = variant {return v}
return false
}
set {variant = .getCannedMessageModulePart3Request(newValue)}
}
///
/// TODO: REPLACE
var getCannedMessageModulePart3Response: String {
get {
if case .getCannedMessageModulePart3Response(let v)? = variant {return v}
return String()
}
set {variant = .getCannedMessageModulePart3Response(newValue)}
}
///
/// Get the Canned Message Module message part4 in the response to this message.
var getCannedMessageModulePart4Request: Bool {
get {
if case .getCannedMessageModulePart4Request(let v)? = variant {return v}
return false
}
set {variant = .getCannedMessageModulePart4Request(newValue)}
}
///
/// TODO: REPLACE
var getCannedMessageModulePart4Response: String {
get {
if case .getCannedMessageModulePart4Response(let v)? = variant {return v}
return String()
}
set {variant = .getCannedMessageModulePart4Response(newValue)}
}
///
/// Set the canned message module part 1 text.
var setCannedMessageModulePart1: String {
get {
if case .setCannedMessageModulePart1(let v)? = variant {return v}
return String()
}
set {variant = .setCannedMessageModulePart1(newValue)}
}
///
/// Set the canned message module part 2 text.
var setCannedMessageModulePart2: String {
get {
if case .setCannedMessageModulePart2(let v)? = variant {return v}
return String()
}
set {variant = .setCannedMessageModulePart2(newValue)}
}
///
/// Set the canned message module part 3 text.
var setCannedMessageModulePart3: String {
get {
if case .setCannedMessageModulePart3(let v)? = variant {return v}
return String()
}
set {variant = .setCannedMessageModulePart3(newValue)}
}
///
/// Set the canned message module part 4 text.
var setCannedMessageModulePart4: String {
get {
if case .setCannedMessageModulePart4(let v)? = variant {return v}
return String()
}
set {variant = .setCannedMessageModulePart4(newValue)}
set {variant = .setCannedMessageModuleMessages(newValue)}
}
///
@ -352,6 +272,26 @@ struct AdminMessage {
set {variant = .shutdownSeconds(newValue)}
}
///
/// Request the node to send device metadata (firmware, protobuf version, etc)
var getDeviceMetadataRequest: UInt32 {
get {
if case .getDeviceMetadataRequest(let v)? = variant {return v}
return 0
}
set {variant = .getDeviceMetadataRequest(newValue)}
}
///
/// Device metadata response
var getDeviceMetadataResponse: DeviceMetadata {
get {
if case .getDeviceMetadataResponse(let v)? = variant {return v}
return DeviceMetadata()
}
set {variant = .getDeviceMetadataResponse(newValue)}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
///
@ -405,6 +345,9 @@ struct AdminMessage {
/// Sent immediatly after a config change has been sent to ensure comms, if this is not recieved, the config will be reverted after 10 mins
case confirmSetModuleConfig(Bool)
///
/// Send all channels in the response to this message
case getAllChannelRequest(Bool)
///
/// Setting channels/radio config remotely carries the risk that you might send an invalid config and the radio never talks to your mesh again.
/// Therefore if setting either of these properties remotely, you must send a confirm_xxx message within 10 minutes.
/// If you fail to do so, the radio will assume loss of comms and revert your changes.
@ -421,44 +364,23 @@ struct AdminMessage {
/// Tell the node to reboot in this many seconds (or <0 to cancel reboot)
case rebootSeconds(Int32)
///
/// Get the Canned Message Module message part1 in the response to this message.
case getCannedMessageModulePart1Request(Bool)
/// Get the Canned Message Module messages in the response to this message.
case getCannedMessageModuleMessagesRequest(Bool)
///
/// TODO: REPLACE
case getCannedMessageModulePart1Response(String)
/// Get the Canned Message Module messages in the response to this message.
case getCannedMessageModuleMessagesResponse(String)
///
/// Get the Canned Message Module message part2 in the response to this message.
case getCannedMessageModulePart2Request(Bool)
///
/// TODO: REPLACE
case getCannedMessageModulePart2Response(String)
///
/// Get the Canned Message Module message part3 in the response to this message.
case getCannedMessageModulePart3Request(Bool)
///
/// TODO: REPLACE
case getCannedMessageModulePart3Response(String)
///
/// Get the Canned Message Module message part4 in the response to this message.
case getCannedMessageModulePart4Request(Bool)
///
/// TODO: REPLACE
case getCannedMessageModulePart4Response(String)
///
/// Set the canned message module part 1 text.
case setCannedMessageModulePart1(String)
///
/// Set the canned message module part 2 text.
case setCannedMessageModulePart2(String)
///
/// Set the canned message module part 3 text.
case setCannedMessageModulePart3(String)
///
/// Set the canned message module part 4 text.
case setCannedMessageModulePart4(String)
/// Set the Canned Message Module messages text.
case setCannedMessageModuleMessages(String)
///
/// Tell the node to shutdown in this many seconds (or <0 to cancel shutdown)
case shutdownSeconds(Int32)
///
/// Request the node to send device metadata (firmware, protobuf version, etc)
case getDeviceMetadataRequest(UInt32)
///
/// Device metadata response
case getDeviceMetadataResponse(DeviceMetadata)
#if !swift(>=4.1)
static func ==(lhs: AdminMessage.OneOf_Variant, rhs: AdminMessage.OneOf_Variant) -> Bool {
@ -522,6 +444,10 @@ struct AdminMessage {
guard case .confirmSetModuleConfig(let l) = lhs, case .confirmSetModuleConfig(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getAllChannelRequest, .getAllChannelRequest): return {
guard case .getAllChannelRequest(let l) = lhs, case .getAllChannelRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.confirmSetChannel, .confirmSetChannel): return {
guard case .confirmSetChannel(let l) = lhs, case .confirmSetChannel(let r) = rhs else { preconditionFailure() }
return l == r
@ -538,58 +464,30 @@ struct AdminMessage {
guard case .rebootSeconds(let l) = lhs, case .rebootSeconds(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart1Request, .getCannedMessageModulePart1Request): return {
guard case .getCannedMessageModulePart1Request(let l) = lhs, case .getCannedMessageModulePart1Request(let r) = rhs else { preconditionFailure() }
case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return {
guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart1Response, .getCannedMessageModulePart1Response): return {
guard case .getCannedMessageModulePart1Response(let l) = lhs, case .getCannedMessageModulePart1Response(let r) = rhs else { preconditionFailure() }
case (.getCannedMessageModuleMessagesResponse, .getCannedMessageModuleMessagesResponse): return {
guard case .getCannedMessageModuleMessagesResponse(let l) = lhs, case .getCannedMessageModuleMessagesResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart2Request, .getCannedMessageModulePart2Request): return {
guard case .getCannedMessageModulePart2Request(let l) = lhs, case .getCannedMessageModulePart2Request(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart2Response, .getCannedMessageModulePart2Response): return {
guard case .getCannedMessageModulePart2Response(let l) = lhs, case .getCannedMessageModulePart2Response(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart3Request, .getCannedMessageModulePart3Request): return {
guard case .getCannedMessageModulePart3Request(let l) = lhs, case .getCannedMessageModulePart3Request(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart3Response, .getCannedMessageModulePart3Response): return {
guard case .getCannedMessageModulePart3Response(let l) = lhs, case .getCannedMessageModulePart3Response(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart4Request, .getCannedMessageModulePart4Request): return {
guard case .getCannedMessageModulePart4Request(let l) = lhs, case .getCannedMessageModulePart4Request(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getCannedMessageModulePart4Response, .getCannedMessageModulePart4Response): return {
guard case .getCannedMessageModulePart4Response(let l) = lhs, case .getCannedMessageModulePart4Response(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setCannedMessageModulePart1, .setCannedMessageModulePart1): return {
guard case .setCannedMessageModulePart1(let l) = lhs, case .setCannedMessageModulePart1(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setCannedMessageModulePart2, .setCannedMessageModulePart2): return {
guard case .setCannedMessageModulePart2(let l) = lhs, case .setCannedMessageModulePart2(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setCannedMessageModulePart3, .setCannedMessageModulePart3): return {
guard case .setCannedMessageModulePart3(let l) = lhs, case .setCannedMessageModulePart3(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.setCannedMessageModulePart4, .setCannedMessageModulePart4): return {
guard case .setCannedMessageModulePart4(let l) = lhs, case .setCannedMessageModulePart4(let r) = rhs else { preconditionFailure() }
case (.setCannedMessageModuleMessages, .setCannedMessageModuleMessages): return {
guard case .setCannedMessageModuleMessages(let l) = lhs, case .setCannedMessageModuleMessages(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.shutdownSeconds, .shutdownSeconds): return {
guard case .shutdownSeconds(let l) = lhs, case .shutdownSeconds(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getDeviceMetadataRequest, .getDeviceMetadataRequest): return {
guard case .getDeviceMetadataRequest(let l) = lhs, case .getDeviceMetadataRequest(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.getDeviceMetadataResponse, .getDeviceMetadataResponse): return {
guard case .getDeviceMetadataResponse(let l) = lhs, case .getDeviceMetadataResponse(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
@ -624,6 +522,10 @@ struct AdminMessage {
///
/// TODO: REPLACE
case loraConfig // = 5
///
/// TODO: REPLACE
case bluetoothConfig // = 6
case UNRECOGNIZED(Int)
init() {
@ -638,6 +540,7 @@ struct AdminMessage {
case 3: self = .wifiConfig
case 4: self = .displayConfig
case 5: self = .loraConfig
case 6: self = .bluetoothConfig
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -650,6 +553,7 @@ struct AdminMessage {
case .wifiConfig: return 3
case .displayConfig: return 4
case .loraConfig: return 5
case .bluetoothConfig: return 6
case .UNRECOGNIZED(let i): return i
}
}
@ -736,6 +640,7 @@ extension AdminMessage.ConfigType: CaseIterable {
.wifiConfig,
.displayConfig,
.loraConfig,
.bluetoothConfig,
]
}
@ -780,23 +685,17 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
15: .standard(proto: "get_module_config_response"),
16: .standard(proto: "set_module_config"),
17: .standard(proto: "confirm_set_module_config"),
18: .standard(proto: "get_all_channel_request"),
32: .standard(proto: "confirm_set_channel"),
33: .standard(proto: "confirm_set_radio"),
34: .standard(proto: "exit_simulator"),
35: .standard(proto: "reboot_seconds"),
36: .standard(proto: "get_canned_message_module_part1_request"),
37: .standard(proto: "get_canned_message_module_part1_response"),
38: .standard(proto: "get_canned_message_module_part2_request"),
39: .standard(proto: "get_canned_message_module_part2_response"),
40: .standard(proto: "get_canned_message_module_part3_request"),
41: .standard(proto: "get_canned_message_module_part3_response"),
42: .standard(proto: "get_canned_message_module_part4_request"),
43: .standard(proto: "get_canned_message_module_part4_response"),
44: .standard(proto: "set_canned_message_module_part1"),
45: .standard(proto: "set_canned_message_module_part2"),
46: .standard(proto: "set_canned_message_module_part3"),
47: .standard(proto: "set_canned_message_module_part4"),
36: .standard(proto: "get_canned_message_module_messages_request"),
37: .standard(proto: "get_canned_message_module_messages_response"),
44: .standard(proto: "set_canned_message_module_messages"),
51: .standard(proto: "shutdown_seconds"),
52: .standard(proto: "get_device_metadata_request"),
53: .standard(proto: "get_device_metadata_response"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -957,6 +856,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
self.variant = .confirmSetModuleConfig(v)
}
}()
case 18: try {
var v: Bool?
try decoder.decodeSingularBoolField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getAllChannelRequest(v)
}
}()
case 32: try {
var v: Bool?
try decoder.decodeSingularBoolField(value: &v)
@ -994,7 +901,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
try decoder.decodeSingularBoolField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart1Request(v)
self.variant = .getCannedMessageModuleMessagesRequest(v)
}
}()
case 37: try {
@ -1002,55 +909,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart1Response(v)
}
}()
case 38: try {
var v: Bool?
try decoder.decodeSingularBoolField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart2Request(v)
}
}()
case 39: try {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart2Response(v)
}
}()
case 40: try {
var v: Bool?
try decoder.decodeSingularBoolField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart3Request(v)
}
}()
case 41: try {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart3Response(v)
}
}()
case 42: try {
var v: Bool?
try decoder.decodeSingularBoolField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart4Request(v)
}
}()
case 43: try {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getCannedMessageModulePart4Response(v)
self.variant = .getCannedMessageModuleMessagesResponse(v)
}
}()
case 44: try {
@ -1058,31 +917,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .setCannedMessageModulePart1(v)
}
}()
case 45: try {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .setCannedMessageModulePart2(v)
}
}()
case 46: try {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .setCannedMessageModulePart3(v)
}
}()
case 47: try {
var v: String?
try decoder.decodeSingularStringField(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .setCannedMessageModulePart4(v)
self.variant = .setCannedMessageModuleMessages(v)
}
}()
case 51: try {
@ -1093,6 +928,27 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
self.variant = .shutdownSeconds(v)
}
}()
case 52: try {
var v: UInt32?
try decoder.decodeSingularUInt32Field(value: &v)
if let v = v {
if self.variant != nil {try decoder.handleConflictingOneOf()}
self.variant = .getDeviceMetadataRequest(v)
}
}()
case 53: try {
var v: DeviceMetadata?
var hadOneofValue = false
if let current = self.variant {
hadOneofValue = true
if case .getDeviceMetadataResponse(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.variant = .getDeviceMetadataResponse(v)
}
}()
default: break
}
}
@ -1160,6 +1016,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
guard case .confirmSetModuleConfig(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 17)
}()
case .getAllChannelRequest?: try {
guard case .getAllChannelRequest(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 18)
}()
case .confirmSetChannel?: try {
guard case .confirmSetChannel(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 32)
@ -1176,58 +1036,30 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
guard case .rebootSeconds(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularInt32Field(value: v, fieldNumber: 35)
}()
case .getCannedMessageModulePart1Request?: try {
guard case .getCannedMessageModulePart1Request(let v)? = self.variant else { preconditionFailure() }
case .getCannedMessageModuleMessagesRequest?: try {
guard case .getCannedMessageModuleMessagesRequest(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 36)
}()
case .getCannedMessageModulePart1Response?: try {
guard case .getCannedMessageModulePart1Response(let v)? = self.variant else { preconditionFailure() }
case .getCannedMessageModuleMessagesResponse?: try {
guard case .getCannedMessageModuleMessagesResponse(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 37)
}()
case .getCannedMessageModulePart2Request?: try {
guard case .getCannedMessageModulePart2Request(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 38)
}()
case .getCannedMessageModulePart2Response?: try {
guard case .getCannedMessageModulePart2Response(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 39)
}()
case .getCannedMessageModulePart3Request?: try {
guard case .getCannedMessageModulePart3Request(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 40)
}()
case .getCannedMessageModulePart3Response?: try {
guard case .getCannedMessageModulePart3Response(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 41)
}()
case .getCannedMessageModulePart4Request?: try {
guard case .getCannedMessageModulePart4Request(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularBoolField(value: v, fieldNumber: 42)
}()
case .getCannedMessageModulePart4Response?: try {
guard case .getCannedMessageModulePart4Response(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 43)
}()
case .setCannedMessageModulePart1?: try {
guard case .setCannedMessageModulePart1(let v)? = self.variant else { preconditionFailure() }
case .setCannedMessageModuleMessages?: try {
guard case .setCannedMessageModuleMessages(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 44)
}()
case .setCannedMessageModulePart2?: try {
guard case .setCannedMessageModulePart2(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 45)
}()
case .setCannedMessageModulePart3?: try {
guard case .setCannedMessageModulePart3(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 46)
}()
case .setCannedMessageModulePart4?: try {
guard case .setCannedMessageModulePart4(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularStringField(value: v, fieldNumber: 47)
}()
case .shutdownSeconds?: try {
guard case .shutdownSeconds(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularInt32Field(value: v, fieldNumber: 51)
}()
case .getDeviceMetadataRequest?: try {
guard case .getDeviceMetadataRequest(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 52)
}()
case .getDeviceMetadataResponse?: try {
guard case .getDeviceMetadataResponse(let v)? = self.variant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 53)
}()
case nil: break
}
try unknownFields.traverse(visitor: &visitor)
@ -1248,6 +1080,7 @@ extension AdminMessage.ConfigType: SwiftProtobuf._ProtoNameProviding {
3: .same(proto: "WIFI_CONFIG"),
4: .same(proto: "DISPLAY_CONFIG"),
5: .same(proto: "LORA_CONFIG"),
6: .same(proto: "BLUETOOTH_CONFIG"),
]
}

View file

@ -29,19 +29,7 @@ struct CannedMessageModuleConfig {
///
/// Predefined messages for canned message module separated by '|' characters.
var messagesPart1: String = String()
///
/// TODO: REPLACE
var messagesPart2: String = String()
///
/// TODO: REPLACE
var messagesPart3: String = String()
///
/// TODO: REPLACE
var messagesPart4: String = String()
var messages: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
@ -57,10 +45,7 @@ extension CannedMessageModuleConfig: @unchecked Sendable {}
extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "CannedMessageModuleConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
11: .same(proto: "messagesPart1"),
12: .same(proto: "messagesPart2"),
13: .same(proto: "messagesPart3"),
14: .same(proto: "messagesPart4"),
1: .same(proto: "messages"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -69,36 +54,21 @@ extension CannedMessageModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._Messa
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 11: try { try decoder.decodeSingularStringField(value: &self.messagesPart1) }()
case 12: try { try decoder.decodeSingularStringField(value: &self.messagesPart2) }()
case 13: try { try decoder.decodeSingularStringField(value: &self.messagesPart3) }()
case 14: try { try decoder.decodeSingularStringField(value: &self.messagesPart4) }()
case 1: try { try decoder.decodeSingularStringField(value: &self.messages) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.messagesPart1.isEmpty {
try visitor.visitSingularStringField(value: self.messagesPart1, fieldNumber: 11)
}
if !self.messagesPart2.isEmpty {
try visitor.visitSingularStringField(value: self.messagesPart2, fieldNumber: 12)
}
if !self.messagesPart3.isEmpty {
try visitor.visitSingularStringField(value: self.messagesPart3, fieldNumber: 13)
}
if !self.messagesPart4.isEmpty {
try visitor.visitSingularStringField(value: self.messagesPart4, fieldNumber: 14)
if !self.messages.isEmpty {
try visitor.visitSingularStringField(value: self.messages, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: CannedMessageModuleConfig, rhs: CannedMessageModuleConfig) -> Bool {
if lhs.messagesPart1 != rhs.messagesPart1 {return false}
if lhs.messagesPart2 != rhs.messagesPart2 {return false}
if lhs.messagesPart3 != rhs.messagesPart3 {return false}
if lhs.messagesPart4 != rhs.messagesPart4 {return false}
if lhs.messages != rhs.messages {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

View file

@ -77,6 +77,14 @@ struct Config {
set {payloadVariant = .lora(newValue)}
}
var bluetooth: Config.BluetoothConfig {
get {
if case .bluetooth(let v)? = payloadVariant {return v}
return Config.BluetoothConfig()
}
set {payloadVariant = .bluetooth(newValue)}
}
var unknownFields = SwiftProtobuf.UnknownStorage()
///
@ -88,6 +96,7 @@ struct Config {
case wifi(Config.WiFiConfig)
case display(Config.DisplayConfig)
case lora(Config.LoRaConfig)
case bluetooth(Config.BluetoothConfig)
#if !swift(>=4.1)
static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool {
@ -119,6 +128,10 @@ struct Config {
guard case .lora(let l) = lhs, case .lora(let r) = rhs else { preconditionFailure() }
return l == r
}()
case (.bluetooth, .bluetooth): return {
guard case .bluetooth(let l) = lhs, case .bluetooth(let r) = rhs else { preconditionFailure() }
return l == r
}()
default: return false
}
}
@ -359,7 +372,7 @@ struct Config {
///
/// Power Config\
/// See [power management](/docs/software/other/power) for additional power management state machine option details.
/// See [Power Config](/docs/settings/config/power) for additional power config details.
struct PowerConfig {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -509,6 +522,15 @@ struct Config {
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// Enable WiFi (disables Bluetooth)
var enabled: Bool = false
///
/// If set, this node will try to join the specified wifi network and
/// acquire an address via DHCP
var mode: Config.WiFiConfig.WiFiMode = .client
///
/// If set, this node will try to join the specified wifi network and
/// acquire an address via DHCP
@ -518,22 +540,53 @@ struct Config {
/// If set, will be use to authenticate to the named wifi
var psk: String = String()
///
/// If set, the node will operate as an AP (and DHCP server), otherwise it
/// will be a station
var apMode: Bool = false
///
/// If set, the node AP will broadcast as a hidden SSID
var apHidden: Bool = false
var unknownFields = SwiftProtobuf.UnknownStorage()
enum WiFiMode: SwiftProtobuf.Enum {
typealias RawValue = Int
///
/// This mode is used to connect to an external WiFi network
case client // = 0
///
/// In this mode the node will operate as an AP (and DHCP server)
case accessPoint // = 1
///
/// If set, the node AP will broadcast as a hidden SSID
case accessPointHidden // = 2
case UNRECOGNIZED(Int)
init() {
self = .client
}
init?(rawValue: Int) {
switch rawValue {
case 0: self = .client
case 1: self = .accessPoint
case 2: self = .accessPointHidden
default: self = .UNRECOGNIZED(rawValue)
}
}
var rawValue: Int {
switch self {
case .client: return 0
case .accessPoint: return 1
case .accessPointHidden: return 2
case .UNRECOGNIZED(let i): return i
}
}
}
init() {}
}
///
/// TODO: REPLACE
/// Display Config
struct DisplayConfig {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -553,6 +606,11 @@ struct Config {
/// Potentially useful for devices without user buttons.
var autoScreenCarouselSecs: UInt32 = 0
///
/// If this is set, the displayed compass will always point north. if unset, the old behaviour
/// (top of display is heading direction) is used.
var compassNorthTop: Bool = false
var unknownFields = SwiftProtobuf.UnknownStorage()
///
@ -861,6 +919,68 @@ struct Config {
init() {}
}
struct BluetoothConfig {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// Enable Bluetooth on the device
var enabled: Bool = false
///
/// Determines the pairing strategy for the device
var mode: Config.BluetoothConfig.PairingMode = .randomPin
///
/// Specified pin for PairingMode.FixedPin
var fixedPin: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
enum PairingMode: SwiftProtobuf.Enum {
typealias RawValue = Int
///
/// Device generates a random pin that will be shown on the screen of the device for pairing
case randomPin // = 0
///
/// Device requires a specified fixed pin for pairing
case fixedPin // = 1
///
/// Device requires no pin for pairing
case noPin // = 2
case UNRECOGNIZED(Int)
init() {
self = .randomPin
}
init?(rawValue: Int) {
switch rawValue {
case 0: self = .randomPin
case 1: self = .fixedPin
case 2: self = .noPin
default: self = .UNRECOGNIZED(rawValue)
}
}
var rawValue: Int {
switch self {
case .randomPin: return 0
case .fixedPin: return 1
case .noPin: return 2
case .UNRECOGNIZED(let i): return i
}
}
}
init() {}
}
init() {}
}
@ -916,6 +1036,15 @@ extension Config.PowerConfig.ChargeCurrent: CaseIterable {
]
}
extension Config.WiFiConfig.WiFiMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [Config.WiFiConfig.WiFiMode] = [
.client,
.accessPoint,
.accessPointHidden,
]
}
extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [
@ -960,6 +1089,15 @@ extension Config.LoRaConfig.ModemPreset: CaseIterable {
]
}
extension Config.BluetoothConfig.PairingMode: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [Config.BluetoothConfig.PairingMode] = [
.randomPin,
.fixedPin,
.noPin,
]
}
#endif // swift(>=4.2)
#if swift(>=5.5) && canImport(_Concurrency)
@ -972,11 +1110,14 @@ extension Config.PositionConfig.PositionFlags: @unchecked Sendable {}
extension Config.PowerConfig: @unchecked Sendable {}
extension Config.PowerConfig.ChargeCurrent: @unchecked Sendable {}
extension Config.WiFiConfig: @unchecked Sendable {}
extension Config.WiFiConfig.WiFiMode: @unchecked Sendable {}
extension Config.DisplayConfig: @unchecked Sendable {}
extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {}
extension Config.LoRaConfig: @unchecked Sendable {}
extension Config.LoRaConfig.RegionCode: @unchecked Sendable {}
extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {}
extension Config.BluetoothConfig: @unchecked Sendable {}
extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
@ -990,6 +1131,7 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
4: .same(proto: "wifi"),
5: .same(proto: "display"),
6: .same(proto: "lora"),
7: .same(proto: "bluetooth"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1076,6 +1218,19 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
self.payloadVariant = .lora(v)
}
}()
case 7: try {
var v: Config.BluetoothConfig?
var hadOneofValue = false
if let current = self.payloadVariant {
hadOneofValue = true
if case .bluetooth(let m) = current {v = m}
}
try decoder.decodeSingularMessageField(value: &v)
if let v = v {
if hadOneofValue {try decoder.handleConflictingOneOf()}
self.payloadVariant = .bluetooth(v)
}
}()
default: break
}
}
@ -1111,6 +1266,10 @@ extension Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBas
guard case .lora(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
}()
case .bluetooth?: try {
guard case .bluetooth(let v)? = self.payloadVariant else { preconditionFailure() }
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
}()
case nil: break
}
try unknownFields.traverse(visitor: &visitor)
@ -1377,10 +1536,10 @@ extension Config.PowerConfig.ChargeCurrent: SwiftProtobuf._ProtoNameProviding {
extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = Config.protoMessageName + ".WiFiConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "ssid"),
2: .same(proto: "psk"),
3: .standard(proto: "ap_mode"),
4: .standard(proto: "ap_hidden"),
1: .same(proto: "enabled"),
2: .same(proto: "mode"),
3: .same(proto: "ssid"),
4: .same(proto: "psk"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1389,47 +1548,56 @@ extension Config.WiFiConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.ssid) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.psk) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.apMode) }()
case 4: try { try decoder.decodeSingularBoolField(value: &self.apHidden) }()
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.mode) }()
case 3: try { try decoder.decodeSingularStringField(value: &self.ssid) }()
case 4: try { try decoder.decodeSingularStringField(value: &self.psk) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.enabled != false {
try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1)
}
if self.mode != .client {
try visitor.visitSingularEnumField(value: self.mode, fieldNumber: 2)
}
if !self.ssid.isEmpty {
try visitor.visitSingularStringField(value: self.ssid, fieldNumber: 1)
try visitor.visitSingularStringField(value: self.ssid, fieldNumber: 3)
}
if !self.psk.isEmpty {
try visitor.visitSingularStringField(value: self.psk, fieldNumber: 2)
}
if self.apMode != false {
try visitor.visitSingularBoolField(value: self.apMode, fieldNumber: 3)
}
if self.apHidden != false {
try visitor.visitSingularBoolField(value: self.apHidden, fieldNumber: 4)
try visitor.visitSingularStringField(value: self.psk, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Config.WiFiConfig, rhs: Config.WiFiConfig) -> Bool {
if lhs.enabled != rhs.enabled {return false}
if lhs.mode != rhs.mode {return false}
if lhs.ssid != rhs.ssid {return false}
if lhs.psk != rhs.psk {return false}
if lhs.apMode != rhs.apMode {return false}
if lhs.apHidden != rhs.apHidden {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Config.WiFiConfig.WiFiMode: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "Client"),
1: .same(proto: "AccessPoint"),
2: .same(proto: "AccessPointHidden"),
]
}
extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = Config.protoMessageName + ".DisplayConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "screen_on_secs"),
2: .standard(proto: "gps_format"),
3: .standard(proto: "auto_screen_carousel_secs"),
4: .standard(proto: "compass_north_top"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1441,6 +1609,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.screenOnSecs) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.gpsFormat) }()
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.autoScreenCarouselSecs) }()
case 4: try { try decoder.decodeSingularBoolField(value: &self.compassNorthTop) }()
default: break
}
}
@ -1456,6 +1625,9 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
if self.autoScreenCarouselSecs != 0 {
try visitor.visitSingularUInt32Field(value: self.autoScreenCarouselSecs, fieldNumber: 3)
}
if self.compassNorthTop != false {
try visitor.visitSingularBoolField(value: self.compassNorthTop, fieldNumber: 4)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1463,6 +1635,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
if lhs.screenOnSecs != rhs.screenOnSecs {return false}
if lhs.gpsFormat != rhs.gpsFormat {return false}
if lhs.autoScreenCarouselSecs != rhs.autoScreenCarouselSecs {return false}
if lhs.compassNorthTop != rhs.compassNorthTop {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -1594,3 +1767,55 @@ extension Config.LoRaConfig.ModemPreset: SwiftProtobuf._ProtoNameProviding {
6: .same(proto: "ShortFast"),
]
}
extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = Config.protoMessageName + ".BluetoothConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "enabled"),
2: .same(proto: "mode"),
3: .standard(proto: "fixed_pin"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
case 2: try { try decoder.decodeSingularEnumField(value: &self.mode) }()
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.fixedPin) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.enabled != false {
try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1)
}
if self.mode != .randomPin {
try visitor.visitSingularEnumField(value: self.mode, fieldNumber: 2)
}
if self.fixedPin != 0 {
try visitor.visitSingularUInt32Field(value: self.fixedPin, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Config.BluetoothConfig, rhs: Config.BluetoothConfig) -> Bool {
if lhs.enabled != rhs.enabled {return false}
if lhs.mode != rhs.mode {return false}
if lhs.fixedPin != rhs.fixedPin {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Config.BluetoothConfig.PairingMode: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "RandomPin"),
1: .same(proto: "FixedPin"),
2: .same(proto: "NoPin"),
]
}

View file

@ -0,0 +1,85 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: device_metadata.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// 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 {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
///
/// Device metadata response
struct DeviceMetadata {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// Device firmware version string
var firmwareVersion: String = String()
///
/// Device state version
var deviceStateVersion: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension DeviceMetadata: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "DeviceMetadata"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "firmware_version"),
2: .standard(proto: "device_state_version"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.firmwareVersion) }()
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.deviceStateVersion) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.firmwareVersion.isEmpty {
try visitor.visitSingularStringField(value: self.firmwareVersion, fieldNumber: 1)
}
if self.deviceStateVersion != 0 {
try visitor.visitSingularUInt32Field(value: self.deviceStateVersion, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: DeviceMetadata, rhs: DeviceMetadata) -> Bool {
if lhs.firmwareVersion != rhs.firmwareVersion {return false}
if lhs.deviceStateVersion != rhs.deviceStateVersion {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -91,6 +91,17 @@ struct LocalConfig {
/// Clears the value of `lora`. Subsequent reads from it will return its default value.
mutating func clearLora() {_uniqueStorage()._lora = nil}
///
/// The part of the config that is specific to the Bluetooth settings
var bluetooth: Config.BluetoothConfig {
get {return _storage._bluetooth ?? Config.BluetoothConfig()}
set {_uniqueStorage()._bluetooth = newValue}
}
/// Returns true if `bluetooth` has been explicitly set.
var hasBluetooth: Bool {return _storage._bluetooth != nil}
/// Clears the value of `bluetooth`. Subsequent reads from it will return its default value.
mutating func clearBluetooth() {_uniqueStorage()._bluetooth = nil}
///
/// A version integer used to invalidate old save files when we make
/// incompatible changes This integer is set at build time and is private to
@ -221,7 +232,8 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
4: .same(proto: "wifi"),
5: .same(proto: "display"),
6: .same(proto: "lora"),
7: .same(proto: "version"),
7: .same(proto: "bluetooth"),
8: .same(proto: "version"),
]
fileprivate class _StorageClass {
@ -231,6 +243,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
var _wifi: Config.WiFiConfig? = nil
var _display: Config.DisplayConfig? = nil
var _lora: Config.LoRaConfig? = nil
var _bluetooth: Config.BluetoothConfig? = nil
var _version: UInt32 = 0
static let defaultInstance = _StorageClass()
@ -244,6 +257,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
_wifi = source._wifi
_display = source._display
_lora = source._lora
_bluetooth = source._bluetooth
_version = source._version
}
}
@ -269,7 +283,8 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 4: try { try decoder.decodeSingularMessageField(value: &_storage._wifi) }()
case 5: try { try decoder.decodeSingularMessageField(value: &_storage._display) }()
case 6: try { try decoder.decodeSingularMessageField(value: &_storage._lora) }()
case 7: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }()
case 7: try { try decoder.decodeSingularMessageField(value: &_storage._bluetooth) }()
case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }()
default: break
}
}
@ -300,8 +315,11 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
try { if let v = _storage._lora {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
try { if let v = _storage._bluetooth {
try visitor.visitSingularMessageField(value: v, fieldNumber: 7)
} }()
if _storage._version != 0 {
try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 7)
try visitor.visitSingularUInt32Field(value: _storage._version, fieldNumber: 8)
}
}
try unknownFields.traverse(visitor: &visitor)
@ -318,6 +336,7 @@ extension LocalConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if _storage._wifi != rhs_storage._wifi {return false}
if _storage._display != rhs_storage._display {return false}
if _storage._lora != rhs_storage._lora {return false}
if _storage._bluetooth != rhs_storage._bluetooth {return false}
if _storage._version != rhs_storage._version {return false}
return true
}

View file

@ -130,6 +130,10 @@ enum HardwareModel: SwiftProtobuf.Enum {
/// M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
case m5Stack // = 44
///
/// B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
case stationG1 // = 45
///
/// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
case privateHw // = 255
@ -166,6 +170,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
case 42: self = .nrf52840Pca10059
case 43: self = .drDev
case 44: self = .m5Stack
case 45: self = .stationG1
case 255: self = .privateHw
default: self = .UNRECOGNIZED(rawValue)
}
@ -198,6 +203,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
case .nrf52840Pca10059: return 42
case .drDev: return 43
case .m5Stack: return 44
case .stationG1: return 45
case .privateHw: return 255
case .UNRECOGNIZED(let i): return i
}
@ -235,6 +241,7 @@ extension HardwareModel: CaseIterable {
.nrf52840Pca10059,
.drDev,
.m5Stack,
.stationG1,
.privateHw,
]
}
@ -791,23 +798,6 @@ struct User {
/// Also, "long_name" should be their licence number.
var isLicensed: Bool = false
///
/// Transmit power at antenna connector, in decibel-milliwatt
/// An optional self-reported value useful in network planning, discovery
/// and positioning - along with ant_gain_dbi and ant_azimuth below
var txPowerDbm: UInt32 = 0
///
/// Antenna gain (applicable to both Tx and Rx), in decibel-isotropic
var antGainDbi: UInt32 = 0
///
/// Directional antenna true azimuth *if applicable*, in degrees (0-360)
/// Only applicable in case of stationary nodes with a directional antenna
/// Zero = not applicable (mobile or omni) or not specified
/// (use a value of 360 to indicate an antenna azimuth of zero degrees)
var antAzimuth: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -1075,33 +1065,20 @@ struct DataMessage {
/// a message a heart or poop emoji.
var emoji: UInt32 = 0
///
/// Location structure
var location: Location {
get {return _location ?? Location()}
set {_location = newValue}
}
/// Returns true if `location` has been explicitly set.
var hasLocation: Bool {return self._location != nil}
/// Clears the value of `location`. Subsequent reads from it will return its default value.
mutating func clearLocation() {self._location = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _location: Location? = nil
}
///
/// Location of a waypoint to associate with a message
struct Location {
/// Waypoint message, used to share arbitrary locations across the mesh
struct Waypoint {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
///
/// Id of the location
/// Id of the waypoint
var id: UInt32 = 0
///
@ -1113,13 +1090,21 @@ struct Location {
var longitudeI: Int32 = 0
///
/// Time the location is to expire (epoch)
/// Time the waypoint is to expire (epoch)
var expire: UInt32 = 0
///
/// If true, only allow the original sender to update the location.
/// If true, only allow the original sender to update the waypoint.
var locked: Bool = false
///
/// Name of the waypoint - max 30 chars
var name: String = String()
///*
/// Description of the waypoint - max 100 chars
var description_p: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -1485,65 +1470,58 @@ struct NodeInfo {
///
/// The node number
var num: UInt32 {
get {return _storage._num}
set {_uniqueStorage()._num = newValue}
}
var num: UInt32 = 0
///
/// The user info for this node
var user: User {
get {return _storage._user ?? User()}
set {_uniqueStorage()._user = newValue}
get {return _user ?? User()}
set {_user = newValue}
}
/// Returns true if `user` has been explicitly set.
var hasUser: Bool {return _storage._user != nil}
var hasUser: Bool {return self._user != nil}
/// Clears the value of `user`. Subsequent reads from it will return its default value.
mutating func clearUser() {_uniqueStorage()._user = nil}
mutating func clearUser() {self._user = nil}
///
/// This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true.
/// Position.time now indicates the last time we received a POSITION from that node.
var position: Position {
get {return _storage._position ?? Position()}
set {_uniqueStorage()._position = newValue}
get {return _position ?? Position()}
set {_position = newValue}
}
/// Returns true if `position` has been explicitly set.
var hasPosition: Bool {return _storage._position != nil}
var hasPosition: Bool {return self._position != nil}
/// Clears the value of `position`. Subsequent reads from it will return its default value.
mutating func clearPosition() {_uniqueStorage()._position = nil}
mutating func clearPosition() {self._position = nil}
///
/// Returns the Signal-to-noise ratio (SNR) of the last received message,
/// as measured by the receiver. Return SNR of the last received message in dB
var snr: Float {
get {return _storage._snr}
set {_uniqueStorage()._snr = newValue}
}
var snr: Float = 0
///
/// Set to indicate the last time we received a packet from this node
var lastHeard: UInt32 {
get {return _storage._lastHeard}
set {_uniqueStorage()._lastHeard = newValue}
}
var lastHeard: UInt32 = 0
///
/// The latest device metrics for the node.
var deviceMetrics: DeviceMetrics {
get {return _storage._deviceMetrics ?? DeviceMetrics()}
set {_uniqueStorage()._deviceMetrics = newValue}
get {return _deviceMetrics ?? DeviceMetrics()}
set {_deviceMetrics = newValue}
}
/// Returns true if `deviceMetrics` has been explicitly set.
var hasDeviceMetrics: Bool {return _storage._deviceMetrics != nil}
var hasDeviceMetrics: Bool {return self._deviceMetrics != nil}
/// Clears the value of `deviceMetrics`. Subsequent reads from it will return its default value.
mutating func clearDeviceMetrics() {_uniqueStorage()._deviceMetrics = nil}
mutating func clearDeviceMetrics() {self._deviceMetrics = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _storage = _StorageClass.defaultInstance
fileprivate var _user: User? = nil
fileprivate var _position: Position? = nil
fileprivate var _deviceMetrics: DeviceMetrics? = nil
}
///
@ -2130,7 +2108,7 @@ extension Routing: @unchecked Sendable {}
extension Routing.OneOf_Variant: @unchecked Sendable {}
extension Routing.Error: @unchecked Sendable {}
extension DataMessage: @unchecked Sendable {}
extension Location: @unchecked Sendable {}
extension Waypoint: @unchecked Sendable {}
extension MeshPacket: @unchecked Sendable {}
extension MeshPacket.OneOf_PayloadVariant: @unchecked Sendable {}
extension MeshPacket.Priority: @unchecked Sendable {}
@ -2176,6 +2154,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
42: .same(proto: "NRF52840_PCA10059"),
43: .same(proto: "DR_DEV"),
44: .same(proto: "M5STACK"),
45: .same(proto: "STATION_G1"),
255: .same(proto: "PRIVATE_HW"),
]
}
@ -2464,9 +2443,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
4: .same(proto: "macaddr"),
6: .standard(proto: "hw_model"),
7: .standard(proto: "is_licensed"),
10: .standard(proto: "tx_power_dbm"),
11: .standard(proto: "ant_gain_dbi"),
12: .standard(proto: "ant_azimuth"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2481,9 +2457,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
case 4: try { try decoder.decodeSingularBytesField(value: &self.macaddr) }()
case 6: try { try decoder.decodeSingularEnumField(value: &self.hwModel) }()
case 7: try { try decoder.decodeSingularBoolField(value: &self.isLicensed) }()
case 10: try { try decoder.decodeSingularUInt32Field(value: &self.txPowerDbm) }()
case 11: try { try decoder.decodeSingularUInt32Field(value: &self.antGainDbi) }()
case 12: try { try decoder.decodeSingularUInt32Field(value: &self.antAzimuth) }()
default: break
}
}
@ -2508,15 +2481,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
if self.isLicensed != false {
try visitor.visitSingularBoolField(value: self.isLicensed, fieldNumber: 7)
}
if self.txPowerDbm != 0 {
try visitor.visitSingularUInt32Field(value: self.txPowerDbm, fieldNumber: 10)
}
if self.antGainDbi != 0 {
try visitor.visitSingularUInt32Field(value: self.antGainDbi, fieldNumber: 11)
}
if self.antAzimuth != 0 {
try visitor.visitSingularUInt32Field(value: self.antAzimuth, fieldNumber: 12)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -2527,9 +2491,6 @@ extension User: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase,
if lhs.macaddr != rhs.macaddr {return false}
if lhs.hwModel != rhs.hwModel {return false}
if lhs.isLicensed != rhs.isLicensed {return false}
if lhs.txPowerDbm != rhs.txPowerDbm {return false}
if lhs.antGainDbi != rhs.antGainDbi {return false}
if lhs.antAzimuth != rhs.antAzimuth {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -2677,7 +2638,6 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
6: .standard(proto: "request_id"),
7: .standard(proto: "reply_id"),
8: .same(proto: "emoji"),
9: .same(proto: "location"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2694,17 +2654,12 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 6: try { try decoder.decodeSingularFixed32Field(value: &self.requestID) }()
case 7: try { try decoder.decodeSingularFixed32Field(value: &self.replyID) }()
case 8: try { try decoder.decodeSingularFixed32Field(value: &self.emoji) }()
case 9: try { try decoder.decodeSingularMessageField(value: &self._location) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.portnum != .unknownApp {
try visitor.visitSingularEnumField(value: self.portnum, fieldNumber: 1)
}
@ -2729,9 +2684,6 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if self.emoji != 0 {
try visitor.visitSingularFixed32Field(value: self.emoji, fieldNumber: 8)
}
try { if let v = self._location {
try visitor.visitSingularMessageField(value: v, fieldNumber: 9)
} }()
try unknownFields.traverse(visitor: &visitor)
}
@ -2744,20 +2696,21 @@ extension DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if lhs.requestID != rhs.requestID {return false}
if lhs.replyID != rhs.replyID {return false}
if lhs.emoji != rhs.emoji {return false}
if lhs._location != rhs._location {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "Location"
extension Waypoint: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "Waypoint"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .standard(proto: "latitude_i"),
3: .standard(proto: "longitude_i"),
4: .same(proto: "expire"),
5: .same(proto: "locked"),
6: .same(proto: "name"),
7: .same(proto: "description"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -2771,6 +2724,8 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
case 3: try { try decoder.decodeSingularSFixed32Field(value: &self.longitudeI) }()
case 4: try { try decoder.decodeSingularUInt32Field(value: &self.expire) }()
case 5: try { try decoder.decodeSingularBoolField(value: &self.locked) }()
case 6: try { try decoder.decodeSingularStringField(value: &self.name) }()
case 7: try { try decoder.decodeSingularStringField(value: &self.description_p) }()
default: break
}
}
@ -2792,15 +2747,23 @@ extension Location: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
if self.locked != false {
try visitor.visitSingularBoolField(value: self.locked, fieldNumber: 5)
}
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 6)
}
if !self.description_p.isEmpty {
try visitor.visitSingularStringField(value: self.description_p, fieldNumber: 7)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Location, rhs: Location) -> Bool {
static func ==(lhs: Waypoint, rhs: Waypoint) -> Bool {
if lhs.id != rhs.id {return false}
if lhs.latitudeI != rhs.latitudeI {return false}
if lhs.longitudeI != rhs.longitudeI {return false}
if lhs.expire != rhs.expire {return false}
if lhs.locked != rhs.locked {return false}
if lhs.name != rhs.name {return false}
if lhs.description_p != rhs.description_p {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
@ -3022,98 +2985,56 @@ extension NodeInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
6: .standard(proto: "device_metrics"),
]
fileprivate class _StorageClass {
var _num: UInt32 = 0
var _user: User? = nil
var _position: Position? = nil
var _snr: Float = 0
var _lastHeard: UInt32 = 0
var _deviceMetrics: DeviceMetrics? = nil
static let defaultInstance = _StorageClass()
private init() {}
init(copying source: _StorageClass) {
_num = source._num
_user = source._user
_position = source._position
_snr = source._snr
_lastHeard = source._lastHeard
_deviceMetrics = source._deviceMetrics
}
}
fileprivate mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _StorageClass(copying: _storage)
}
return _storage
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
_ = _uniqueStorage()
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularUInt32Field(value: &_storage._num) }()
case 2: try { try decoder.decodeSingularMessageField(value: &_storage._user) }()
case 3: try { try decoder.decodeSingularMessageField(value: &_storage._position) }()
case 4: try { try decoder.decodeSingularFloatField(value: &_storage._snr) }()
case 5: try { try decoder.decodeSingularFixed32Field(value: &_storage._lastHeard) }()
case 6: try { try decoder.decodeSingularMessageField(value: &_storage._deviceMetrics) }()
default: break
}
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.num) }()
case 2: try { try decoder.decodeSingularMessageField(value: &self._user) }()
case 3: try { try decoder.decodeSingularMessageField(value: &self._position) }()
case 4: try { try decoder.decodeSingularFloatField(value: &self.snr) }()
case 5: try { try decoder.decodeSingularFixed32Field(value: &self.lastHeard) }()
case 6: try { try decoder.decodeSingularMessageField(value: &self._deviceMetrics) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if _storage._num != 0 {
try visitor.visitSingularUInt32Field(value: _storage._num, fieldNumber: 1)
}
try { if let v = _storage._user {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try { if let v = _storage._position {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
if _storage._snr != 0 {
try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4)
}
if _storage._lastHeard != 0 {
try visitor.visitSingularFixed32Field(value: _storage._lastHeard, fieldNumber: 5)
}
try { if let v = _storage._deviceMetrics {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.num != 0 {
try visitor.visitSingularUInt32Field(value: self.num, fieldNumber: 1)
}
try { if let v = self._user {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
try { if let v = self._position {
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
} }()
if self.snr != 0 {
try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 4)
}
if self.lastHeard != 0 {
try visitor.visitSingularFixed32Field(value: self.lastHeard, fieldNumber: 5)
}
try { if let v = self._deviceMetrics {
try visitor.visitSingularMessageField(value: v, fieldNumber: 6)
} }()
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: NodeInfo, rhs: NodeInfo) -> Bool {
if lhs._storage !== rhs._storage {
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
let _storage = _args.0
let rhs_storage = _args.1
if _storage._num != rhs_storage._num {return false}
if _storage._user != rhs_storage._user {return false}
if _storage._position != rhs_storage._position {return false}
if _storage._snr != rhs_storage._snr {return false}
if _storage._lastHeard != rhs_storage._lastHeard {return false}
if _storage._deviceMetrics != rhs_storage._deviceMetrics {return false}
return true
}
if !storagesAreEqual {return false}
}
if lhs.num != rhs.num {return false}
if lhs._user != rhs._user {return false}
if lhs._position != rhs._position {return false}
if lhs.snr != rhs.snr {return false}
if lhs.lastHeard != rhs.lastHeard {return false}
if lhs._deviceMetrics != rhs._deviceMetrics {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

View file

@ -179,7 +179,7 @@ struct ModuleConfig {
/// If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as
/// is_uplink_enabled or is_downlink_enabled.
/// But if this flag is set, all MQTT features will be disabled and no servers will be contacted.
var disabled: Bool = false
var enabled: Bool = false
///
/// The server to use for our MQTT global message gateway feature.
@ -473,31 +473,11 @@ struct ModuleConfig {
/// Enable/Disable the telemetry measurement module on-device display
var environmentScreenEnabled: Bool = false
///
/// Sometimes sensor reads can fail.
/// If this happens, we will retry a configurable number of attempts,
/// each attempt will be delayed by the minimum required refresh rate for that sensor
var environmentReadErrorCountThreshold: UInt32 = 0
///
/// Sometimes we can end up with more than read_error_count_threshold failures.
/// In this case, we will stop trying to read from the sensor for a while.
/// Wait this long until trying to read from the sensor again
var environmentRecoveryInterval: UInt32 = 0
///
/// We'll always read the sensor in Celsius, but sometimes we might want to
/// display the results in Fahrenheit as a "user preference".
var environmentDisplayFahrenheit: Bool = false
///
/// Specify the sensor type
var environmentSensorType: TelemetrySensorType = .notSet
///
/// Specify the peferred GPIO Pin for sensor readings
var environmentSensorPin: UInt32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -865,7 +845,7 @@ extension ModuleConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = ModuleConfig.protoMessageName + ".MQTTConfig"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "disabled"),
1: .same(proto: "enabled"),
2: .same(proto: "address"),
3: .same(proto: "username"),
4: .same(proto: "password"),
@ -878,7 +858,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularBoolField(value: &self.disabled) }()
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
case 2: try { try decoder.decodeSingularStringField(value: &self.address) }()
case 3: try { try decoder.decodeSingularStringField(value: &self.username) }()
case 4: try { try decoder.decodeSingularStringField(value: &self.password) }()
@ -889,8 +869,8 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.disabled != false {
try visitor.visitSingularBoolField(value: self.disabled, fieldNumber: 1)
if self.enabled != false {
try visitor.visitSingularBoolField(value: self.enabled, fieldNumber: 1)
}
if !self.address.isEmpty {
try visitor.visitSingularStringField(value: self.address, fieldNumber: 2)
@ -908,7 +888,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
}
static func ==(lhs: ModuleConfig.MQTTConfig, rhs: ModuleConfig.MQTTConfig) -> Bool {
if lhs.disabled != rhs.disabled {return false}
if lhs.enabled != rhs.enabled {return false}
if lhs.address != rhs.address {return false}
if lhs.username != rhs.username {return false}
if lhs.password != rhs.password {return false}
@ -1184,11 +1164,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
2: .standard(proto: "environment_update_interval"),
3: .standard(proto: "environment_measurement_enabled"),
4: .standard(proto: "environment_screen_enabled"),
5: .standard(proto: "environment_read_error_count_threshold"),
6: .standard(proto: "environment_recovery_interval"),
7: .standard(proto: "environment_display_fahrenheit"),
8: .standard(proto: "environment_sensor_type"),
9: .standard(proto: "environment_sensor_pin"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1201,11 +1177,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.environmentUpdateInterval) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.environmentMeasurementEnabled) }()
case 4: try { try decoder.decodeSingularBoolField(value: &self.environmentScreenEnabled) }()
case 5: try { try decoder.decodeSingularUInt32Field(value: &self.environmentReadErrorCountThreshold) }()
case 6: try { try decoder.decodeSingularUInt32Field(value: &self.environmentRecoveryInterval) }()
case 7: try { try decoder.decodeSingularBoolField(value: &self.environmentDisplayFahrenheit) }()
case 8: try { try decoder.decodeSingularEnumField(value: &self.environmentSensorType) }()
case 9: try { try decoder.decodeSingularUInt32Field(value: &self.environmentSensorPin) }()
default: break
}
}
@ -1224,21 +1196,9 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
if self.environmentScreenEnabled != false {
try visitor.visitSingularBoolField(value: self.environmentScreenEnabled, fieldNumber: 4)
}
if self.environmentReadErrorCountThreshold != 0 {
try visitor.visitSingularUInt32Field(value: self.environmentReadErrorCountThreshold, fieldNumber: 5)
}
if self.environmentRecoveryInterval != 0 {
try visitor.visitSingularUInt32Field(value: self.environmentRecoveryInterval, fieldNumber: 6)
}
if self.environmentDisplayFahrenheit != false {
try visitor.visitSingularBoolField(value: self.environmentDisplayFahrenheit, fieldNumber: 7)
}
if self.environmentSensorType != .notSet {
try visitor.visitSingularEnumField(value: self.environmentSensorType, fieldNumber: 8)
}
if self.environmentSensorPin != 0 {
try visitor.visitSingularUInt32Field(value: self.environmentSensorPin, fieldNumber: 9)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1247,11 +1207,7 @@ extension ModuleConfig.TelemetryConfig: SwiftProtobuf.Message, SwiftProtobuf._Me
if lhs.environmentUpdateInterval != rhs.environmentUpdateInterval {return false}
if lhs.environmentMeasurementEnabled != rhs.environmentMeasurementEnabled {return false}
if lhs.environmentScreenEnabled != rhs.environmentScreenEnabled {return false}
if lhs.environmentReadErrorCountThreshold != rhs.environmentReadErrorCountThreshold {return false}
if lhs.environmentRecoveryInterval != rhs.environmentRecoveryInterval {return false}
if lhs.environmentDisplayFahrenheit != rhs.environmentDisplayFahrenheit {return false}
if lhs.environmentSensorType != rhs.environmentSensorType {return false}
if lhs.environmentSensorPin != rhs.environmentSensorPin {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

View file

@ -79,6 +79,7 @@ enum PortNum: SwiftProtobuf.Enum {
///
/// Waypoint payloads.
/// Payload is a [Waypoint](/docs/developers/protobufs/api#waypoint) message
case waypointApp // = 8
///

View file

@ -29,49 +29,29 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
/// No external telemetry sensor explicitly set
case notSet // = 0
///
/// Moderate accuracy temperature
case dht11 // = 1
///
/// High accuracy temperature
case ds18B20 // = 2
///
/// Moderate accuracy temperature and humidity
case dht12 // = 3
///
/// Moderate accuracy temperature and humidity
case dht21 // = 4
///
/// Moderate accuracy temperature and humidity
case dht22 // = 5
///
/// High accuracy temperature, pressure, humidity
case bme280 // = 6
case bme280 // = 1
///
/// High accuracy temperature, pressure, humidity, and air resistance
case bme680 // = 7
case bme680 // = 2
///
/// Very high accuracy temperature
case mcp9808 // = 8
///
/// Moderate accuracy temperature and humidity
case shtc3 // = 9
case mcp9808 // = 3
///
/// Moderate accuracy current and voltage
case ina260 // = 10
case ina260 // = 4
///
/// Moderate accuracy current and voltage
case ina219 // = 11
case ina219 // = 5
///
/// High accuracy temperature and pressure
case bmp280 // = 6
case UNRECOGNIZED(Int)
init() {
@ -81,17 +61,12 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .notSet
case 1: self = .dht11
case 2: self = .ds18B20
case 3: self = .dht12
case 4: self = .dht21
case 5: self = .dht22
case 6: self = .bme280
case 7: self = .bme680
case 8: self = .mcp9808
case 9: self = .shtc3
case 10: self = .ina260
case 11: self = .ina219
case 1: self = .bme280
case 2: self = .bme680
case 3: self = .mcp9808
case 4: self = .ina260
case 5: self = .ina219
case 6: self = .bmp280
default: self = .UNRECOGNIZED(rawValue)
}
}
@ -99,17 +74,12 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
var rawValue: Int {
switch self {
case .notSet: return 0
case .dht11: return 1
case .ds18B20: return 2
case .dht12: return 3
case .dht21: return 4
case .dht22: return 5
case .bme280: return 6
case .bme680: return 7
case .mcp9808: return 8
case .shtc3: return 9
case .ina260: return 10
case .ina219: return 11
case .bme280: return 1
case .bme680: return 2
case .mcp9808: return 3
case .ina260: return 4
case .ina219: return 5
case .bmp280: return 6
case .UNRECOGNIZED(let i): return i
}
}
@ -122,17 +92,12 @@ extension TelemetrySensorType: CaseIterable {
// The compiler won't synthesize support with the UNRECOGNIZED case.
static var allCases: [TelemetrySensorType] = [
.notSet,
.dht11,
.ds18B20,
.dht12,
.dht21,
.dht22,
.bme280,
.bme680,
.mcp9808,
.shtc3,
.ina260,
.ina219,
.bmp280,
]
}
@ -285,17 +250,12 @@ extension Telemetry.OneOf_Variant: @unchecked Sendable {}
extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
0: .same(proto: "NotSet"),
1: .same(proto: "DHT11"),
2: .same(proto: "DS18B20"),
3: .same(proto: "DHT12"),
4: .same(proto: "DHT21"),
5: .same(proto: "DHT22"),
6: .same(proto: "BME280"),
7: .same(proto: "BME680"),
8: .same(proto: "MCP9808"),
9: .same(proto: "SHTC3"),
10: .same(proto: "INA260"),
11: .same(proto: "INA219"),
1: .same(proto: "BME280"),
2: .same(proto: "BME680"),
3: .same(proto: "MCP9808"),
4: .same(proto: "INA260"),
5: .same(proto: "INA219"),
6: .same(proto: "BMP280"),
]
}

View file

@ -16,15 +16,12 @@ struct Connect: View {
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
@State private var showingVersionSheet = false
@State var initialLoad: Bool = true
@State var isPreferredRadio: Bool = false
@State var firmwareVersion = "0.0.0"
@State var minimumVersion = "1.3.27"
@State var minimumVersion = "1.3.39"
@State var invalidVersion = false
var body: some View {
@ -267,23 +264,18 @@ struct Connect: View {
InvalidVersion(errorText: "1.3 ALPHA PREVIEW this version of the app supports only version \(minimumVersion) and above. Your device has been disconnected.")
}
.onChange(of: firmwareVersion) { iv in
.onChange(of: (self.bleManager.connectedVersion)) { ic in
bleManager.disconnectPeripheral()
}
.onChange(of: self.bleManager.isConnected) { ic in
firmwareVersion = bleManager.lastConnnectionVersion
firmwareVersion = self.bleManager.connectedVersion
let supportedVersion = firmwareVersion == "0.0.0" || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedSame
invalidVersion = !supportedVersion
if invalidVersion {
bleManager.disconnectPeripheral()
}
}
.onAppear(perform: {

View file

@ -0,0 +1,22 @@
//
// DistanceText.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/19/22.
//
import SwiftUI
import CoreLocation
import MapKit
struct DistanceText: View {
var meters: CLLocationDistance
var body: some View {
let distanceFormatter = MKDistanceFormatter()
Text("Distance: \(distanceFormatter.string(fromDistance: Double(meters)))")
}
}

View file

@ -11,6 +11,8 @@ struct Contacts: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@State var onboarding = true
@FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)],

View file

@ -213,34 +213,41 @@ struct UserMessageList: View {
let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp))
Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray)
Text("Sent \(messageDate, style: .date) \(messageDate.formattedDate(format: "h:mm:ss a"))").font(.caption2).foregroundColor(.gray)
}
VStack {
Text("Received ACK: \(message.receivedACK ? "✔️" : "")")
}
if message.receivedACK {
VStack {
let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp))
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
if ackDate >= sixMonthsAgo! {
Text("ACK \(ackDate, style: .date) \(ackDate, style: .time)").font(.caption2).foregroundColor(.gray)
} else {
Text("Unknown Age").font(.caption2).foregroundColor(.gray)
}
Text("Received Ack \(message.receivedACK ? "✔️" : "")")
}
}
if message.ackError > 0 {
let ackErrorVal = RoutingError(rawValue: Int(message.ackError))
Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true)
}
VStack {
let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp))
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
if ackDate >= sixMonthsAgo! {
Text((ackDate.formattedDate(format: "h:mm:ss a"))).font(.caption2).foregroundColor(.gray)
} else {
Text("Unknown Age").font(.caption2).foregroundColor(.gray)
}
}
if message.ackSNR != 0 {
VStack {
Text("ACK SNR \(String(message.ackSNR))")
Text("Ack SNR \(String(message.ackSNR))")
.font(.caption2)
.foregroundColor(.gray)
}
@ -289,9 +296,21 @@ struct UserMessageList: View {
HStack {
if message.receivedACK {
if currentUser && message.receivedACK {
// Ack Received
Text("Acknowledged").font(.caption2).foregroundColor(.gray)
} else if currentUser && message.ackError == 0 {
// Empty Error
Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.yellow)
} else if currentUser && message.ackError > 0 {
let ackErrorVal = RoutingError(rawValue: Int(message.ackError))
Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true)
.font(.caption2).foregroundColor(.red)
}
}
}
@ -302,9 +321,9 @@ struct UserMessageList: View {
Spacer(minLength:50)
}
}
.id(message.messageId)
.padding([.leading, .trailing])
.frame(maxWidth: .infinity)
.id(message.messageId)
.alert(isPresented: $showDeleteMessageAlert) {
Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."),
primaryButton: .destructive(Text("Delete")) {
@ -443,8 +462,8 @@ struct UserMessageList: View {
focusedField = nil
replyMessageId = 0
if sendPositionWithMessage {
if bleManager.sendPosition(destNum: user.num, wantResponse: false) {
print("Position Sent")
if bleManager.sendLocation(destNum: user.num, wantAck: true) {
print("Location Sent")
}
}
}

View file

@ -22,7 +22,7 @@ struct LocationHistory: View {
List {
ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
ForEach(node.positions!.reversed() as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
VStack {
@ -88,12 +88,9 @@ struct LocationHistory: View {
.font(.subheadline)
.foregroundColor(.accentColor)
.symbolRenderingMode(.hierarchical)
Text("Time:")
.font(.caption)
DateTimeText(dateTime: mappin.time)
.foregroundColor(.gray)
.font(.caption)
}
}
}

View file

@ -102,7 +102,7 @@ struct NodeDetail: View {
) {
Button("Shutdown Node?", role: .destructive) {
if !bleManager.sendShutdown(destNum: node.num, wantResponse: true) {
if !bleManager.sendShutdown(destNum: node.num) {
print("Shutdown Failed")
}
@ -130,7 +130,7 @@ struct NodeDetail: View {
Button("Reboot Node?", role: .destructive) {
if !bleManager.sendReboot(destNum: node.num, wantResponse: true) {
if !bleManager.sendReboot(destNum: node.num) {
print("Reboot Failed")
}

View file

@ -9,6 +9,7 @@
// A view showing a list of devices that have been seen on the mesh network from the perspective of the connected device.
import SwiftUI
import CoreLocation
struct NodeList: View {
@ -19,7 +20,7 @@ struct NodeList: View {
@State var initialLoad: Bool = true
@FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
sortDescriptors: [NSSortDescriptor(key: "user.shortName", ascending: true)],
animation: .default)
private var nodes: FetchedResults<NodeInfoEntity>
@ -35,8 +36,8 @@ struct NodeList: View {
if nodes.count == 0 {
Text("Scan for Radios").font(.largeTitle)
Text("No LoRa Mesh Nodes Found").font(.title2)
Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.")
Text("No Meshtastic Nodes Found").font(.title2)
Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your iPhone, iPad or Mac.")
.font(.body)
Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.")
Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.")
@ -61,7 +62,7 @@ struct NodeList: View {
if UIDevice.current.userInterfaceIdiom == .pad { Text(node.user?.longName ?? "Unknown").font(.headline)
.offset(x: -15)
} else {
Text(node.user?.longName ?? "Unknown").font(.title).offset(x: -15)
Text(node.user?.longName ?? "Unknown").font(.title2).offset(x: -15)
}
}
.padding(.bottom, 10)
@ -80,21 +81,49 @@ struct NodeList: View {
Text("Currently Connected").font(.title3).foregroundColor(Color.accentColor)
}
}
Spacer()
}
Spacer()
HStack(alignment: .bottom) {
Image(systemName: "clock.badge.checkmark.fill").font(.title3)
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
LastHeardText(lastHeard: node.lastHeard).font(.subheadline).foregroundColor(.gray)
} else {
LastHeardText(lastHeard: node.lastHeard).font(.title3).foregroundColor(.gray)
}
}
if node.positions?.count ?? 0 > 0 && (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num != node.num) {
Spacer()
HStack(alignment: .bottom) {
let lastPostion = node.positions!.reversed()[0] as! PositionEntity
let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)
let nodeCoord = CLLocation(latitude: lastPostion.coordinate!.latitude, longitude: lastPostion.coordinate!.longitude)
let metersAway = nodeCoord.distance(from: myCoord)
Image(systemName: "lines.measurement.horizontal").font(.title3)
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
DistanceText(meters: metersAway).font(.subheadline).foregroundColor(.gray)
} else {
DistanceText(meters: metersAway).font(.title3).foregroundColor(.gray)
}
}
}
}
.padding([.leading, .top, .bottom])
}
@ -106,16 +135,9 @@ struct NodeList: View {
if initialLoad {
self.bleManager.context = context
self.bleManager.userSettings = userSettings
self.bleManager.context = context
self.initialLoad = false
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
if nodes.count > 0 {
selection = "0"
}
}
}
}
}

View file

@ -20,7 +20,7 @@ struct TelemetryLog: View {
List {
ForEach(node.telemetries!.array as! [TelemetryEntity], id: \.self) { (tel: TelemetryEntity) in
ForEach(node.telemetries!.reversed() as! [TelemetryEntity], id: \.self) { (tel: TelemetryEntity) in
VStack (alignment: .leading) {
@ -86,14 +86,10 @@ struct TelemetryLog: View {
Text("Environment Metrics")
.font(.title)
let sensor = SensorTypes(rawValue: Int(node.telemetryConfig?.environmentSensorType ?? 0))
let tempReadingType = (!(node.telemetryConfig?.environmentDisplayFahrenheit ?? true)) ? "°C" : "°F"
if sensor == SensorTypes.bme280 ||
sensor == SensorTypes.bme680 ||
sensor == SensorTypes.shtc3 ||
sensor == SensorTypes.mcp9808 {
if tel.temperature > 0 {
Image(systemName: "thermometer")
.font(.callout)
@ -104,9 +100,7 @@ struct TelemetryLog: View {
.font(.callout)
}
if sensor == SensorTypes.bme280 ||
sensor == SensorTypes.bme680 ||
sensor == SensorTypes.shtc3 {
if tel.relativeHumidity > 0 {
Image(systemName: "humidity")
.font(.callout)
@ -117,8 +111,7 @@ struct TelemetryLog: View {
.font(.callout)
}
if sensor == SensorTypes.bme280 ||
sensor == SensorTypes.bme680 {
if tel.barometricPressure > 0 {
Image(systemName: "barometer")
.font(.callout)
@ -129,7 +122,7 @@ struct TelemetryLog: View {
.font(.callout)
}
if sensor == SensorTypes.bme680 {
if tel.gasResistance > 0 {
Image(systemName: "aqi.medium")
.font(.callout)
@ -140,8 +133,7 @@ struct TelemetryLog: View {
.font(.callout)
}
if sensor == SensorTypes.ina219 ||
sensor == SensorTypes.ina260 {
if tel.current > 0 {
Image(systemName: "directcurrent")
.font(.callout)
@ -185,7 +177,7 @@ struct TelemetryLog: View {
Spacer()
Text("Device Metrics")
.font(.title)
.font(.title3)
Spacer()
}
@ -242,8 +234,6 @@ struct TelemetryLog: View {
} else if tel.metricsType == 1 {
// Environment Metrics
let sensor = SensorTypes(rawValue: Int(node.telemetryConfig?.environmentSensorType ?? 0))
let tempReadingType = (!(node.telemetryConfig?.environmentDisplayFahrenheit ?? true)) ? "°C" : "°F"
@ -260,10 +250,7 @@ struct TelemetryLog: View {
HStack {
if sensor == SensorTypes.bme280 ||
sensor == SensorTypes.bme680 ||
sensor == SensorTypes.shtc3 ||
sensor == SensorTypes.mcp9808 {
if tel.temperature > 0 {
Image(systemName: "thermometer")
.font(.callout)
@ -277,9 +264,7 @@ struct TelemetryLog: View {
HStack {
if sensor == SensorTypes.bme280 ||
sensor == SensorTypes.bme680 ||
sensor == SensorTypes.shtc3 {
if tel.relativeHumidity > 0 {
Image(systemName: "humidity")
.font(.callout)
@ -291,8 +276,7 @@ struct TelemetryLog: View {
}
}
if sensor == SensorTypes.ina219 ||
sensor == SensorTypes.ina260 {
if tel.current > 0 {
HStack {
@ -317,8 +301,7 @@ struct TelemetryLog: View {
}
}
if sensor == SensorTypes.bme280 ||
sensor == SensorTypes.bme680 {
if tel.barometricPressure > 0 {
HStack {
@ -332,7 +315,7 @@ struct TelemetryLog: View {
}
}
if sensor == SensorTypes.bme680 {
if tel.gasResistance > 0 {
HStack {

View file

@ -0,0 +1,26 @@
//
// Onboarding.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 8/21/22.
//
import SwiftUI
struct Onboarding: View {
var body: some View {
VStack {
Text("🗺️ Set Your Region to Mesh and Message")
.font(.largeTitle)
.foregroundColor(.red)
Text("Your region is currently set to UNSET, please set your device to the appropriate region under Settings > LoRa, after you set your region your Meshtastic device will reboot.")
.font(.callout)
.padding()
}
}
}

View file

@ -0,0 +1,167 @@
//
// BluetoothConfig.swift
// Meshtastic Apple
//
// Copyright (c) Garth Vander Houwen 8/18/22.
//
import SwiftUI
struct BluetoothConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
var node: NodeInfoEntity?
@State private var isPresentingSaveConfirm: Bool = false
@State var initialLoad: Bool = true
@State var hasChanges = false
@State var enabled = true
/// Determines the pairing strategy for the device
@State var mode = 0
/// Specified pin for PairingMode.FixedPin
@State var fixedPin = "123456"
let numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .none
return formatter
}()
var body: some View {
VStack {
Form {
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("Enabled", systemImage: "antenna.radiowaves.left.and.right")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Picker("Pairing Mode", selection: $mode ) {
ForEach(BluetoothModes.allCases) { bm in
Text(bm.description)
}
}
.pickerStyle(DefaultPickerStyle())
if mode == 1 {
HStack {
Label("Fixed PIN", systemImage: "wallet.pass")
TextField("Fixed PIN", text: $fixedPin)
.foregroundColor(.gray)
.onChange(of: fixedPin, perform: { value in
let digitCount = fixedPin.utf8.count
// Only mess with the value if it is too big
if digitCount > 6 || digitCount < 6 {
fixedPin = "123456"
}
if digitCount < 6 {
fixedPin = "123456"
}
})
.foregroundColor(.gray)
}
.keyboardType(.decimalPad)
}
}
}
.disabled(bleManager.connectedPeripheral == nil)
Button {
isPresentingSaveConfirm = true
} label: {
Label("Save", systemImage: "square.and.arrow.down")
}
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.confirmationDialog(
"Are you sure?",
isPresented: $isPresentingSaveConfirm
) {
Button("Save Bluetooth Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
var bc = Config.BluetoothConfig()
bc.enabled = enabled
bc.mode = BluetoothModes(rawValue: mode)?.protoEnumValue() ?? Config.BluetoothConfig.PairingMode.randomPin
bc.fixedPin = UInt32(fixedPin) ?? 123456
let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
hasChanges = false
} else {
}
}
}
}
.navigationTitle("Display Config")
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
})
.onAppear {
if self.initialLoad{
self.bleManager.context = context
self.enabled = node!.bluetoothConfig?.enabled ?? true
self.mode = Int(node!.bluetoothConfig?.mode ?? 0)
//self.fixedPin = (String(node!.bluetoothConfig?.fixedPin) ?? "123456")
self.hasChanges = false
self.initialLoad = false
}
}
.onChange(of: enabled) { newEnabled in
if node != nil && node!.bluetoothConfig != nil {
if newEnabled != node!.bluetoothConfig!.enabled { hasChanges = true }
}
}
.onChange(of: mode) { newMode in
if node != nil && node!.bluetoothConfig != nil {
if newMode != node!.bluetoothConfig!.mode { hasChanges = true }
}
}
.onChange(of: fixedPin) { newFixedPin in
if node != nil && node!.bluetoothConfig != nil {
if newFixedPin != String(node!.bluetoothConfig!.fixedPin) { hasChanges = true }
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -6,46 +6,6 @@
//
import SwiftUI
// Default of 0 is Client
enum DeviceRoles: Int, CaseIterable, Identifiable {
case client = 0
case clientMute = 1
case router = 2
case routerClient = 3
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .client:
return "Client (default) - App connected client."
case .clientMute:
return "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 "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."
case .routerClient:
return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client."
}
}
}
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
}
}
}
struct DeviceConfig: View {
@Environment(\.managedObjectContext) var context
@ -124,7 +84,7 @@ struct DeviceConfig: View {
dc.serialDisabled = !serialEnabled
dc.debugLogEnabled = debugLogEnabled
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {
@ -153,7 +113,7 @@ struct DeviceConfig: View {
) {
Button("Erase all device settings?", role: .destructive) {
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num, wantResponse: true) {
if !bleManager.sendFactoryReset(destNum: bleManager.connectedPeripheral.num) {
print("Factory Reset Failed")
}

View file

@ -7,119 +7,6 @@
import SwiftUI
enum GpsFormats: Int, CaseIterable, Identifiable {
case gpsFormatDec = 0
case gpsFormatDms = 1
case gpsFormatUtm = 2
case gpsFormatMgrs = 3
case gpsFormatOlc = 4
case gpsFormatOsgr = 5
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .gpsFormatDec:
return "Decimal Degrees Format"
case .gpsFormatDms:
return "Degrees Minutes Seconds"
case .gpsFormatUtm:
return "Universal Transverse Mercator"
case .gpsFormatMgrs:
return "Military Grid Reference System"
case .gpsFormatOlc:
return "Open Location Code (aka Plus Codes)"
case .gpsFormatOsgr:
return "Ordnance Survey Grid Reference"
}
}
}
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
switch self {
case .gpsFormatDec:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDec
case .gpsFormatDms:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatDms
case .gpsFormatUtm:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatUtm
case .gpsFormatMgrs:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatMgrs
case .gpsFormatOlc:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOlc
case .gpsFormatOsgr:
return Config.DisplayConfig.GpsCoordinateFormat.gpsFormatOsgr
}
}
}
// Default of 0 is One Minute
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 0
case fifteenMinutes = 900
case thirtyMinutes = 1800
case oneHour = 3600
case max = 31536000 // One Year
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
case .thirtyMinutes:
return "Thirty Minutes"
case .oneHour:
return "One Hour"
case .max:
return "Always On"
}
}
}
}
// Default of 0 is off
enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
case off = 0
case thirtySeconds = 30
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .off:
return "Off"
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
}
}
}
}
struct DisplayConfig: View {
@Environment(\.managedObjectContext) var context
@ -134,13 +21,14 @@ struct DisplayConfig: View {
@State var screenOnSeconds = 0
@State var screenCarouselInterval = 0
@State var gpsFormat = 0
@State var compassNorthTop = false
var body: some View {
VStack {
Form {
Section(header: Text("Timing")) {
Section(header: Text("Device Screen")) {
Picker("Screen on for", selection: $screenOnSeconds ) {
ForEach(ScreenOnIntervals.allCases) { soi in
@ -161,8 +49,15 @@ struct DisplayConfig: View {
Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.")
.font(.caption)
.listRowSeparator(.visible)
Toggle(isOn: $compassNorthTop) {
Label("Always point north", systemImage: "location.north.circle")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Text("The compass heading on the screen outside of the circle will always point north.")
.font(.caption)
}
Section(header: Text("Format")) {
Picker("GPS Format", selection: $gpsFormat ) {
@ -203,8 +98,9 @@ struct DisplayConfig: View {
dc.gpsFormat = GpsFormats(rawValue: gpsFormat)!.protoEnumValue()
dc.screenOnSecs = UInt32(screenOnSeconds)
dc.autoScreenCarouselSecs = UInt32(screenCarouselInterval)
dc.compassNorthTop = compassNorthTop
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveDisplayConfig(config: dc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {
@ -234,6 +130,7 @@ struct DisplayConfig: View {
self.gpsFormat = Int(node!.displayConfig?.gpsFormat ?? 0)
self.screenOnSeconds = Int(node!.displayConfig?.screenOnSeconds ?? 0)
self.screenCarouselInterval = Int(node!.displayConfig?.screenCarouselInterval ?? 0)
self.compassNorthTop = node!.displayConfig?.compassNorthTop ?? false
self.hasChanges = false
self.initialLoad = false
}
@ -252,6 +149,13 @@ struct DisplayConfig: View {
if newCarouselSecs != node!.displayConfig!.screenCarouselInterval { hasChanges = true }
}
}
.onChange(of: compassNorthTop) { newCompassNorthTop in
if node != nil && node!.displayConfig != nil {
if newCompassNorthTop != node!.displayConfig!.compassNorthTop { hasChanges = true }
}
}
.onChange(of: gpsFormat) { newGpsFormat in
if node != nil && node!.displayConfig != nil {

View file

@ -7,179 +7,6 @@
import SwiftUI
enum RegionCodes : Int, CaseIterable, Identifiable {
case unset = 0
case us = 1
case eu433 = 2
case eu868 = 3
case cn = 4
case jp = 5
case anz = 6
case kr = 7
case tw = 8
case ru = 9
case `in` = 10
case nz865 = 11
case th = 12
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"
}
}
}
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
}
}
}
enum ModemPresets : Int, CaseIterable, Identifiable {
case LongFast = 0
case LongSlow = 1
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 .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 .VLongSlow:
return Config.LoRaConfig.ModemPreset.vlongSlow
case .MedSlow:
return Config.LoRaConfig.ModemPreset.medSlow
case .MedFast:
return Config.LoRaConfig.ModemPreset.medFast
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 = 0
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"
}
}
}
}
struct LoRaConfig: View {
@Environment(\.managedObjectContext) var context
@ -261,7 +88,7 @@ struct LoRaConfig: View {
lc.region = RegionCodes(rawValue: region)!.protoEnumValue()
lc.modemPreset = ModemPresets(rawValue: modemPreset)!.protoEnumValue()
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveLoRaConfig(config: lc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {

View file

@ -241,7 +241,7 @@ struct CannedMessagesConfig: View {
Section(header: Text("Key Mapping")) {
Picker("inputbrokerEventCw", selection: $inputbrokerEventCw ) {
Picker("Clockwise Rotary Event", selection: $inputbrokerEventCw ) {
ForEach(InputEventChars.allCases) { iec in
Text(iec.description)
}
@ -250,7 +250,7 @@ struct CannedMessagesConfig: View {
.padding(.top, 10)
.padding(.bottom, 10)
Picker("inputbrokerEventCcw", selection: $inputbrokerEventCcw ) {
Picker("Counter Clockwise Rotary Event", selection: $inputbrokerEventCcw ) {
ForEach(InputEventChars.allCases) { iec in
Text(iec.description)
}
@ -259,7 +259,7 @@ struct CannedMessagesConfig: View {
.padding(.top, 10)
.padding(.bottom, 10)
Picker("inputBrokerEventPress", selection: $inputbrokerEventPress ) {
Picker("Encoder Press Event", selection: $inputbrokerEventPress ) {
ForEach(InputEventChars.allCases) { iec in
Text(iec.description)
}
@ -320,7 +320,7 @@ struct CannedMessagesConfig: View {
cmc.inputbrokerEventCcw = InputEventChars(rawValue: inputbrokerEventCcw)!.protoEnumValue()
cmc.inputbrokerEventPress = InputEventChars(rawValue: inputbrokerEventPress)!.protoEnumValue()
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveCannedMessageModuleConfig(config: cmc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {
// Should show a saved successfully alert once I know that to be true
@ -376,6 +376,9 @@ struct CannedMessagesConfig: View {
// RAK Rotary Encoder
updown1Enabled = true
rotary1Enabled = false
inputbrokerPinA = 4
inputbrokerPinB = 10
inputbrokerPinPress = 3
inputbrokerEventCw = InputEventChars.keyUp.rawValue
inputbrokerEventCcw = InputEventChars.keyDown.rawValue
inputbrokerEventPress = InputEventChars.keySelect.rawValue

View file

@ -139,7 +139,7 @@ struct ExternalNotificationConfig: View {
Label("Save", systemImage: "square.and.arrow.down")
}
.disabled(bleManager.connectedPeripheral == nil || !hasChanges || !(node!.myInfo?.hasWifi ?? false))
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
@ -159,7 +159,7 @@ struct ExternalNotificationConfig: View {
enc.output = UInt32(output)
enc.outputMs = UInt32(outputMilliseconds)
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0{

View file

@ -113,7 +113,7 @@ struct RangeTestConfig: View {
rtc.save = save
rtc.sender = UInt32(sender)
let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveRangeTestModuleConfig(config: rtc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {

View file

@ -303,7 +303,7 @@ struct SerialConfig: View {
sc.timeout = UInt32(timeout)
sc.mode = SerialModeTypes(rawValue: mode)!.protoEnumValue()
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveSerialModuleConfig(config: sc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {

View file

@ -6,74 +6,6 @@
//
import SwiftUI
enum SensorTypes: Int, CaseIterable, Identifiable {
/// No external telemetry sensor explicitly set
case notSet = 0
/// High accuracy temperature, pressure, humidity
case bme280 = 6
/// High accuracy temperature, pressure, humidity, and air resistance
case bme680 = 7
/// Very high accuracy temperature
case mcp9808 = 8
/// Moderate accuracy temperature and humidity
case shtc3 = 9
/// Moderate accuracy current and voltage
case ina260 = 10
/// Moderate accuracy current and voltage
case ina219 = 11
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .notSet:
return "Not Set"
case .bme280:
return "BME280 - Temp, pressure & humidity"
case .bme680:
return "BME680 - Temp, pressure, humidity & air resistance"
case .mcp9808:
return "MCP9808 - Temperature"
case .shtc3:
return "SHTC3 - Temperature & humidity"
case .ina260:
return "INA260 - Current & voltage"
case .ina219:
return "INA219 - Current & voltage"
}
}
}
func protoEnumValue() -> TelemetrySensorType {
switch self {
case .notSet:
return TelemetrySensorType.notSet
case .bme280:
return TelemetrySensorType.bme280
case .bme680:
return TelemetrySensorType.bme680
case .mcp9808:
return TelemetrySensorType.mcp9808
case .shtc3:
return TelemetrySensorType.shtc3
case .ina260:
return TelemetrySensorType.ina260
case .ina219:
return TelemetrySensorType.ina219
}
}
}
// Default of 0 is off
enum ErrorRecoveryIntervals: Int, CaseIterable, Identifiable {
@ -188,11 +120,8 @@ struct TelemetryConfig: View {
@State var deviceUpdateInterval = 0
@State var environmentUpdateInterval = 0
@State var environmentMeasurementEnabled = false
@State var environmentSensorType = 0
@State var environmentScreenEnabled = false
@State var environmentDisplayFahrenheit = false
@State var environmentRecoveryInterval = 0
@State var environmentReadErrorCountThreshold = 0
var body: some View {
@ -222,6 +151,9 @@ struct TelemetryConfig: View {
}
Section(header: Text("Sensor Options")) {
Text("I2C Connected sensors will be detected automatically. Supported sensors are BMP280, BME280, BME680, MCP9808, INA219 and INA260.")
.font(.caption)
Toggle(isOn: $environmentMeasurementEnabled) {
@ -229,13 +161,6 @@ struct TelemetryConfig: View {
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Picker("Sensor", selection: $environmentSensorType ) {
ForEach(SensorTypes.allCases) { st in
Text(st.description)
}
}
.pickerStyle(DefaultPickerStyle())
Toggle(isOn: $environmentScreenEnabled) {
Label("Show on device screen", systemImage: "display")
@ -247,37 +172,6 @@ struct TelemetryConfig: View {
Label("Display Fahrenheit", systemImage: "thermometer")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
Section(header: Text("Errors")) {
Picker("Error Count Threshold", selection: $environmentReadErrorCountThreshold) {
ForEach(0..<101) {
if $0 == 0 {
Text("Unset")
} else if $0 % 5 == 0 {
Text("\($0)")
}
}
}
.pickerStyle(DefaultPickerStyle())
Text("Sometimes sensor reads can fail. If this happens, we will retry a configurable number of attempts, each attempt will be delayed by the minimum required refresh rate for that sensor")
.font(.caption)
Picker("Error Recovery Interval", selection: $environmentRecoveryInterval ) {
ForEach(ErrorRecoveryIntervals.allCases) { eri in
Text(eri.description)
}
}
.pickerStyle(DefaultPickerStyle())
Text("Sometimes we can end up with more failures than our error count threshold. In this case, we will stop trying to read from the sensor for a while. Wait this long until trying to read from the sensor again")
.font(.caption)
}
}
.disabled(bleManager.connectedPeripheral == nil)
@ -306,13 +200,10 @@ struct TelemetryConfig: View {
tc.deviceUpdateInterval = UInt32(deviceUpdateInterval)
tc.environmentUpdateInterval = UInt32(environmentUpdateInterval)
tc.environmentMeasurementEnabled = environmentMeasurementEnabled
tc.environmentSensorType = SensorTypes(rawValue: environmentSensorType)!.protoEnumValue()
tc.environmentScreenEnabled = environmentScreenEnabled
tc.environmentDisplayFahrenheit = environmentDisplayFahrenheit
tc.environmentRecoveryInterval = UInt32(environmentRecoveryInterval)
tc.environmentReadErrorCountThreshold = UInt32(environmentReadErrorCountThreshold)
let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveTelemetryModuleConfig(config: tc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {
@ -338,16 +229,11 @@ struct TelemetryConfig: View {
if self.initialLoad{
self.bleManager.context = context
self.deviceUpdateInterval = Int(node!.telemetryConfig?.deviceUpdateInterval ?? 0)
self.environmentUpdateInterval = Int(node!.telemetryConfig?.environmentUpdateInterval ?? 0)
self.environmentMeasurementEnabled = node!.telemetryConfig?.environmentMeasurementEnabled ?? false
self.environmentSensorType = Int(node!.telemetryConfig?.environmentSensorType ?? 0)
self.environmentScreenEnabled = node!.telemetryConfig?.environmentScreenEnabled ?? false
self.environmentDisplayFahrenheit = node!.telemetryConfig?.environmentDisplayFahrenheit ?? false
self.environmentRecoveryInterval = Int(node!.telemetryConfig?.environmentRecoveryInterval ?? 0)
self.environmentReadErrorCountThreshold = Int(node!.telemetryConfig?.environmentReadErrorCountThreshold ?? 0)
self.hasChanges = false
self.initialLoad = false
}
@ -373,13 +259,6 @@ struct TelemetryConfig: View {
if newEnvEnabled != node!.telemetryConfig!.environmentMeasurementEnabled { hasChanges = true }
}
}
.onChange(of: environmentSensorType) { newEnvSensorType in
if node != nil && node!.telemetryConfig != nil {
if newEnvSensorType != node!.telemetryConfig!.environmentSensorType { hasChanges = true }
}
}
.onChange(of: environmentScreenEnabled) { newEnvScreenEnabled in
if node!.telemetryConfig != nil {
@ -394,20 +273,6 @@ struct TelemetryConfig: View {
if newEnvDisplayF != node!.telemetryConfig!.environmentDisplayFahrenheit { hasChanges = true }
}
}
.onChange(of: environmentRecoveryInterval) { newEnvRecoveryInterval in
if node != nil && node!.telemetryConfig != nil {
if newEnvRecoveryInterval != node!.telemetryConfig!.environmentRecoveryInterval { hasChanges = true }
}
}
.onChange(of: environmentReadErrorCountThreshold) { newEnvReadErrorCountThreshold in
if node != nil && node!.telemetryConfig != nil {
if newEnvReadErrorCountThreshold != node!.telemetryConfig!.environmentReadErrorCountThreshold { hasChanges = true }
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -7,71 +7,6 @@
import SwiftUI
enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
case thirtySeconds = 0
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
case thirtyMinutes = 1800
case oneHour = 3600
case maxInt32 = 2147483647
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
case .thirtyMinutes:
return "Thirty Minutes"
case .oneHour:
return "One Hour"
case .maxInt32:
return "On Boot Only"
}
}
}
}
enum GpsAttemptTimes: Int, CaseIterable, Identifiable {
case thirtySeconds = 0
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
case fifteenMinutes = 900
var id: Int { self.rawValue }
var description: String {
get {
switch self {
case .thirtySeconds:
return "Thirty Seconds"
case .oneMinute:
return "One Minute"
case .fiveMinutes:
return "Five Minutes"
case .tenMinutes:
return "Ten Minutes"
case .fifteenMinutes:
return "Fifteen Minutes"
}
}
}
}
enum PositionBroadcastIntervals: Int, CaseIterable, Identifiable {
case thirtySeconds = 30
@ -323,7 +258,7 @@ struct PositionConfig: View {
pc.gpsAttemptTime = UInt32(gpsAttemptTime)
pc.positionBroadcastSecs = UInt32(positionBroadcastSeconds)
let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.savePositionConfig(config: pc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0{
@ -370,6 +305,13 @@ struct PositionConfig: View {
if newSmartPosition != node!.positionConfig!.smartPositionEnabled { hasChanges = true }
}
}
.onChange(of: positionBroadcastSeconds) { newPositionBroadcastSeconds in
if node != nil && node!.positionConfig != nil {
if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true }
}
}
.onChange(of: deviceGpsEnabled) { newDeviceGps in
if node != nil && node!.positionConfig != nil {

View file

@ -0,0 +1,202 @@
//
// WiFiConfig.swift
// Meshtastic
//
// Copyright (c) Garth Vander Houwen 8/1/2022
//
import SwiftUI
struct WiFiConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
var node: NodeInfoEntity?
@State private var isPresentingSaveConfirm: Bool = false
@State var initialLoad: Bool = true
@State var hasChanges: Bool = false
@State var enabled = false
@State var ssid = ""
@State var password = ""
@State var mode = 0
var body: some View {
VStack {
Form {
Text("Enabling WiFi will disable the bluetooth connection to the app.")
.font(.title3)
Section(header: Text("Options")) {
Toggle(isOn: $enabled) {
Label("Enabled", systemImage: "wifi")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Picker("Mode", selection: $mode ) {
ForEach(WiFiModes.allCases) { lu in
Text(lu.description)
}
}
.pickerStyle(DefaultPickerStyle())
}
Section(header: Text("SSID & Password")) {
HStack {
Label("SSID", systemImage: "network")
TextField("SSID", text: $ssid)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: ssid, perform: { value in
let totalBytes = ssid.utf8.count
// Only mess with the value if it is too big
if totalBytes > 32 {
let firstNBytes = Data(ssid.utf8.prefix(32))
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
// Set the shortName back to the last place where it was the right size
ssid = maxBytesString
}
}
})
.foregroundColor(.gray)
}
.keyboardType(.default)
HStack {
Label("Password", systemImage: "wallet.pass")
TextField("Password", text: $password)
.foregroundColor(.gray)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: password, perform: { value in
let totalBytes = password.utf8.count
// Only mess with the value if it is too big
if totalBytes > 63 {
let firstNBytes = Data(password.utf8.prefix(63))
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
}
}
})
.foregroundColor(.gray)
}
.keyboardType(.default)
}
}
.disabled(!(node != nil && node!.myInfo?.hasWifi ?? false))
Button {
isPresentingSaveConfirm = true
} label: {
Label("Save", systemImage: "square.and.arrow.down")
}
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.confirmationDialog(
"Are you sure?",
isPresented: $isPresentingSaveConfirm
) {
Button("Save WiFI Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
var wifi = Config.WiFiConfig()
wifi.enabled = self.enabled
wifi.ssid = self.ssid
wifi.psk = self.password
wifi.mode = WiFiModes(rawValue: self.mode)?.protoEnumValue() ?? WiFiModes.client.protoEnumValue()
let adminMessageId = bleManager.saveWiFiConfig(config: wifi, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
self.hasChanges = false
} else {
}
}
}
}
.navigationTitle("WiFi Config")
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
})
.onAppear {
if self.initialLoad{
self.bleManager.context = context
self.enabled = (node!.wiFiConfig?.enabled ?? false)
self.ssid = node!.wiFiConfig?.ssid ?? ""
self.password = node!.wiFiConfig?.password ?? ""
self.mode = Int(node!.wiFiConfig?.mode ?? 0)
self.hasChanges = false
self.initialLoad = false
}
}
.onChange(of: enabled) { newEnabled in
if node != nil && node!.wiFiConfig != nil {
if newEnabled != node!.wiFiConfig!.enabled { hasChanges = true }
}
}
.onChange(of: ssid) { newSsid in
if node != nil && node!.wiFiConfig != nil {
if newSsid != node!.wiFiConfig!.ssid { hasChanges = true }
}
}
.onChange(of: password) { newPassword in
if node != nil && node!.wiFiConfig != nil {
if newPassword != node!.wiFiConfig!.password { hasChanges = true }
}
}
.onChange(of: mode) { newMode in
if node != nil && node!.wiFiConfig != nil {
if newMode != node!.wiFiConfig!.mode { hasChanges = true }
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

View file

@ -69,6 +69,18 @@ struct Settings: View {
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink() {
BluetoothConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
} label: {
Image(systemName: "antenna.radiowaves.left.and.right")
.symbolRenderingMode(.hierarchical)
Text("Bluetooth (BLE)")
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink {
DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
} label: {
@ -100,6 +112,17 @@ struct Settings: View {
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink {
WiFiConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
} label: {
Image(systemName: "wifi")
.symbolRenderingMode(.hierarchical)
Text("WiFi (ESP32 Only)")
}
.disabled(bleManager.connectedPeripheral == nil)
Text("Default settings values are prefered as they consume no bandwidth when sent over the mesh.")
.font(.caption2)
.fixedSize(horizontal: false, vertical: true)
@ -193,8 +216,7 @@ struct Settings: View {
// Not Implemented:
// Store Forward Config - Not Working, TBEAM Only
// WiFi Config - Would break connection to device
// MQTT Config - Part of WiFi
// MQTT Config - Can do from WebUI once WiFi is enabled
}
.onAppear {

View file

@ -70,6 +70,7 @@ struct ShareChannel: View {
)
Spacer()
Text("Channel Name (Long/Slow)").font(.title)
Text(String(node!.myInfo!.maxChannels))
Spacer()
}
.frame(width: bounds.size.width, height: bounds.size.height)

View file

@ -48,6 +48,17 @@ struct UserConfig: View {
}
}
})
}
.keyboardType(.default)
.disableAutocorrection(true)
Text("Long name can be up to 36 bytes long.")
.font(.caption)
HStack {
Label("Short Name", systemImage: "circlebadge.fill")
TextField("Long Name", text: $shortName)
.foregroundColor(.gray)
.onChange(of: shortName, perform: { value in
let totalBytes = shortName.utf8.count
@ -66,16 +77,6 @@ struct UserConfig: View {
})
.foregroundColor(.gray)
}
.keyboardType(.default)
.disableAutocorrection(true)
Text("Long name can be up to 36 bytes long.")
.font(.caption)
HStack {
Label("Short Name", systemImage: "circlebadge.fill")
TextField("Long Name", text: $shortName)
.foregroundColor(.gray)
}
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
Text("The short name is used in maps and messaging and will be appended to the last 4 of the device MAC address to set the device's BLE Name. It can be up to 4 bytes long.")
@ -112,7 +113,7 @@ struct UserConfig: View {
u.shortName = shortName
u.longName = longName
let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!, wantResponse: true)
let adminMessageId = bleManager.saveUser(config: u, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0 {

View file

@ -15,7 +15,7 @@ fi
pdir=$(realpath "../Meshtastic-protobufs")
sdir=$(realpath "./Meshtastic/Protobufs")
echo "pdir:$pdir sdir:$sdir"
pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto config.proto deviceonly.proto localonly.proto mesh.proto module_config.proto mqtt.proto portnums.proto remote_hardware.proto
pfiles="admin.proto apponly.proto cannedmessages.proto channel.proto config.proto device_metadata.proto deviceonly.proto localonly.proto mesh.proto module_config.proto mqtt.proto portnums.proto remote_hardware.proto
storeforward.proto telemetry.proto"
for pf in $pfiles
do