mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #715 from meshtastic/Logging_View
OS Log Logging view
This commit is contained in:
commit
bf7d57a1eb
35 changed files with 2239 additions and 245 deletions
|
|
@ -7,7 +7,6 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
25183D462C0A6D97001E31D5 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25183D452C0A6D97001E31D5 /* Logger.swift */; };
|
||||
6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */; };
|
||||
6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */; };
|
||||
6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; };
|
||||
|
|
@ -33,7 +32,6 @@
|
|||
DD0E20FD2B87090400F2D100 /* clientonly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0E20FA2B87090400F2D100 /* clientonly.pb.swift */; };
|
||||
DD0E20FE2B87090400F2D100 /* paxcount.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0E20FB2B87090400F2D100 /* paxcount.pb.swift */; };
|
||||
DD0E21012B8A6F1300F2D100 /* DeviceHardware.json in Resources */ = {isa = PBXBuildFile; fileRef = DD0E21002B8A6BC500F2D100 /* DeviceHardware.json */; };
|
||||
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; };
|
||||
DD13AA492AB73BF400BA0C98 /* PositionPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD13AA482AB73BF400BA0C98 /* PositionPopover.swift */; };
|
||||
DD15E4F32B8BA56E00654F61 /* PaxCounterConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD15E4F22B8BA56E00654F61 /* PaxCounterConfig.swift */; };
|
||||
DD15E4F52B8BFC8E00654F61 /* PaxCounterLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD15E4F42B8BFC8E00654F61 /* PaxCounterLog.swift */; };
|
||||
|
|
@ -168,6 +166,10 @@
|
|||
DDCDC6CB29481FCC004C1DDA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDCDC6CD29481FCC004C1DDA /* Localizable.strings */; };
|
||||
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; };
|
||||
DDD43FE32A78C8900083A3E9 /* MqttClientProxyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD43FE22A78C8900083A3E9 /* MqttClientProxyManager.swift */; };
|
||||
DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD5BB082C285DDC007E03CA /* AppLog.swift */; };
|
||||
DDD5BB0B2C285E45007E03CA /* LogDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD5BB0A2C285E45007E03CA /* LogDetail.swift */; };
|
||||
DDD5BB0D2C285F00007E03CA /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD5BB0C2C285F00007E03CA /* Logger.swift */; };
|
||||
DDD5BB102C285FB3007E03CA /* AppLogFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD5BB0F2C285FB3007E03CA /* AppLogFilter.swift */; };
|
||||
DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD6EEAE29BC024700383354 /* Firmware.swift */; };
|
||||
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; };
|
||||
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; };
|
||||
|
|
@ -245,7 +247,6 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
25183D452C0A6D97001E31D5 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||
6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticAppDelegate.swift; sourceTree = "<group>"; };
|
||||
6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectionSensorLog.swift; sourceTree = "<group>"; };
|
||||
6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -274,7 +275,6 @@
|
|||
DD0E20FF2B892E1300F2D100 /* MeshtasticDataModelV 28.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 28.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD0E21002B8A6BC500F2D100 /* DeviceHardware.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DeviceHardware.json; sourceTree = "<group>"; };
|
||||
DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = "<group>"; };
|
||||
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = "<group>"; };
|
||||
DD13AA482AB73BF400BA0C98 /* PositionPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionPopover.swift; sourceTree = "<group>"; };
|
||||
DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV15.xcdatamodel; sourceTree = "<group>"; };
|
||||
DD15E4F22B8BA56E00654F61 /* PaxCounterConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaxCounterConfig.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -443,6 +443,11 @@
|
|||
DDD28D372C0CD2670063CFA3 /* MeshtasticDataModelV 37.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 37.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DDD3BBD4292D763200D609B3 /* MeshtasticTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeshtasticTests.swift; sourceTree = "<group>"; };
|
||||
DDD43FE22A78C8900083A3E9 /* MqttClientProxyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MqttClientProxyManager.swift; sourceTree = "<group>"; };
|
||||
DDD5BB082C285DDC007E03CA /* AppLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLog.swift; sourceTree = "<group>"; };
|
||||
DDD5BB0A2C285E45007E03CA /* LogDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDetail.swift; sourceTree = "<group>"; };
|
||||
DDD5BB0C2C285F00007E03CA /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||
DDD5BB0F2C285FB3007E03CA /* AppLogFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogFilter.swift; sourceTree = "<group>"; };
|
||||
DDD5BB142C28680D007E03CA /* MeshtasticDataModelV 38.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 38.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DDD6EEAE29BC024700383354 /* Firmware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firmware.swift; sourceTree = "<group>"; };
|
||||
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = "<group>"; };
|
||||
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -606,9 +611,9 @@
|
|||
DD4A911C2708C57100501B7E /* Settings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDD5BB0E2C285F92007E03CA /* Logs */,
|
||||
DD93800C2BA74CE3008BEC06 /* Channels */,
|
||||
DD97E96728EFE9A00056DDA4 /* About.swift */,
|
||||
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */,
|
||||
DD4A911D2708C65400501B7E /* AppSettings.swift */,
|
||||
DDAB580C2B0DAA9E00147258 /* Routes.swift */,
|
||||
DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */,
|
||||
|
|
@ -622,6 +627,7 @@
|
|||
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */,
|
||||
DD61937A2863876A00E59241 /* Config */,
|
||||
DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */,
|
||||
DDD5BB082C285DDC007E03CA /* AppLog.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -917,19 +923,19 @@
|
|||
DDC2E18D26CE25CB0042C5E4 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */,
|
||||
DD5E523D298F5A7D00D21B61 /* Weather */,
|
||||
DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */,
|
||||
DDB75A222A13CDA9006ED576 /* BatteryLevelCompact.swift */,
|
||||
DD457187293C7E63000C49FB /* BLESignalStrengthIndicator.swift */,
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */,
|
||||
DDF924C926FBB953009FE055 /* ConnectedDevice.swift */,
|
||||
DDC3B273283F411B00AC321C /* LastHeardText.swift */,
|
||||
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */,
|
||||
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */,
|
||||
DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */,
|
||||
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */,
|
||||
DD457187293C7E63000C49FB /* BLESignalStrengthIndicator.swift */,
|
||||
DDB75A1D2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift */,
|
||||
DDC3B273283F411B00AC321C /* LastHeardText.swift */,
|
||||
DDB75A202A12B954006ED576 /* LoRaSignalStrength.swift */,
|
||||
DDB75A222A13CDA9006ED576 /* BatteryLevelCompact.swift */,
|
||||
DDB75A1D2A0B0CD0006ED576 /* LoRaSignalStrengthIndicator.swift */,
|
||||
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */,
|
||||
DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */,
|
||||
DD5E523D298F5A7D00D21B61 /* Weather */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -947,7 +953,6 @@
|
|||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */,
|
||||
DDDB443C29F6592F00EE2349 /* NetworkManager.swift */,
|
||||
DD3619142B1EF9F900C41C8C /* LocationsHandler.swift */,
|
||||
25183D452C0A6D97001E31D5 /* Logger.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -970,6 +975,15 @@
|
|||
path = Mqtt;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDD5BB0E2C285F92007E03CA /* Logs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDD5BB0A2C285E45007E03CA /* LogDetail.swift */,
|
||||
DDD5BB0F2C285FB3007E03CA /* AppLogFilter.swift */,
|
||||
);
|
||||
path = Logs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDDB26402AABEF7B003AFCB7 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -1003,6 +1017,7 @@
|
|||
DD1933772B084F4200771CD5 /* Measurement.swift */,
|
||||
DDFFA7462B3A7F3C004730DB /* Bundle.swift */,
|
||||
DDF45C362BC46A5A005ED5F2 /* TimeZone.swift */,
|
||||
DDD5BB0C2C285F00007E03CA /* Logger.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1237,6 +1252,7 @@
|
|||
DD93800E2BA74D0C008BEC06 /* ChannelForm.swift in Sources */,
|
||||
DD41A61529AB0035003C5A37 /* NodeWeatherForecast.swift in Sources */,
|
||||
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */,
|
||||
DDD5BB102C285FB3007E03CA /* AppLogFilter.swift in Sources */,
|
||||
DD4640202AFF10F4002A5ECB /* WaypointForm.swift in Sources */,
|
||||
DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */,
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
|
|
@ -1258,8 +1274,10 @@
|
|||
DD5E5213298EE33B00D21B61 /* deviceonly.pb.swift in Sources */,
|
||||
DDE5B4042B2279A700FCDD05 /* TraceRouteLog.swift in Sources */,
|
||||
DD5E5208298EE33B00D21B61 /* rtttl.pb.swift in Sources */,
|
||||
DDD5BB0D2C285F00007E03CA /* Logger.swift in Sources */,
|
||||
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */,
|
||||
DDDB263F2AABEE20003AFCB7 /* NodeList.swift in Sources */,
|
||||
DDD5BB0B2C285E45007E03CA /* LogDetail.swift in Sources */,
|
||||
DDA0B6B2294CDC55001356EC /* Channels.swift in Sources */,
|
||||
DDE9659C2B1C3B6A00531070 /* RouteRecorder.swift in Sources */,
|
||||
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */,
|
||||
|
|
@ -1307,7 +1325,6 @@
|
|||
D93068D32B8129510066FBC8 /* MessageContextMenuItems.swift in Sources */,
|
||||
DD0E20FE2B87090400F2D100 /* paxcount.pb.swift in Sources */,
|
||||
DD5E520A298EE33B00D21B61 /* channel.pb.swift in Sources */,
|
||||
25183D462C0A6D97001E31D5 /* Logger.swift in Sources */,
|
||||
DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */,
|
||||
DD964FC42974767D007C176F /* MapViewFitExtension.swift in Sources */,
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
|
||||
|
|
@ -1323,6 +1340,7 @@
|
|||
D93068DD2B81CA820066FBC8 /* ConfigHeader.swift in Sources */,
|
||||
DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */,
|
||||
D9BC22DB2B7DE8E2006A37D5 /* TileDownloadStatus.swift in Sources */,
|
||||
DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */,
|
||||
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
|
||||
DD5E5202298EE33B00D21B61 /* admin.pb.swift in Sources */,
|
||||
DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */,
|
||||
|
|
@ -1371,7 +1389,6 @@
|
|||
DD58C5F22919AD3C00D5BEFB /* ChannelEntityExtension.swift in Sources */,
|
||||
DDA9515E2BC6F56F00CEA535 /* IndoorAirQuality.swift in Sources */,
|
||||
DDDB444E29F8AB0E00EE2349 /* Int.swift in Sources */,
|
||||
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */,
|
||||
DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */,
|
||||
DDC4C9FF2A8D982900CE201C /* DetectionSensorConfig.swift in Sources */,
|
||||
D9C983A22B79D1A600BDBE6A /* RequestPositionButton.swift in Sources */,
|
||||
|
|
@ -1595,7 +1612,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.10;
|
||||
MARKETING_VERSION = 2.3.12;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1629,7 +1646,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.10;
|
||||
MARKETING_VERSION = 2.3.12;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1837,6 +1854,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DDD5BB142C28680D007E03CA /* MeshtasticDataModelV 38.xcdatamodel */,
|
||||
DDD28D372C0CD2670063CFA3 /* MeshtasticDataModelV 37.xcdatamodel */,
|
||||
DD31B04D2BDC6FD30024FA63 /* MeshtasticDataModelV 36.xcdatamodel */,
|
||||
DD268D8C2BCC7D11008073AE /* MeshtasticDataModelV 35.xcdatamodel */,
|
||||
|
|
@ -1875,7 +1893,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DDD28D372C0CD2670063CFA3 /* MeshtasticDataModelV 37.xcdatamodel */;
|
||||
currentVersion = DDD5BB142C28680D007E03CA /* MeshtasticDataModelV 38.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"originHash" : "e9855e3a299c14a10f11ee0b8f29e4170b09548533939361223a0f50e7caac8c",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "cocoamqtt",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/emqx/CocoaMQTT",
|
||||
"state" : {
|
||||
"revision" : "85387a2478551ad84f39be8a3c8587d34dd2bcf5",
|
||||
"version" : "2.1.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "mqttcocoaasyncsocket",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/leeway1208/MqttCocoaAsyncSocket",
|
||||
"state" : {
|
||||
"revision" : "ce3e18607fd01079495f86ff6195d8a3ca469f73",
|
||||
"version" : "1.0.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sqlite.swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/stephencelis/SQLite.swift.git",
|
||||
"state" : {
|
||||
"revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb",
|
||||
"version" : "0.14.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "starscream",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/daltoniam/Starscream.git",
|
||||
"state" : {
|
||||
"revision" : "a063fda2b8145a231953c20e7a646be254365396",
|
||||
"version" : "3.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "ce20dc083ee485524b802669890291c0d8090170",
|
||||
"version" : "1.22.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import OSLog
|
||||
|
||||
func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> String {
|
||||
var csvString: String = ""
|
||||
|
|
@ -64,6 +65,27 @@ func detectionsToCsv(detections: [MessageEntity]) -> String {
|
|||
return csvString
|
||||
}
|
||||
|
||||
func logToCsvFile(log: [OSLogEntryLog]) -> String {
|
||||
var csvString: String = ""
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
|
||||
// Create PAX Header
|
||||
csvString = "Process, Category, Level, Message, \("timestamp".localized)"
|
||||
for l in log {
|
||||
csvString += "\n"
|
||||
csvString += String(l.process)
|
||||
csvString += ", "
|
||||
csvString += String(l.category)
|
||||
csvString += ", "
|
||||
csvString += String(l.level.description)
|
||||
csvString += ", "
|
||||
csvString += String(l.composedMessage)
|
||||
csvString += ", "
|
||||
csvString += l.date.formattedDate(format: dateFormatString)
|
||||
}
|
||||
return csvString
|
||||
}
|
||||
|
||||
func paxToCsvFile(pax: [PaxCounterEntity]) -> String {
|
||||
var csvString: String = ""
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ extension UserEntity {
|
|||
self.value(forKey: "allMessages") as? [MessageEntity] ?? [MessageEntity]()
|
||||
}
|
||||
|
||||
var adminMessageList: [MessageEntity] {
|
||||
self.value(forKey: "adminMessages") as? [MessageEntity] ?? [MessageEntity]()
|
||||
}
|
||||
|
||||
var sensorMessageList: [MessageEntity] {
|
||||
self.value(forKey: "detectionSensorMessages") as? [MessageEntity] ?? [MessageEntity]()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,3 +15,15 @@ extension Int {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt32 {
|
||||
func toHex() -> String {
|
||||
return String(format: "!%2X", self)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int64 {
|
||||
func toHex() -> String {
|
||||
return String(format: "!%2X", self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
75
Meshtastic/Extensions/Logger.swift
Normal file
75
Meshtastic/Extensions/Logger.swift
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// Logger.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 6/3/24.
|
||||
//
|
||||
|
||||
import OSLog
|
||||
|
||||
extension Logger {
|
||||
|
||||
/// The logger's subsystem.
|
||||
private static var subsystem = Bundle.main.bundleIdentifier!
|
||||
|
||||
/// All admin messages
|
||||
static let admin = Logger(subsystem: subsystem, category: "🏛 Admin")
|
||||
|
||||
/// All logs related to data such as decoding error, parsing issues, etc.
|
||||
static let data = Logger(subsystem: subsystem, category: "🗄️ Data")
|
||||
|
||||
/// All logs related to the mesh
|
||||
static let mesh = Logger(subsystem: subsystem, category: "🕸️ Mesh")
|
||||
|
||||
/// All logs related to MQTT
|
||||
static let mqtt = Logger(subsystem: subsystem, category: "📱 MQTT")
|
||||
|
||||
/// All detailed logs originating from the device (radio).
|
||||
static let radio = Logger(subsystem: subsystem, category: "📟 Radio")
|
||||
|
||||
/// All logs related to services such as network calls, location, etc.
|
||||
static let services = Logger(subsystem: subsystem, category: "🍏 Services")
|
||||
|
||||
/// All logs related to tracking and analytics.
|
||||
static let statistics = Logger(subsystem: subsystem, category: "📊 Stats")
|
||||
|
||||
|
||||
/// Fetch from the logstore
|
||||
static public func fetch(predicateFormat: String) async throws -> [OSLogEntryLog] {
|
||||
|
||||
let store = try OSLogStore(scope: .currentProcessIdentifier)
|
||||
let position = store.position(timeIntervalSinceLatestBoot: 0)
|
||||
//let calendar = Calendar.current
|
||||
//let dayAgo = calendar.date(byAdding: .day, value: -1, to: Date.now)
|
||||
//let position = store.position(date: dayAgo!)
|
||||
let predicate = NSPredicate(format: predicateFormat)
|
||||
let entries = try store.getEntries(at: position, matching: predicate)
|
||||
|
||||
var logs: [OSLogEntryLog] = []
|
||||
for entry in entries {
|
||||
|
||||
try Task.checkCancellation()
|
||||
|
||||
if let log = entry as? OSLogEntryLog {
|
||||
logs.append(log)
|
||||
}
|
||||
}
|
||||
|
||||
if logs.isEmpty { logs = [] }
|
||||
return logs
|
||||
}
|
||||
}
|
||||
|
||||
extension OSLogEntryLog.Level {
|
||||
var description: String {
|
||||
switch self {
|
||||
case .undefined: "undefined"
|
||||
case .debug: "🩺 Debug"
|
||||
case .info: "ℹ️ Info"
|
||||
case .notice: "⚠️ Notice"
|
||||
case .error: "🚨 Error"
|
||||
case .fault: "💥 Fault"
|
||||
@unknown default: "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import SwiftUI
|
|||
import MapKit
|
||||
import CocoaMQTT
|
||||
import OSLog
|
||||
import RegexBuilder
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Meshtastic BLE Device Manager
|
||||
|
|
@ -41,11 +42,13 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
var TORADIO_characteristic: CBCharacteristic!
|
||||
var FROMRADIO_characteristic: CBCharacteristic!
|
||||
var FROMNUM_characteristic: CBCharacteristic!
|
||||
var LOGRADIO_characteristic: CBCharacteristic!
|
||||
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
|
||||
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
|
||||
let FROMRADIO_UUID = CBUUID(string: "0x2C55E69E-4993-11ED-B878-0242AC120002")
|
||||
let EOL_FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
|
||||
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
|
||||
let LOGRADIO_UUID = CBUUID(string: "0x6C6FD238-78FA-436B-AACF-15C5BE1EF2E2")
|
||||
|
||||
// MARK: init BLEManager
|
||||
override init() {
|
||||
|
|
@ -277,7 +280,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
guard let services = peripheral.services else { return }
|
||||
for service in services where service.uuid == meshtasticServiceCBUUID {
|
||||
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID], for: service)
|
||||
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID, LOGRADIO_UUID], for: service)
|
||||
Logger.services.info("✅ BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")")
|
||||
}
|
||||
}
|
||||
|
|
@ -310,6 +313,11 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
Logger.services.info("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")")
|
||||
FROMNUM_characteristic = characteristic
|
||||
peripheral.setNotifyValue(true, for: characteristic)
|
||||
|
||||
case LOGRADIO_UUID:
|
||||
Logger.services.info("✅ BLE did discover LOGRADIO (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")")
|
||||
LOGRADIO_characteristic = characteristic
|
||||
peripheral.setNotifyValue(true, for: characteristic)
|
||||
|
||||
default:
|
||||
break
|
||||
|
|
@ -388,7 +396,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
|
||||
let messageDescription = "🛎️ Requested Device Metadata for node \(toUser.longName ?? "unknown".localized) by \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
return 0
|
||||
|
|
@ -492,7 +500,11 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
|
||||
Logger.services.error("didUpdateNotificationStateFor error: \(error?.localizedDescription ?? "Unknown")")
|
||||
if let error {
|
||||
Logger.services.error("💥 BLE didUpdateNotificationStateFor error: \(characteristic.uuid, privacy: .public) \(error.localizedDescription, privacy: .public)")
|
||||
} else {
|
||||
Logger.services.info("ℹ️ peripheral didUpdateNotificationStateFor \(characteristic.uuid, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Data Read / Update Characteristic Event
|
||||
|
|
@ -514,6 +526,74 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
|
||||
switch characteristic.uuid {
|
||||
case LOGRADIO_UUID:
|
||||
if (characteristic.value == nil || characteristic.value!.isEmpty) {
|
||||
return
|
||||
}
|
||||
let coordsSearch = Regex {
|
||||
Capture {
|
||||
Regex {
|
||||
"lat="
|
||||
OneOrMore(.digit)
|
||||
}
|
||||
}
|
||||
Capture {" "}
|
||||
Capture {
|
||||
Regex {
|
||||
"long="
|
||||
OneOrMore(.digit)
|
||||
}
|
||||
}
|
||||
}
|
||||
.anchorsMatchLineEndings()
|
||||
if var log = String(data: characteristic.value!, encoding: .utf8) {
|
||||
|
||||
/// Debug Log Level
|
||||
if (log.starts(with: "DEBUG |")) {
|
||||
do {
|
||||
let logString = log
|
||||
if let coordsMatch = try coordsSearch.firstMatch(in: logString) {
|
||||
log = "\(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces))"
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
|
||||
}else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🐞 \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🐞 \(log.replacingOccurrences(of: "DEBUG |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} else if (log.starts(with: "INFO |")) {
|
||||
do {
|
||||
let logString = log
|
||||
if let coordsMatch = try coordsSearch.firstMatch(in: logString) {
|
||||
log = "\(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces))"
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("🛰️ \(log.prefix(upTo: coordsMatch.range.lowerBound), privacy: .public) \(coordsMatch.0.replacingOccurrences(of: "[,]", with: "", options: .regularExpression), privacy: .private) \(log.suffix(from: coordsMatch.range.upperBound), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("🐞 \(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.info("✅ \(log.replacingOccurrences(of: "INFO |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
}
|
||||
} else if (log.starts(with: "WARN |")) {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.warning("⚠️ \(log.replacingOccurrences(of: "WARN |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else if (log.starts(with: "ERROR |")) {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.error("💥 \(log.replacingOccurrences(of: "ERROR |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else if (log.starts(with: "CRIT |")) {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.critical("💥 \(log.replacingOccurrences(of: "CRIT |", with: "").trimmingCharacters(in: .whitespaces), privacy: .public)")
|
||||
} else {
|
||||
log = log.replacingOccurrences(of: "[,]", with: "", options: .regularExpression)
|
||||
Logger.radio.debug("📟 \(log)")
|
||||
}
|
||||
}
|
||||
|
||||
case FROMRADIO_UUID:
|
||||
|
||||
|
|
@ -1065,7 +1145,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return false
|
||||
}
|
||||
let messageDescription = "🚀 Sent Set Fixed Postion Admin Message to: \(fromUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: fromUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1090,7 +1170,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return false
|
||||
}
|
||||
let messageDescription = "🚀 Sent Remove Fixed Position Admin Message to: \(fromUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: fromUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1160,7 +1240,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return false
|
||||
}
|
||||
let messageDescription = "🚀 Sent Shutdown Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1185,7 +1265,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return false
|
||||
}
|
||||
let messageDescription = "🚀 Sent Reboot Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1210,7 +1290,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return false
|
||||
}
|
||||
let messageDescription = "🚀 Sent Reboot OTA Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1236,7 +1316,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
automaticallyReconnect = false
|
||||
let messageDescription = "🚀 Sent enter DFU mode Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1261,7 +1341,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
|
||||
let messageDescription = "🚀 Sent Factory Reset Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1285,7 +1365,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
meshPacket.decoded = dataMessage
|
||||
let messageDescription = "🚀 Sent NodeDB Reset Admin Message to: \(toUser.longName ?? "unknown".localized) from: \(fromUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -1328,7 +1408,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🎛️ Requested Channel \(channel.index) for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
return 0
|
||||
|
|
@ -1352,7 +1432,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved Channel \(channel.index) for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
return 0
|
||||
|
|
@ -1498,7 +1578,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return 0
|
||||
}
|
||||
let messageDescription = "🛟 Saved User Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
return 0
|
||||
|
|
@ -1619,7 +1699,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
dataMessage.portnum = PortNum.adminApp
|
||||
meshPacket.decoded = dataMessage
|
||||
let messageDescription = "🛟 Saved Ham Parameters for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
return 0
|
||||
|
|
@ -1643,7 +1723,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
let messageDescription = "🛟 Saved Bluetooth Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertBluetoothConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1671,7 +1751,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
dataMessage.portnum = PortNum.adminApp
|
||||
meshPacket.decoded = dataMessage
|
||||
let messageDescription = "🛟 Saved Device Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertDeviceConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1698,7 +1778,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
dataMessage.portnum = PortNum.adminApp
|
||||
meshPacket.decoded = dataMessage
|
||||
let messageDescription = "🛟 Saved Display Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertDisplayConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1725,7 +1805,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
let messageDescription = "🛟 Saved LoRa Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertLoRaConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1756,7 +1836,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Position Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertPositionConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1787,7 +1867,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Power Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertPowerConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1818,7 +1898,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Network Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertNetworkConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1848,7 +1928,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Ambient Lighting Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertAmbientLightingModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1878,7 +1958,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Canned Message Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1909,7 +1989,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Canned Message Module Messages for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1939,7 +2019,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved Detection Sensor Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertDetectionSensorModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1968,7 +2048,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved External Notification Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1997,7 +2077,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved PAX Counter Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertPaxCounterModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2027,7 +2107,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved RTTTL Ringtone Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2058,7 +2138,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved MQTT Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertMqttModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2087,7 +2167,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛟 Saved Range Test Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertRangeTestModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2117,7 +2197,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved Serial Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertSerialModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2146,7 +2226,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛟 Saved Store & Forward Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertStoreForwardModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2175,7 +2255,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
upsertTelemetryModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2206,7 +2286,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛎️ Sent a Get Channel \(channelIndex) Request Admin Message for node: \(toUser.longName ?? "unknown".localized))"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -2280,7 +2360,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛎️ Requested Bluetooth Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2311,7 +2391,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛎️ Requested Device Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2342,7 +2422,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛎️ Requested Display Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2373,7 +2453,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛎️ Requested LoRa Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -2405,7 +2485,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
let messageDescription = "🛎️ Requested Network Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2435,7 +2515,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Position Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2465,7 +2545,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Power Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2495,7 +2575,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Ambient Lighting Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2525,7 +2605,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Canned Messages Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2555,7 +2635,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested External Notificaiton Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2585,7 +2665,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested PAX Counter Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2615,7 +2695,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested RTTTL Ringtone Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2645,7 +2725,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Range Test Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2675,7 +2755,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested MQTT Module Config on admin channel \(adminIndex) for node: \(String(connectedPeripheral.num))"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2705,7 +2785,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Detection Sensor Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2735,7 +2815,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Serial Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2765,7 +2845,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Store and Forward Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -2795,14 +2875,14 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
meshPacket.decoded = dataMessage
|
||||
|
||||
let messageDescription = "🛎️ Requested Telemetry Module Config on admin channel \(adminIndex) for node: \(toUser.longName ?? "unknown".localized)"
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
|
||||
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Send an admin message to a radio, save a message to core data for logging
|
||||
private func sendAdminMessageToRadio(meshPacket: MeshPacket, adminDescription: String, fromUser: UserEntity, toUser: UserEntity) -> Bool {
|
||||
private func sendAdminMessageToRadio(meshPacket: MeshPacket, adminDescription: String) -> Bool {
|
||||
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
|
|
@ -2812,25 +2892,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
|
||||
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
|
||||
let newMessage = MessageEntity(context: context!)
|
||||
newMessage.messageId = Int64(meshPacket.id)
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.admin = true
|
||||
newMessage.adminDescription = adminDescription
|
||||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
|
||||
do {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
try context!.save()
|
||||
Logger.mesh.debug("\(adminDescription)")
|
||||
return true
|
||||
} catch {
|
||||
context!.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Error inserting new core data MessageEntity: \(nsError)")
|
||||
}
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
Logger.mesh.debug("\(adminDescription)")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
import OSLog
|
||||
|
||||
extension Logger {
|
||||
|
||||
/// The logger's subsystem.
|
||||
private static var subsystem = Bundle.main.bundleIdentifier!
|
||||
|
||||
/// All logs related to data such as decoding error, parsing issues, etc.
|
||||
static let data = Logger(subsystem: subsystem, category: "🗄️ Data")
|
||||
|
||||
/// All logs related to the mesh
|
||||
static let mesh = Logger(subsystem: subsystem, category: "🕸️ Mesh")
|
||||
|
||||
/// All logs related to services such as network calls, location, etc.
|
||||
static let services = Logger(subsystem: subsystem, category: "🍏 Services")
|
||||
|
||||
/// All logs related to tracking and analytics.
|
||||
static let statistics = Logger(subsystem: subsystem, category: "📈 Stats")
|
||||
}
|
||||
|
|
@ -683,6 +683,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
telemetry.voltage = telemetryMessage.deviceMetrics.voltage
|
||||
telemetry.uptimeSeconds = Int32(telemetryMessage.deviceMetrics.uptimeSeconds)
|
||||
telemetry.metricsType = 0
|
||||
Logger.statistics.info("📈 [Mesh Statistics] Channel Utilization: \(telemetryMessage.deviceMetrics.channelUtilization) Airtime: \(telemetryMessage.deviceMetrics.airUtilTx) for Node: \(packet.from.toHex())")
|
||||
} else if telemetryMessage.variant == Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) {
|
||||
// Environment Metrics
|
||||
telemetry.barometricPressure = telemetryMessage.environmentMetrics.barometricPressure
|
||||
|
|
@ -708,7 +709,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
try context.save()
|
||||
// Only log telemetry from the mesh not the connected device
|
||||
if connectedNode != Int64(packet.from) {
|
||||
Logger.data.info("💾 Telemetry Saved for Node: \(packet.from)")
|
||||
Logger.data.info("💾 [Telemetry] Saved for Node: \(packet.from.toHex())")
|
||||
} else if telemetry.metricsType == 0 {
|
||||
// Connected Device Metrics
|
||||
// ------------------------
|
||||
|
|
@ -749,10 +750,10 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
|
|||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Error Saving Telemetry for Node \(packet.from) Error: \(nsError)")
|
||||
Logger.data.error("💥 Error Saving Telemetry for Node \(packet.from, privacy: .public) Error: \(nsError, privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
Logger.data.error("Error Fetching NodeInfoEntity for Node \(packet.from)")
|
||||
Logger.data.error("💥 Error Fetching NodeInfoEntity for Node \(packet.from.toHex(), privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,28 +81,28 @@ class MqttClientProxyManager {
|
|||
}
|
||||
}
|
||||
func subscribe(topic: String, qos: CocoaMQTTQoS) {
|
||||
Logger.services.info("📲 MQTT Client Proxy subscribed to: \(topic)")
|
||||
Logger.mqtt.info("📲 [MQTT Client Proxy] subscribed to: \(topic)")
|
||||
mqttClientProxy?.subscribe(topic, qos: qos)
|
||||
}
|
||||
func unsubscribe(topic: String) {
|
||||
mqttClientProxy?.unsubscribe(topic)
|
||||
Logger.services.info("📲 MQTT Client Proxy unsubscribe for: \(topic)")
|
||||
Logger.mqtt.info("📲 [MQTT Client Proxy] unsubscribe to topic: \(topic)")
|
||||
}
|
||||
func publish(message: String, topic: String, qos: CocoaMQTTQoS) {
|
||||
mqttClientProxy?.publish(topic, withString: message, qos: qos)
|
||||
Logger.services.debug("📲 MQTT Client Proxy publish for: \(topic)")
|
||||
Logger.mqtt.debug("📲 [MQTT Client Proxy] publish for: \(topic)")
|
||||
}
|
||||
func disconnect() {
|
||||
if let client = mqttClientProxy {
|
||||
client.disconnect()
|
||||
Logger.services.info("📲 MQTT Client Proxy Disconnected")
|
||||
Logger.mqtt.info("📲 [MQTT Client Proxy] disconnected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MqttClientProxyManager: CocoaMQTTDelegate {
|
||||
func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) {
|
||||
Logger.services.info("📲 MQTT Client Proxy didConnectAck: \(ack)")
|
||||
Logger.mqtt.info("📲 MQTT Client Proxy didConnectAck: \(ack)")
|
||||
if ack == .accept {
|
||||
delegate?.onMqttConnected()
|
||||
} else {
|
||||
|
|
@ -130,34 +130,33 @@ extension MqttClientProxyManager: CocoaMQTTDelegate {
|
|||
}
|
||||
}
|
||||
func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) {
|
||||
Logger.services.debug("mqttDidDisconnect: \(err?.localizedDescription ?? "")")
|
||||
|
||||
Logger.mqtt.debug("📲 [MQTT Client Proxy] disconnected: \(err?.localizedDescription ?? "")")
|
||||
if let error = err {
|
||||
delegate?.onMqttError(message: error.localizedDescription)
|
||||
}
|
||||
delegate?.onMqttDisconnected()
|
||||
}
|
||||
func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) {
|
||||
Logger.services.debug("📲 MQTT Client Proxy didPublishMessage from MqttClientProxyManager: \(message)")
|
||||
Logger.mqtt.info("📲 [MQTT Client Proxy] published messsage from MqttClientProxyManager: \(message)")
|
||||
}
|
||||
func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) {
|
||||
Logger.services.debug("📲 MQTT Client Proxy didPublishAck from MqttClientProxyManager: \(id)")
|
||||
Logger.mqtt.info("📲 [MQTT Client Proxy] published Ack from MqttClientProxyManager: \(id)")
|
||||
}
|
||||
|
||||
public func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) {
|
||||
delegate?.onMqttMessageReceived(message: message)
|
||||
Logger.services.debug("📲 MQTT Client Proxy message received on topic: \(message.topic)")
|
||||
Logger.mqtt.info("📲 [MQTT Client Proxy] message received on topic: \(message.topic)")
|
||||
}
|
||||
func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String]) {
|
||||
Logger.services.info("📲 MQTT Client Proxy didSubscribeTopics: \(success.allKeys.count) topics. failed: \(failed.count) topics")
|
||||
Logger.mqtt.debug("📲 [MQTT Client Proxy] subscribed to topics: \(success.allKeys.count) topics. failed: \(failed.count) topics")
|
||||
}
|
||||
func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopics topics: [String]) {
|
||||
Logger.services.info("didUnsubscribeTopics: \(topics.joined(separator: ", "))")
|
||||
Logger.mqtt.debug("📲 [MQTT Client Proxy] unsubscribed from topics: \(topics.joined(separator: "- "))")
|
||||
}
|
||||
func mqttDidPing(_ mqtt: CocoaMQTT) {
|
||||
Logger.services.info("📲 MQTT Client Proxy mqttDidPing")
|
||||
Logger.mqtt.debug("📲 [MQTT Client Proxy] ping")
|
||||
}
|
||||
func mqttDidReceivePong(_ mqtt: CocoaMQTT) {
|
||||
Logger.services.info("📲 MQTT Client Proxy mqttDidReceivePong")
|
||||
Logger.mqtt.debug("📲 [MQTT Client Proxy] pong")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV 37.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV 38.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,465 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22758" systemVersion="23F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="green" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ledState" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="red" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="ambientLightingConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceLoggingEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
|
||||
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="downlinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<fetchedProperty name="allPrivateMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="channel == $FETCH_SOURCE.index && toUser == nil AND isEmoji == false"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="detectionTriggeredHigh" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="disableTripleClick" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="isManaged" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="ledHeartbeatEnabled" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="tzdef" optional="YES" attributeType="String"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
|
||||
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hwModel" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="units" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wakeOnTapOrMotion" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="useI2SAsBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="fetchedProperty" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="ExternalNotificationConfigEntity"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="routeLocation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RouteEntity" inverseName="locations" inverseEntity="RouteEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ignoreMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<fetchedProperty name="tapbacks" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="replyID == $FETCH_SOURCE.messageId AND isEmoji == true"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPositionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="13" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPublishIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mapReportingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="proxyToClientEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="root" optional="YES" attributeType="String" defaultValueString="msh"/>
|
||||
<attribute name="tlsEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="toUser == nil"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ntpServer" optional="YES" attributeType="String"/>
|
||||
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="favorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="firstHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hopsAway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="viaMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
|
||||
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
|
||||
<relationship name="pax" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PaxCounterEntity" inverseName="paxNode" inverseEntity="PaxCounterEntity"/>
|
||||
<relationship name="paxCounterConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PaxCounterConfigEntity" inverseName="paxCounterConfigNode" inverseEntity="PaxCounterConfigEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="powerConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PowerConfigEntity" inverseName="powerConfigNode" inverseEntity="PowerConfigEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="rtttlConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RTTTLConfigEntity" inverseName="rtttlConfigNode" inverseEntity="RTTTLConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="storeForwardConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreForwardConfigEntity" inverseName="storeForwardConfigNode" inverseEntity="StoreForwardConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="traceRoutes" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteEntity" inverseName="node" inverseEntity="TraceRouteEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PaxCounterConfigEntity" representedClassName="PaxCounterConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleThreshold" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiThreshold" optional="YES" attributeType="Integer 32" defaultValueString="-80" usesScalarValueType="YES"/>
|
||||
<relationship name="paxCounterConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="paxCounterConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PaxCounterEntity" representedClassName="PaxCounterEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ble" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="paxNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="pax" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="broadcastSmartMinimumDistance" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="broadcastSmartMinimumIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsEnGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latest" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="precisionBits" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PowerConfigEntity" representedClassName="PowerConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adcMultiplierOverride" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceBatteryInaAddress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isPowerSaving" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="lsSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minWakeSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="onBatteryShutdownAfterSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="waitBluetoothSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="powerConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="powerConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RouteEntity" representedClassName="RouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="color" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="distance" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="elevationGain" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="endDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="notes" optional="YES" attributeType="String"/>
|
||||
<relationship name="locations" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="LocationEntity" inverseName="routeLocation" inverseEntity="LocationEntity"/>
|
||||
</entity>
|
||||
<entity name="RTTTLConfigEntity" representedClassName="RTTTLConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ringtone" optional="YES" attributeType="String" maxValueString="228" defaultValueString=""/>
|
||||
<relationship name="rtttlConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rtttlConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideConsoleSerialPort" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="StoreForwardConfigEntity" representedClassName="StoreForwardConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="powerMeasurementEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="powerScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="powerUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="iaq" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptimeSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteEntity" representedClassName="TraceRouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="route" optional="YES" attributeType="Transformable" customClassName="[UInt32]"/>
|
||||
<attribute name="routeText" optional="YES" attributeType="String"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteHopEntity" inverseName="traceRoute" inverseEntity="TraceRouteHopEntity"/>
|
||||
<relationship name="node" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="traceRoutes" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteHopEntity" representedClassName="TraceRouteHopEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="traceRoute" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TraceRouteEntity" inverseName="hops" inverseEntity="TraceRouteEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastMessage" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numString" optional="YES" attributeType="String"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="adminMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10 "/>
|
||||
</fetchedProperty>
|
||||
<fetchedProperty name="detectionSensorMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND portNum = 10"/>
|
||||
</fetchedProperty>
|
||||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="id"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -134,7 +134,7 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext, includeRoutes
|
|||
|
||||
func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat("mesh.log.nodeinfo.received %@".localized, String(packet.from))
|
||||
let logString = String.localizedStringWithFormat("mesh.log.nodeinfo.received %@".localized, packet.from.toHex())
|
||||
MeshLogger.log("📟 \(logString)")
|
||||
|
||||
guard packet.from > 0 else { return }
|
||||
|
|
@ -383,18 +383,16 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
}
|
||||
}
|
||||
|
||||
func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
func upsertBluetoothConfigPacket(config: Config.BluetoothConfig, nodeNum: Int64, context: NSManagedObjectContext) {
|
||||
|
||||
let logString = String.localizedStringWithFormat("mesh.log.bluetooth.config %@".localized, String(nodeNum))
|
||||
MeshLogger.log("📶 \(logString)")
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
let fetchNodeInfoRequest = NodeInfoEntity.fetchRequest()
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else {
|
||||
return
|
||||
}
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest)
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
if fetchedNode[0].bluetoothConfig == nil {
|
||||
|
|
@ -402,26 +400,28 @@ func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, node
|
|||
newBluetoothConfig.enabled = config.enabled
|
||||
newBluetoothConfig.mode = Int32(config.mode.rawValue)
|
||||
newBluetoothConfig.fixedPin = Int32(config.fixedPin)
|
||||
newBluetoothConfig.deviceLoggingEnabled = config.deviceLoggingEnabled
|
||||
fetchedNode[0].bluetoothConfig = newBluetoothConfig
|
||||
} else {
|
||||
fetchedNode[0].bluetoothConfig?.enabled = config.enabled
|
||||
fetchedNode[0].bluetoothConfig?.mode = Int32(config.mode.rawValue)
|
||||
fetchedNode[0].bluetoothConfig?.fixedPin = Int32(config.fixedPin)
|
||||
fetchedNode[0].bluetoothConfig?.deviceLoggingEnabled = config.deviceLoggingEnabled
|
||||
}
|
||||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 Updated Bluetooth Config for node number: \(String(nodeNum))")
|
||||
Logger.data.info("💾 Updated Bluetooth Config for node: \(nodeNum.toHex(), privacy: .public)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Error Updating Core Data BluetoothConfigEntity: \(nsError)")
|
||||
Logger.data.error("💥 Error Updating Core Data BluetoothConfigEntity: \(nsError, privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
Logger.data.error("No Nodes found in local database matching node number \(nodeNum) unable to save Bluetooth Config")
|
||||
Logger.data.error("💥 No Nodes found in local database matching node \(nodeNum.toHex(), privacy: .public) unable to save Bluetooth Config")
|
||||
}
|
||||
} catch {
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Fetching node for core data BluetoothConfigEntity failed: \(nsError)")
|
||||
Logger.data.error("💥 Fetching node for core data BluetoothConfigEntity failed: \(nsError, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -245,6 +245,16 @@ struct AdminMessage {
|
|||
set {payloadVariant = .deleteFileRequest(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set zero and offset for scale chips
|
||||
var setScale: UInt32 {
|
||||
get {
|
||||
if case .setScale(let v)? = payloadVariant {return v}
|
||||
return 0
|
||||
}
|
||||
set {payloadVariant = .setScale(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the owner for this node
|
||||
var setOwner: User {
|
||||
|
|
@ -513,6 +523,9 @@ struct AdminMessage {
|
|||
/// Delete the file by the specified path from the device
|
||||
case deleteFileRequest(String)
|
||||
///
|
||||
/// Set zero and offset for scale chips
|
||||
case setScale(UInt32)
|
||||
///
|
||||
/// Set the owner for this node
|
||||
case setOwner(User)
|
||||
///
|
||||
|
|
@ -667,6 +680,10 @@ struct AdminMessage {
|
|||
guard case .deleteFileRequest(let l) = lhs, case .deleteFileRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setScale, .setScale): return {
|
||||
guard case .setScale(let l) = lhs, case .setScale(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setOwner, .setOwner): return {
|
||||
guard case .setOwner(let l) = lhs, case .setOwner(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
|
|
@ -1039,6 +1056,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
20: .standard(proto: "get_node_remote_hardware_pins_response"),
|
||||
21: .standard(proto: "enter_dfu_mode_request"),
|
||||
22: .standard(proto: "delete_file_request"),
|
||||
23: .standard(proto: "set_scale"),
|
||||
32: .standard(proto: "set_owner"),
|
||||
33: .standard(proto: "set_channel"),
|
||||
34: .standard(proto: "set_config"),
|
||||
|
|
@ -1274,6 +1292,14 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
self.payloadVariant = .deleteFileRequest(v)
|
||||
}
|
||||
}()
|
||||
case 23: try {
|
||||
var v: UInt32?
|
||||
try decoder.decodeSingularUInt32Field(value: &v)
|
||||
if let v = v {
|
||||
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .setScale(v)
|
||||
}
|
||||
}()
|
||||
case 32: try {
|
||||
var v: User?
|
||||
var hadOneofValue = false
|
||||
|
|
@ -1546,6 +1572,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
guard case .deleteFileRequest(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularStringField(value: v, fieldNumber: 22)
|
||||
}()
|
||||
case .setScale?: try {
|
||||
guard case .setScale(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 23)
|
||||
}()
|
||||
case .setOwner?: try {
|
||||
guard case .setOwner(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 32)
|
||||
|
|
|
|||
|
|
@ -630,6 +630,11 @@ struct Config {
|
|||
/// I2C address of INA_2XX to use for reading device battery voltage
|
||||
var deviceBatteryInaAddress: UInt32 = 0
|
||||
|
||||
///
|
||||
/// If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled.
|
||||
/// Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options.
|
||||
var powermonEnables: UInt64 = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -799,6 +804,10 @@ struct Config {
|
|||
/// Should we wake the screen up on accelerometer detected motion or tap
|
||||
var wakeOnTapOrMotion: Bool = false
|
||||
|
||||
///
|
||||
/// Indicates how to rotate or invert the compass output to accurate display on the display.
|
||||
var compassOrientation: Config.DisplayConfig.CompassOrientation = .degrees0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -998,6 +1007,76 @@ struct Config {
|
|||
|
||||
}
|
||||
|
||||
enum CompassOrientation: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
|
||||
///
|
||||
/// The compass and the display are in the same orientation.
|
||||
case degrees0 // = 0
|
||||
|
||||
///
|
||||
/// Rotate the compass by 90 degrees.
|
||||
case degrees90 // = 1
|
||||
|
||||
///
|
||||
/// Rotate the compass by 180 degrees.
|
||||
case degrees180 // = 2
|
||||
|
||||
///
|
||||
/// Rotate the compass by 270 degrees.
|
||||
case degrees270 // = 3
|
||||
|
||||
///
|
||||
/// Don't rotate the compass, but invert the result.
|
||||
case degrees0Inverted // = 4
|
||||
|
||||
///
|
||||
/// Rotate the compass by 90 degrees and invert.
|
||||
case degrees90Inverted // = 5
|
||||
|
||||
///
|
||||
/// Rotate the compass by 180 degrees and invert.
|
||||
case degrees180Inverted // = 6
|
||||
|
||||
///
|
||||
/// Rotate the compass by 270 degrees and invert.
|
||||
case degrees270Inverted // = 7
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
init() {
|
||||
self = .degrees0
|
||||
}
|
||||
|
||||
init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 0: self = .degrees0
|
||||
case 1: self = .degrees90
|
||||
case 2: self = .degrees180
|
||||
case 3: self = .degrees270
|
||||
case 4: self = .degrees0Inverted
|
||||
case 5: self = .degrees90Inverted
|
||||
case 6: self = .degrees180Inverted
|
||||
case 7: self = .degrees270Inverted
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
var rawValue: Int {
|
||||
switch self {
|
||||
case .degrees0: return 0
|
||||
case .degrees90: return 1
|
||||
case .degrees180: return 2
|
||||
case .degrees270: return 3
|
||||
case .degrees0Inverted: return 4
|
||||
case .degrees90Inverted: return 5
|
||||
case .degrees180Inverted: return 6
|
||||
case .degrees270Inverted: return 7
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
|
@ -1334,6 +1413,10 @@ struct Config {
|
|||
/// Specified PIN for PairingMode.FixedPin
|
||||
var fixedPin: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Enables device (serial style logs) over Bluetooth
|
||||
var deviceLoggingEnabled: Bool = false
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
enum PairingMode: SwiftProtobuf.Enum {
|
||||
|
|
@ -1485,6 +1568,20 @@ extension Config.DisplayConfig.DisplayMode: CaseIterable {
|
|||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.CompassOrientation: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static let allCases: [Config.DisplayConfig.CompassOrientation] = [
|
||||
.degrees0,
|
||||
.degrees90,
|
||||
.degrees180,
|
||||
.degrees270,
|
||||
.degrees0Inverted,
|
||||
.degrees90Inverted,
|
||||
.degrees180Inverted,
|
||||
.degrees270Inverted,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.LoRaConfig.RegionCode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static let allCases: [Config.LoRaConfig.RegionCode] = [
|
||||
|
|
@ -1553,6 +1650,7 @@ extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {}
|
|||
extension Config.DisplayConfig.DisplayUnits: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.OledType: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.DisplayMode: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.CompassOrientation: @unchecked Sendable {}
|
||||
extension Config.LoRaConfig: @unchecked Sendable {}
|
||||
extension Config.LoRaConfig.RegionCode: @unchecked Sendable {}
|
||||
extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {}
|
||||
|
|
@ -1986,6 +2084,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
7: .standard(proto: "ls_secs"),
|
||||
8: .standard(proto: "min_wake_secs"),
|
||||
9: .standard(proto: "device_battery_ina_address"),
|
||||
32: .standard(proto: "powermon_enables"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2002,6 +2101,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
case 7: try { try decoder.decodeSingularUInt32Field(value: &self.lsSecs) }()
|
||||
case 8: try { try decoder.decodeSingularUInt32Field(value: &self.minWakeSecs) }()
|
||||
case 9: try { try decoder.decodeSingularUInt32Field(value: &self.deviceBatteryInaAddress) }()
|
||||
case 32: try { try decoder.decodeSingularUInt64Field(value: &self.powermonEnables) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2032,6 +2132,9 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if self.deviceBatteryInaAddress != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.deviceBatteryInaAddress, fieldNumber: 9)
|
||||
}
|
||||
if self.powermonEnables != 0 {
|
||||
try visitor.visitSingularUInt64Field(value: self.powermonEnables, fieldNumber: 32)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2044,6 +2147,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if lhs.lsSecs != rhs.lsSecs {return false}
|
||||
if lhs.minWakeSecs != rhs.minWakeSecs {return false}
|
||||
if lhs.deviceBatteryInaAddress != rhs.deviceBatteryInaAddress {return false}
|
||||
if lhs.powermonEnables != rhs.powermonEnables {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -2197,6 +2301,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
8: .same(proto: "displaymode"),
|
||||
9: .standard(proto: "heading_bold"),
|
||||
10: .standard(proto: "wake_on_tap_or_motion"),
|
||||
11: .standard(proto: "compass_orientation"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2215,6 +2320,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
case 8: try { try decoder.decodeSingularEnumField(value: &self.displaymode) }()
|
||||
case 9: try { try decoder.decodeSingularBoolField(value: &self.headingBold) }()
|
||||
case 10: try { try decoder.decodeSingularBoolField(value: &self.wakeOnTapOrMotion) }()
|
||||
case 11: try { try decoder.decodeSingularEnumField(value: &self.compassOrientation) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2251,6 +2357,9 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
if self.wakeOnTapOrMotion != false {
|
||||
try visitor.visitSingularBoolField(value: self.wakeOnTapOrMotion, fieldNumber: 10)
|
||||
}
|
||||
if self.compassOrientation != .degrees0 {
|
||||
try visitor.visitSingularEnumField(value: self.compassOrientation, fieldNumber: 11)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2265,6 +2374,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp
|
|||
if lhs.displaymode != rhs.displaymode {return false}
|
||||
if lhs.headingBold != rhs.headingBold {return false}
|
||||
if lhs.wakeOnTapOrMotion != rhs.wakeOnTapOrMotion {return false}
|
||||
if lhs.compassOrientation != rhs.compassOrientation {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -2306,6 +2416,19 @@ extension Config.DisplayConfig.DisplayMode: SwiftProtobuf._ProtoNameProviding {
|
|||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.CompassOrientation: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "DEGREES_0"),
|
||||
1: .same(proto: "DEGREES_90"),
|
||||
2: .same(proto: "DEGREES_180"),
|
||||
3: .same(proto: "DEGREES_270"),
|
||||
4: .same(proto: "DEGREES_0_INVERTED"),
|
||||
5: .same(proto: "DEGREES_90_INVERTED"),
|
||||
6: .same(proto: "DEGREES_180_INVERTED"),
|
||||
7: .same(proto: "DEGREES_270_INVERTED"),
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = Config.protoMessageName + ".LoRaConfig"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
|
|
@ -2471,6 +2594,7 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
1: .same(proto: "enabled"),
|
||||
2: .same(proto: "mode"),
|
||||
3: .standard(proto: "fixed_pin"),
|
||||
4: .standard(proto: "device_logging_enabled"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -2482,6 +2606,7 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
case 1: try { try decoder.decodeSingularBoolField(value: &self.enabled) }()
|
||||
case 2: try { try decoder.decodeSingularEnumField(value: &self.mode) }()
|
||||
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.fixedPin) }()
|
||||
case 4: try { try decoder.decodeSingularBoolField(value: &self.deviceLoggingEnabled) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -2497,6 +2622,9 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
if self.fixedPin != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.fixedPin, fieldNumber: 3)
|
||||
}
|
||||
if self.deviceLoggingEnabled != false {
|
||||
try visitor.visitSingularBoolField(value: self.deviceLoggingEnabled, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -2504,6 +2632,7 @@ extension Config.BluetoothConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageI
|
|||
if lhs.enabled != rhs.enabled {return false}
|
||||
if lhs.mode != rhs.mode {return false}
|
||||
if lhs.fixedPin != rhs.fixedPin {return false}
|
||||
if lhs.deviceLoggingEnabled != rhs.deviceLoggingEnabled {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,18 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// wiphone https://www.wiphone.io/
|
||||
case wiphone // = 20
|
||||
|
||||
///
|
||||
/// WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk
|
||||
case wioWm1110 // = 21
|
||||
|
||||
///
|
||||
/// RAK2560 Solar base station based on RAK4630
|
||||
case rak2560 // = 22
|
||||
|
||||
///
|
||||
/// Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
||||
case heltecHru3601 // = 23
|
||||
|
||||
///
|
||||
/// B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
||||
case stationG1 // = 25
|
||||
|
|
@ -288,6 +300,10 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS
|
||||
case radiomaster900BanditNano // = 64
|
||||
|
||||
///
|
||||
/// Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors
|
||||
case heltecCapsuleSensorV3 // = 65
|
||||
|
||||
///
|
||||
/// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
/// 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.
|
||||
|
|
@ -322,6 +338,9 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 18: self = .nanoG2Ultra
|
||||
case 19: self = .loraType
|
||||
case 20: self = .wiphone
|
||||
case 21: self = .wioWm1110
|
||||
case 22: self = .rak2560
|
||||
case 23: self = .heltecHru3601
|
||||
case 25: self = .stationG1
|
||||
case 26: self = .rak11310
|
||||
case 27: self = .senseloraRp2040
|
||||
|
|
@ -362,6 +381,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 62: self = .twcMeshV4
|
||||
case 63: self = .nrf52PromicroDiy
|
||||
case 64: self = .radiomaster900BanditNano
|
||||
case 65: self = .heltecCapsuleSensorV3
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -390,6 +410,9 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .nanoG2Ultra: return 18
|
||||
case .loraType: return 19
|
||||
case .wiphone: return 20
|
||||
case .wioWm1110: return 21
|
||||
case .rak2560: return 22
|
||||
case .heltecHru3601: return 23
|
||||
case .stationG1: return 25
|
||||
case .rak11310: return 26
|
||||
case .senseloraRp2040: return 27
|
||||
|
|
@ -430,6 +453,7 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .twcMeshV4: return 62
|
||||
case .nrf52PromicroDiy: return 63
|
||||
case .radiomaster900BanditNano: return 64
|
||||
case .heltecCapsuleSensorV3: return 65
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -463,6 +487,9 @@ extension HardwareModel: CaseIterable {
|
|||
.nanoG2Ultra,
|
||||
.loraType,
|
||||
.wiphone,
|
||||
.wioWm1110,
|
||||
.rak2560,
|
||||
.heltecHru3601,
|
||||
.stationG1,
|
||||
.rak11310,
|
||||
.senseloraRp2040,
|
||||
|
|
@ -503,6 +530,7 @@ extension HardwareModel: CaseIterable {
|
|||
.twcMeshV4,
|
||||
.nrf52PromicroDiy,
|
||||
.radiomaster900BanditNano,
|
||||
.heltecCapsuleSensorV3,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -2701,6 +2729,129 @@ struct NodeRemoteHardwarePin {
|
|||
fileprivate var _pin: RemoteHardwarePin? = nil
|
||||
}
|
||||
|
||||
struct ChunkedPayload {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// The ID of the entire payload
|
||||
var payloadID: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The total number of chunks in the payload
|
||||
var chunkCount: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The current chunk index in the total
|
||||
var chunkIndex: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The binary data of the current chunk
|
||||
var payloadChunk: Data = Data()
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// Wrapper message for broken repeated oneof support
|
||||
struct resend_chunks {
|
||||
// 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.
|
||||
|
||||
var chunks: [UInt32] = []
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// Responses to a ChunkedPayload request
|
||||
struct ChunkedPayloadResponse {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// The ID of the entire payload
|
||||
var payloadID: UInt32 = 0
|
||||
|
||||
var payloadVariant: ChunkedPayloadResponse.OneOf_PayloadVariant? = nil
|
||||
|
||||
///
|
||||
/// Request to transfer chunked payload
|
||||
var requestTransfer: Bool {
|
||||
get {
|
||||
if case .requestTransfer(let v)? = payloadVariant {return v}
|
||||
return false
|
||||
}
|
||||
set {payloadVariant = .requestTransfer(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Accept the transfer chunked payload
|
||||
var acceptTransfer: Bool {
|
||||
get {
|
||||
if case .acceptTransfer(let v)? = payloadVariant {return v}
|
||||
return false
|
||||
}
|
||||
set {payloadVariant = .acceptTransfer(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Request missing indexes in the chunked payload
|
||||
var resendChunks: resend_chunks {
|
||||
get {
|
||||
if case .resendChunks(let v)? = payloadVariant {return v}
|
||||
return resend_chunks()
|
||||
}
|
||||
set {payloadVariant = .resendChunks(newValue)}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
enum OneOf_PayloadVariant: Equatable {
|
||||
///
|
||||
/// Request to transfer chunked payload
|
||||
case requestTransfer(Bool)
|
||||
///
|
||||
/// Accept the transfer chunked payload
|
||||
case acceptTransfer(Bool)
|
||||
///
|
||||
/// Request missing indexes in the chunked payload
|
||||
case resendChunks(resend_chunks)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
static func ==(lhs: ChunkedPayloadResponse.OneOf_PayloadVariant, rhs: ChunkedPayloadResponse.OneOf_PayloadVariant) -> Bool {
|
||||
// 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 (lhs, rhs) {
|
||||
case (.requestTransfer, .requestTransfer): return {
|
||||
guard case .requestTransfer(let l) = lhs, case .requestTransfer(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.acceptTransfer, .acceptTransfer): return {
|
||||
guard case .acceptTransfer(let l) = lhs, case .acceptTransfer(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.resendChunks, .resendChunks): return {
|
||||
guard case .resendChunks(let l) = lhs, case .resendChunks(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension HardwareModel: @unchecked Sendable {}
|
||||
extension Constants: @unchecked Sendable {}
|
||||
|
|
@ -2736,6 +2887,10 @@ extension Neighbor: @unchecked Sendable {}
|
|||
extension DeviceMetadata: @unchecked Sendable {}
|
||||
extension Heartbeat: @unchecked Sendable {}
|
||||
extension NodeRemoteHardwarePin: @unchecked Sendable {}
|
||||
extension ChunkedPayload: @unchecked Sendable {}
|
||||
extension resend_chunks: @unchecked Sendable {}
|
||||
extension ChunkedPayloadResponse: @unchecked Sendable {}
|
||||
extension ChunkedPayloadResponse.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
|
@ -2765,6 +2920,9 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
18: .same(proto: "NANO_G2_ULTRA"),
|
||||
19: .same(proto: "LORA_TYPE"),
|
||||
20: .same(proto: "WIPHONE"),
|
||||
21: .same(proto: "WIO_WM1110"),
|
||||
22: .same(proto: "RAK2560"),
|
||||
23: .same(proto: "HELTEC_HRU_3601"),
|
||||
25: .same(proto: "STATION_G1"),
|
||||
26: .same(proto: "RAK11310"),
|
||||
27: .same(proto: "SENSELORA_RP2040"),
|
||||
|
|
@ -2805,6 +2963,7 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
62: .same(proto: "TWC_MESH_V4"),
|
||||
63: .same(proto: "NRF52_PROMICRO_DIY"),
|
||||
64: .same(proto: "RADIOMASTER_900_BANDIT_NANO"),
|
||||
65: .same(proto: "HELTEC_CAPSULE_SENSOR_V3"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
@ -4775,3 +4934,169 @@ extension NodeRemoteHardwarePin: SwiftProtobuf.Message, SwiftProtobuf._MessageIm
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension ChunkedPayload: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ChunkedPayload"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "payload_id"),
|
||||
2: .standard(proto: "chunk_count"),
|
||||
3: .standard(proto: "chunk_index"),
|
||||
4: .standard(proto: "payload_chunk"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.payloadID) }()
|
||||
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.chunkCount) }()
|
||||
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.chunkIndex) }()
|
||||
case 4: try { try decoder.decodeSingularBytesField(value: &self.payloadChunk) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.payloadID != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.payloadID, fieldNumber: 1)
|
||||
}
|
||||
if self.chunkCount != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.chunkCount, fieldNumber: 2)
|
||||
}
|
||||
if self.chunkIndex != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.chunkIndex, fieldNumber: 3)
|
||||
}
|
||||
if !self.payloadChunk.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.payloadChunk, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: ChunkedPayload, rhs: ChunkedPayload) -> Bool {
|
||||
if lhs.payloadID != rhs.payloadID {return false}
|
||||
if lhs.chunkCount != rhs.chunkCount {return false}
|
||||
if lhs.chunkIndex != rhs.chunkIndex {return false}
|
||||
if lhs.payloadChunk != rhs.payloadChunk {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension resend_chunks: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".resend_chunks"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "chunks"),
|
||||
]
|
||||
|
||||
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.decodeRepeatedUInt32Field(value: &self.chunks) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if !self.chunks.isEmpty {
|
||||
try visitor.visitPackedUInt32Field(value: self.chunks, fieldNumber: 1)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: resend_chunks, rhs: resend_chunks) -> Bool {
|
||||
if lhs.chunks != rhs.chunks {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension ChunkedPayloadResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".ChunkedPayloadResponse"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "payload_id"),
|
||||
2: .standard(proto: "request_transfer"),
|
||||
3: .standard(proto: "accept_transfer"),
|
||||
4: .standard(proto: "resend_chunks"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.payloadID) }()
|
||||
case 2: try {
|
||||
var v: Bool?
|
||||
try decoder.decodeSingularBoolField(value: &v)
|
||||
if let v = v {
|
||||
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .requestTransfer(v)
|
||||
}
|
||||
}()
|
||||
case 3: try {
|
||||
var v: Bool?
|
||||
try decoder.decodeSingularBoolField(value: &v)
|
||||
if let v = v {
|
||||
if self.payloadVariant != nil {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .acceptTransfer(v)
|
||||
}
|
||||
}()
|
||||
case 4: try {
|
||||
var v: resend_chunks?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .resendChunks(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .resendChunks(v)
|
||||
}
|
||||
}()
|
||||
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.payloadID != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.payloadID, fieldNumber: 1)
|
||||
}
|
||||
switch self.payloadVariant {
|
||||
case .requestTransfer?: try {
|
||||
guard case .requestTransfer(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 2)
|
||||
}()
|
||||
case .acceptTransfer?: try {
|
||||
guard case .acceptTransfer(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 3)
|
||||
}()
|
||||
case .resendChunks?: try {
|
||||
guard case .resendChunks(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
|
||||
}()
|
||||
case nil: break
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: ChunkedPayloadResponse, rhs: ChunkedPayloadResponse) -> Bool {
|
||||
if lhs.payloadID != rhs.payloadID {return false}
|
||||
if lhs.payloadVariant != rhs.payloadVariant {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
179
Meshtastic/Protobufs/meshtastic/powermon.pb.swift
Normal file
179
Meshtastic/Protobufs/meshtastic/powermon.pb.swift
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/powermon.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
|
||||
}
|
||||
|
||||
/// Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
|
||||
///But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us)
|
||||
struct PowerMon {
|
||||
// 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.
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
/// Any significant power changing event in meshtastic should be tagged with a powermon state transition.
|
||||
///If you are making new meshtastic features feel free to add new entries at the end of this definition.
|
||||
enum State: SwiftProtobuf.Enum {
|
||||
typealias RawValue = Int
|
||||
case none // = 0
|
||||
case cpuDeepSleep // = 1
|
||||
case cpuLightSleep // = 2
|
||||
|
||||
///
|
||||
///The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only
|
||||
///occasionally. In cases where that rail has multiple devices on it we usually want to have logging on
|
||||
///the state of that rail as an independent record.
|
||||
///For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen.
|
||||
///
|
||||
///The log messages will be short and complete (see PowerMon.Event in the protobufs for details).
|
||||
///something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states.
|
||||
///(We use a bitmask for states so that if a log message gets lost it won't be fatal)
|
||||
case vext1On // = 4
|
||||
case loraRxon // = 8
|
||||
case loraTxon // = 16
|
||||
case loraRxactive // = 32
|
||||
case btOn // = 64
|
||||
case ledOn // = 128
|
||||
case screenOn // = 256
|
||||
case screenDrawing // = 512
|
||||
case wifiOn // = 1024
|
||||
|
||||
///
|
||||
///GPS is actively trying to find our location
|
||||
///See GPSPowerState for more details
|
||||
case gpsActive // = 2048
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
init() {
|
||||
self = .none
|
||||
}
|
||||
|
||||
init?(rawValue: Int) {
|
||||
switch rawValue {
|
||||
case 0: self = .none
|
||||
case 1: self = .cpuDeepSleep
|
||||
case 2: self = .cpuLightSleep
|
||||
case 4: self = .vext1On
|
||||
case 8: self = .loraRxon
|
||||
case 16: self = .loraTxon
|
||||
case 32: self = .loraRxactive
|
||||
case 64: self = .btOn
|
||||
case 128: self = .ledOn
|
||||
case 256: self = .screenOn
|
||||
case 512: self = .screenDrawing
|
||||
case 1024: self = .wifiOn
|
||||
case 2048: self = .gpsActive
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
var rawValue: Int {
|
||||
switch self {
|
||||
case .none: return 0
|
||||
case .cpuDeepSleep: return 1
|
||||
case .cpuLightSleep: return 2
|
||||
case .vext1On: return 4
|
||||
case .loraRxon: return 8
|
||||
case .loraTxon: return 16
|
||||
case .loraRxactive: return 32
|
||||
case .btOn: return 64
|
||||
case .ledOn: return 128
|
||||
case .screenOn: return 256
|
||||
case .screenDrawing: return 512
|
||||
case .wifiOn: return 1024
|
||||
case .gpsActive: return 2048
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension PowerMon.State: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
static let allCases: [PowerMon.State] = [
|
||||
.none,
|
||||
.cpuDeepSleep,
|
||||
.cpuLightSleep,
|
||||
.vext1On,
|
||||
.loraRxon,
|
||||
.loraTxon,
|
||||
.loraRxactive,
|
||||
.btOn,
|
||||
.ledOn,
|
||||
.screenOn,
|
||||
.screenDrawing,
|
||||
.wifiOn,
|
||||
.gpsActive,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension PowerMon: @unchecked Sendable {}
|
||||
extension PowerMon.State: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
||||
extension PowerMon: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".PowerMon"
|
||||
static let _protobuf_nameMap = SwiftProtobuf._NameMap()
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let _ = try decoder.nextFieldNumber() {
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: PowerMon, rhs: PowerMon) -> Bool {
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension PowerMon.State: SwiftProtobuf._ProtoNameProviding {
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "None"),
|
||||
1: .same(proto: "CPU_DeepSleep"),
|
||||
2: .same(proto: "CPU_LightSleep"),
|
||||
4: .same(proto: "Vext1_On"),
|
||||
8: .same(proto: "Lora_RXOn"),
|
||||
16: .same(proto: "Lora_TXOn"),
|
||||
32: .same(proto: "Lora_RXActive"),
|
||||
64: .same(proto: "BT_On"),
|
||||
128: .same(proto: "LED_On"),
|
||||
256: .same(proto: "Screen_On"),
|
||||
512: .same(proto: "Screen_Drawing"),
|
||||
1024: .same(proto: "Wifi_On"),
|
||||
2048: .same(proto: "GPS_Active"),
|
||||
]
|
||||
}
|
||||
|
|
@ -120,6 +120,14 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
///
|
||||
/// AHT10 Integrated temperature and humidity sensor
|
||||
case aht10 // = 23
|
||||
|
||||
///
|
||||
/// DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction)
|
||||
case dfrobotLark // = 24
|
||||
|
||||
///
|
||||
/// NAU7802 Scale Chip or compatible
|
||||
case nau7802 // = 25
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
init() {
|
||||
|
|
@ -152,6 +160,8 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case 21: self = .ltr390Uv
|
||||
case 22: self = .tsl25911Fn
|
||||
case 23: self = .aht10
|
||||
case 24: self = .dfrobotLark
|
||||
case 25: self = .nau7802
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -182,6 +192,8 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case .ltr390Uv: return 21
|
||||
case .tsl25911Fn: return 22
|
||||
case .aht10: return 23
|
||||
case .dfrobotLark: return 24
|
||||
case .nau7802: return 25
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -217,6 +229,8 @@ extension TelemetrySensorType: CaseIterable {
|
|||
.ltr390Uv,
|
||||
.tsl25911Fn,
|
||||
.aht10,
|
||||
.dfrobotLark,
|
||||
.nau7802,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -302,6 +316,27 @@ struct EnvironmentMetrics {
|
|||
/// VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor.
|
||||
var whiteLux: Float = 0
|
||||
|
||||
///
|
||||
/// Infrared lux
|
||||
var irLux: Float = 0
|
||||
|
||||
///
|
||||
/// Ultraviolet lux
|
||||
var uvLux: Float = 0
|
||||
|
||||
///
|
||||
/// Wind direction in degrees
|
||||
/// 0 degrees = North, 90 = East, etc...
|
||||
var windDirection: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Wind speed in m/s
|
||||
var windSpeed: Float = 0
|
||||
|
||||
///
|
||||
/// Weight in KG
|
||||
var weight: Float = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
|
@ -503,6 +538,26 @@ struct Telemetry {
|
|||
init() {}
|
||||
}
|
||||
|
||||
///
|
||||
/// NAU7802 Telemetry configuration, for saving to flash
|
||||
struct Nau7802Config {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// The offset setting for the NAU7802
|
||||
var zeroOffset: Int32 = 0
|
||||
|
||||
///
|
||||
/// The calibration factor for the NAU7802
|
||||
var calibrationFactor: Float = 0
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension TelemetrySensorType: @unchecked Sendable {}
|
||||
extension DeviceMetrics: @unchecked Sendable {}
|
||||
|
|
@ -511,6 +566,7 @@ extension PowerMetrics: @unchecked Sendable {}
|
|||
extension AirQualityMetrics: @unchecked Sendable {}
|
||||
extension Telemetry: @unchecked Sendable {}
|
||||
extension Telemetry.OneOf_Variant: @unchecked Sendable {}
|
||||
extension Nau7802Config: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
|
@ -543,6 +599,8 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
|
|||
21: .same(proto: "LTR390UV"),
|
||||
22: .same(proto: "TSL25911FN"),
|
||||
23: .same(proto: "AHT10"),
|
||||
24: .same(proto: "DFROBOT_LARK"),
|
||||
25: .same(proto: "NAU7802"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -615,6 +673,11 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
8: .same(proto: "distance"),
|
||||
9: .same(proto: "lux"),
|
||||
10: .standard(proto: "white_lux"),
|
||||
11: .standard(proto: "ir_lux"),
|
||||
12: .standard(proto: "uv_lux"),
|
||||
13: .standard(proto: "wind_direction"),
|
||||
14: .standard(proto: "wind_speed"),
|
||||
15: .same(proto: "weight"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -633,6 +696,11 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
case 8: try { try decoder.decodeSingularFloatField(value: &self.distance) }()
|
||||
case 9: try { try decoder.decodeSingularFloatField(value: &self.lux) }()
|
||||
case 10: try { try decoder.decodeSingularFloatField(value: &self.whiteLux) }()
|
||||
case 11: try { try decoder.decodeSingularFloatField(value: &self.irLux) }()
|
||||
case 12: try { try decoder.decodeSingularFloatField(value: &self.uvLux) }()
|
||||
case 13: try { try decoder.decodeSingularUInt32Field(value: &self.windDirection) }()
|
||||
case 14: try { try decoder.decodeSingularFloatField(value: &self.windSpeed) }()
|
||||
case 15: try { try decoder.decodeSingularFloatField(value: &self.weight) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -669,6 +737,21 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if self.whiteLux != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.whiteLux, fieldNumber: 10)
|
||||
}
|
||||
if self.irLux != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.irLux, fieldNumber: 11)
|
||||
}
|
||||
if self.uvLux != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.uvLux, fieldNumber: 12)
|
||||
}
|
||||
if self.windDirection != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.windDirection, fieldNumber: 13)
|
||||
}
|
||||
if self.windSpeed != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.windSpeed, fieldNumber: 14)
|
||||
}
|
||||
if self.weight != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.weight, fieldNumber: 15)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -683,6 +766,11 @@ extension EnvironmentMetrics: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if lhs.distance != rhs.distance {return false}
|
||||
if lhs.lux != rhs.lux {return false}
|
||||
if lhs.whiteLux != rhs.whiteLux {return false}
|
||||
if lhs.irLux != rhs.irLux {return false}
|
||||
if lhs.uvLux != rhs.uvLux {return false}
|
||||
if lhs.windDirection != rhs.windDirection {return false}
|
||||
if lhs.windSpeed != rhs.windSpeed {return false}
|
||||
if lhs.weight != rhs.weight {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -959,3 +1047,41 @@ extension Telemetry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension Nau7802Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".Nau7802Config"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "zeroOffset"),
|
||||
2: .same(proto: "calibrationFactor"),
|
||||
]
|
||||
|
||||
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.decodeSingularInt32Field(value: &self.zeroOffset) }()
|
||||
case 2: try { try decoder.decodeSingularFloatField(value: &self.calibrationFactor) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if self.zeroOffset != 0 {
|
||||
try visitor.visitSingularInt32Field(value: self.zeroOffset, fieldNumber: 1)
|
||||
}
|
||||
if self.calibrationFactor != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.calibrationFactor, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: Nau7802Config, rhs: Nau7802Config) -> Bool {
|
||||
if lhs.zeroOffset != rhs.zeroOffset {return false}
|
||||
if lhs.calibrationFactor != rhs.calibrationFactor {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
//
|
||||
// AdminMessageList.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 7/2/22.
|
||||
//
|
||||
/*
|
||||
Abstract:
|
||||
A view showing the details for a node.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
import CoreLocation
|
||||
|
||||
struct AdminMessageList: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
var user: UserEntity?
|
||||
|
||||
var body: some View {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmmssa", options: 0, locale: Locale.current)
|
||||
let localeTimeFormat = DateFormatter.dateFormat(fromTemplate: "h:mm:ss a", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mm:ss a")
|
||||
let timeFormatString = (localeTimeFormat ?? "h:mm:ss a")
|
||||
|
||||
List {
|
||||
if user != nil {
|
||||
|
||||
ForEach( user!.adminMessageList.reversed() ) { am in
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text("\(am.adminDescription ?? "unknown".localized)")
|
||||
.font(.caption)
|
||||
|
||||
Text("Sent \(Date(timeIntervalSince1970: TimeInterval(am.messageTimestamp)).formattedDate(format: dateFormatString))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption2)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
let ackErrorVal = RoutingError(rawValue: Int(am.ackError))
|
||||
|
||||
if am.ackTimestamp > 0 {
|
||||
if am.realACK {
|
||||
|
||||
Text(ackErrorVal?.display ?? "Empty Ack Error")
|
||||
.foregroundColor(am.receivedACK ? .gray : .red)
|
||||
.font(.caption2)
|
||||
} else {
|
||||
Text("Implicit ACK from another node")
|
||||
.foregroundColor(.orange)
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
|
||||
if am.receivedACK && am.ackTimestamp > 0 {
|
||||
Text(" \(Date(timeIntervalSince1970: TimeInterval(am.ackTimestamp)).formattedDate(format: timeFormatString))")
|
||||
.foregroundColor(am.realACK ? .gray : .orange)
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("admin.log")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
.onAppear {
|
||||
if self.bleManager.context == nil {
|
||||
self.bleManager.context = context
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
227
Meshtastic/Views/Settings/AppLog.swift
Normal file
227
Meshtastic/Views/Settings/AppLog.swift
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
//
|
||||
// AppLog.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 6/4/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import OSLog
|
||||
|
||||
/// Needed for TableColumnForEach
|
||||
@available(iOS 17.4, *)
|
||||
struct AppLog: View {
|
||||
|
||||
@State private var logs: [OSLogEntryLog] = []
|
||||
@State private var sortOrder = [KeyPathComparator(\OSLogEntryLog.date, order: .reverse)]
|
||||
@State private var selection: OSLogEntry.ID?
|
||||
@State private var selectedLog: OSLogEntryLog?
|
||||
@State private var presentingErrorDetails: Bool = false
|
||||
@State private var searchText = ""
|
||||
@State private var category: Int = -1
|
||||
@State private var level: Int = -1
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
@State var isEditingFilters = false
|
||||
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
private let dateFormatStyle = Date.FormatStyle()
|
||||
.hour(.twoDigits(amPM: .omitted))
|
||||
.minute()
|
||||
.second()
|
||||
.secondFraction(.fractional(3))
|
||||
|
||||
var body: some View {
|
||||
|
||||
Table(logs, selection: $selection, sortOrder: $sortOrder) {
|
||||
if idiom != .phone {
|
||||
TableColumn("log.time") { value in
|
||||
Text(value.date.formatted(dateFormatStyle))
|
||||
}
|
||||
.width(min: 125, max: 150)
|
||||
TableColumn("log.category", value: \.category)
|
||||
.width(min: 125, max: 150)
|
||||
TableColumn("log.level") { value in
|
||||
Text(value.level.description)
|
||||
}
|
||||
.width(min: 75, max: 100)
|
||||
|
||||
}
|
||||
TableColumn("log.message", value: \.composedMessage) { value in
|
||||
Text(value.composedMessage)
|
||||
.font(idiom == .phone ? .caption : .body)
|
||||
}
|
||||
.width(ideal: 200, max: .infinity)
|
||||
}
|
||||
.monospaced()
|
||||
.sheet(isPresented: $isEditingFilters) {
|
||||
AppLogFilter(category: $category, level: $level)
|
||||
}
|
||||
.safeAreaInset(edge: .bottom, alignment: .trailing) {
|
||||
HStack {
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
isEditingFilters = !isEditingFilters
|
||||
}
|
||||
}) {
|
||||
Image(systemName: !isEditingFilters ? "line.3.horizontal.decrease.circle" : "line.3.horizontal.decrease.circle.fill")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
|
||||
}
|
||||
.controlSize(.regular)
|
||||
.padding(5)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.padding(.trailing, 5)
|
||||
.searchable(text: $searchText, placement: .navigationBarDrawer, prompt: "Search")
|
||||
.disabled(selection != nil)
|
||||
.overlay {
|
||||
if logs.isEmpty {
|
||||
ContentUnavailableView("No Logs Available", systemImage: "scroll")
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
await logs = searchAppLogs()
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
.onChange(of: sortOrder) { _, sortOrder in
|
||||
withAnimation {
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
Task {
|
||||
await logs = searchAppLogs()
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
}
|
||||
.onChange(of: category) { _ in
|
||||
Task {
|
||||
await logs = searchAppLogs()
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
}
|
||||
.onChange(of: level) { _ in
|
||||
Task {
|
||||
await logs = searchAppLogs()
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
}
|
||||
.onChange(of: selection) { newSelection in
|
||||
presentingErrorDetails = true
|
||||
let log = logs.first {
|
||||
$0.id == newSelection
|
||||
}
|
||||
selectedLog = log
|
||||
}
|
||||
.sheet(item: $selectedLog, onDismiss: didDismiss) { log in
|
||||
LogDetail(log: log)
|
||||
.padding()
|
||||
}
|
||||
.task {
|
||||
logs = await searchAppLogs()
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
.fileExporter(
|
||||
isPresented: $isExporting,
|
||||
document: CsvDocument(emptyCsv: exportString),
|
||||
contentType: .commaSeparatedText,
|
||||
defaultFilename: String("Meshtastic Application Logs"),
|
||||
onCompletion: { result in
|
||||
switch result {
|
||||
case .success:
|
||||
self.isExporting = false
|
||||
Logger.services.info("Application log download succeeded.")
|
||||
case .failure(let error):
|
||||
Logger.services.error("Application log download failed: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
)
|
||||
.navigationBarTitle("Debug Logs\(logs.isEmpty ? "" : " (\(logs.count))")", displayMode: .inline)
|
||||
.toolbar {
|
||||
#if targetEnvironment(macCatalyst)
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button(action: {
|
||||
Task {
|
||||
await logs = searchAppLogs()
|
||||
logs.sort(using: sortOrder)
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "arrow.clockwise.circle")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if !logs.isEmpty {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button(action: {
|
||||
exportString = logToCsvFile(log: logs)
|
||||
isExporting = true
|
||||
}) {
|
||||
Image(systemName: "square.and.arrow.down")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func didDismiss() {
|
||||
selection = nil
|
||||
selectedLog = nil
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17.4, *)
|
||||
extension AppLog {
|
||||
@MainActor
|
||||
private func searchAppLogs() async -> [OSLogEntryLog] {
|
||||
do {
|
||||
/// Case Insensitive Search Text Predicates
|
||||
let searchPredicates = ["composedMessage", "category", "subsystem", "process"].map { property in
|
||||
return NSPredicate(format: "%K CONTAINS[c] %@", property, searchText)
|
||||
}
|
||||
/// Create a compound predicate using each text search preicate as an OR
|
||||
let textSearchPredicate = NSCompoundPredicate(type: .or, subpredicates: searchPredicates)
|
||||
/// Create an array of predicates to hold our AND predicates
|
||||
var predicates: [NSPredicate] = []
|
||||
/// Subsystem Predicate
|
||||
let subsystemPredicate = NSPredicate(format: "subsystem IN %@", ["com.apple.SwiftUI", "com.apple.coredata", "gvh.MeshtasticClient"])
|
||||
predicates.append(subsystemPredicate)
|
||||
/// Category
|
||||
if category > -1 {
|
||||
let categoryPredicate = NSPredicate(format: "category == %@", LogCategories(rawValue: category)!.description)
|
||||
predicates.append(categoryPredicate)
|
||||
}
|
||||
/// Log Level
|
||||
if level > -1 {
|
||||
let levelPredicate = NSPredicate(format: "messageType == %@", LogLevels(rawValue: level)?.level ?? "info")
|
||||
predicates.append(levelPredicate)
|
||||
}
|
||||
|
||||
if predicates.count > 0 || !searchText.isEmpty {
|
||||
if !searchText.isEmpty {
|
||||
let filterPredicates = NSCompoundPredicate(type: .and, subpredicates: predicates)
|
||||
let compoundPredicate = NSCompoundPredicate(type: .and, subpredicates: [textSearchPredicate, filterPredicates])
|
||||
let logs = try await Logger.fetch(predicateFormat: compoundPredicate.predicateFormat)
|
||||
return logs
|
||||
} else {
|
||||
let filterPredicates = NSCompoundPredicate(type: .and, subpredicates: predicates)
|
||||
let logs = try await Logger.fetch(predicateFormat: filterPredicates.predicateFormat)
|
||||
return logs
|
||||
}
|
||||
} else {
|
||||
let logs = try await Logger.fetch(predicateFormat: subsystemPredicate.predicateFormat)
|
||||
|
||||
return logs
|
||||
}
|
||||
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OSLogEntry: Identifiable { }
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
// Copyright (c) Garth Vander Houwen 8/18/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
struct BluetoothConfig: View {
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
@ -18,6 +18,7 @@ struct BluetoothConfig: View {
|
|||
@State var mode = 0
|
||||
@State var fixedPin = "123456"
|
||||
@State var shortPin = false
|
||||
@State var deviceLoggingEnabled = false
|
||||
var pinLength: Int = 6
|
||||
let numberFormatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
|
|
@ -68,18 +69,24 @@ struct BluetoothConfig: View {
|
|||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
|
||||
Toggle(isOn: $deviceLoggingEnabled) {
|
||||
Label("Device Logging Enabled", systemImage: "ladybug")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
}
|
||||
.disabled(self.bleManager.connectedPeripheral == nil || node?.bluetoothConfig == nil)
|
||||
|
||||
SaveConfigButton(node: node, hasChanges: $hasChanges) {
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if connectedNode != nil {
|
||||
if let myNodeNum = bleManager.connectedPeripheral?.num,
|
||||
let connectedNode = getNodeInfo(id: myNodeNum, context: context) {
|
||||
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: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
bc.deviceLoggingEnabled = deviceLoggingEnabled
|
||||
let adminMessageId = bleManager.saveBluetoothConfig(config: bc, fromUser: connectedNode.user!, toUser: node!.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
// Should show a saved successfully alert once I know that to be true
|
||||
// for now just disable the button after a successful save
|
||||
|
|
@ -90,21 +97,26 @@ struct BluetoothConfig: View {
|
|||
}
|
||||
|
||||
.navigationTitle("bluetooth.config")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
.navigationBarItems(
|
||||
trailing: ZStack {
|
||||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: bleManager.connectedPeripheral?.shortName ?? "?"
|
||||
)
|
||||
}
|
||||
)
|
||||
.onAppear {
|
||||
if self.bleManager.context == nil {
|
||||
self.bleManager.context = context
|
||||
}
|
||||
setBluetoothValues()
|
||||
// Need to request a BluetoothConfig from the remote node before allowing changes
|
||||
if bleManager.connectedPeripheral != nil && node?.bluetoothConfig == nil {
|
||||
if let connectedPeripheral = bleManager.connectedPeripheral, let node, node.bluetoothConfig == nil {
|
||||
Logger.mesh.info("empty bluetooth config")
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
|
||||
if node != nil && connectedNode != nil {
|
||||
_ = bleManager.requestBluetoothConfig(fromUser: connectedNode!.user!, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
let connectedNode = getNodeInfo(id: connectedPeripheral.num, context: context)
|
||||
if let connectedNode {
|
||||
_ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,11 +135,17 @@ struct BluetoothConfig: View {
|
|||
if newFixedPin != String(node!.bluetoothConfig!.fixedPin) { hasChanges = true }
|
||||
}
|
||||
}
|
||||
.onChange(of: deviceLoggingEnabled) { newDeviceLogging in
|
||||
if node != nil && node!.bluetoothConfig != nil {
|
||||
if newDeviceLogging != node!.bluetoothConfig!.deviceLoggingEnabled { hasChanges = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
func setBluetoothValues() {
|
||||
self.enabled = node?.bluetoothConfig?.enabled ?? true
|
||||
self.mode = Int(node?.bluetoothConfig?.mode ?? 0)
|
||||
self.fixedPin = String(node?.bluetoothConfig?.fixedPin ?? 123456)
|
||||
self.deviceLoggingEnabled = node?.bluetoothConfig?.deviceLoggingEnabled ?? false
|
||||
self.hasChanges = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
141
Meshtastic/Views/Settings/Logs/AppLogFilter.swift
Normal file
141
Meshtastic/Views/Settings/Logs/AppLogFilter.swift
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// AppLogFilter.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Garth Vander Houwen on 6/15/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
import SwiftUI
|
||||
|
||||
enum LogCategories: Int, CaseIterable, Identifiable {
|
||||
|
||||
case admin = 0
|
||||
case data = 1
|
||||
case mesh = 2
|
||||
case mqtt = 3
|
||||
case radio = 4
|
||||
case services = 5
|
||||
case stats = 6
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
switch self {
|
||||
|
||||
case .admin:
|
||||
return "🏛 Admin"
|
||||
case .data:
|
||||
return "🗄️ Data"
|
||||
case .mesh:
|
||||
return "🕸️ Mesh"
|
||||
case .mqtt:
|
||||
return "📱 MQTT"
|
||||
case .radio:
|
||||
return "📟 Radio"
|
||||
case .services:
|
||||
return "🍏 Services"
|
||||
case .stats:
|
||||
return "📊 Stats"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LogLevels: Int, CaseIterable, Identifiable {
|
||||
|
||||
case debug = 0
|
||||
case info = 1
|
||||
case notice = 2
|
||||
case error = 3
|
||||
case fault = 4
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var level: String {
|
||||
switch self {
|
||||
case .debug:
|
||||
return "debug"
|
||||
case .info:
|
||||
return "info"
|
||||
case .notice:
|
||||
return "notice"
|
||||
case .error:
|
||||
return "error"
|
||||
case .fault:
|
||||
return "fault"
|
||||
}
|
||||
}
|
||||
var description: String {
|
||||
switch self {
|
||||
case .debug:
|
||||
return "🩺 Debug"
|
||||
case .info:
|
||||
return "ℹ️ Info"
|
||||
case .notice:
|
||||
return "⚠️ Notice"
|
||||
case .error:
|
||||
return "🚨 Error"
|
||||
case .fault:
|
||||
return "💥 Fault"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppLogFilter: View {
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
/// Filters
|
||||
var filterTitle = "App Log Filters"
|
||||
//@Binding
|
||||
@Binding var category: Int
|
||||
@Binding var level: Int
|
||||
|
||||
var body: some View {
|
||||
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section(header: Text(filterTitle)) {
|
||||
HStack {
|
||||
Label("Category", systemImage: "square.grid.2x2")
|
||||
Picker("", selection: $category) {
|
||||
Text("All Categories")
|
||||
.tag(-1)
|
||||
ForEach(LogCategories.allCases) { lc in
|
||||
Text("\(lc.description)")
|
||||
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
}
|
||||
|
||||
HStack {
|
||||
Label("Level", systemImage: "stairs")
|
||||
Picker("", selection: $level) {
|
||||
Text("All Levels")
|
||||
.tag(-1)
|
||||
ForEach(LogLevels.allCases) { ll in
|
||||
Text("\(ll.description)")
|
||||
//.tag(ll.rawValue)
|
||||
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
#if targetEnvironment(macCatalyst)
|
||||
Spacer()
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Label("close", systemImage: "xmark")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding(.bottom)
|
||||
#endif
|
||||
}
|
||||
.presentationDetents([.fraction(0.6), .fraction(0.75)])
|
||||
.presentationDragIndicator(.visible)
|
||||
}
|
||||
}
|
||||
158
Meshtastic/Views/Settings/Logs/LogDetail.swift
Normal file
158
Meshtastic/Views/Settings/Logs/LogDetail.swift
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// LogDetail.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 6/5/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
import OSLog
|
||||
|
||||
//@available(iOS 17.0, macOS 14.0, *)
|
||||
struct LogDetail: View {
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
var log: OSLogEntryLog
|
||||
var font: Font = .title2
|
||||
|
||||
private let dateFormatStyle = Date.FormatStyle()
|
||||
.day(.defaultDigits)
|
||||
.month(.defaultDigits)
|
||||
.year(.twoDigits)
|
||||
.hour(.twoDigits(amPM: .omitted))
|
||||
.minute()
|
||||
.second()
|
||||
.secondFraction(.fractional(3))
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Text("OS Log Entry Details")
|
||||
.font(.largeTitle)
|
||||
}
|
||||
Divider()
|
||||
HStack(alignment: .top) {
|
||||
VStack(alignment: .leading) {
|
||||
List {
|
||||
/// Time
|
||||
Label {
|
||||
Text("log.time".localized + ":")
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: idiom == .phone ? 115 : 190, alignment: .trailing)
|
||||
Text(log.date.formatted(dateFormatStyle))
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
} icon: {
|
||||
Image(systemName: "timer")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(idiom == .phone ? .callout : .title)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.listSectionSeparator(.hidden, edges: .top)
|
||||
.listSectionSeparator(.visible, edges: .bottom)
|
||||
/// Subsystem
|
||||
Label {
|
||||
Text("log.subsystem".localized + ":")
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: idiom == .phone ? 115 : 190, alignment: .trailing)
|
||||
Text(log.subsystem)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
} icon: {
|
||||
Image(systemName: "gear")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.listRowSeparator(.visible)
|
||||
/// Process
|
||||
Label {
|
||||
Text("log.process".localized + ":")
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: idiom == .phone ? 115 : 190, alignment: .trailing)
|
||||
Text(log.process)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
} icon: {
|
||||
Image(systemName: "tag")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.listRowSeparator(.visible)
|
||||
/// Category
|
||||
Label {
|
||||
Text("log.category".localized + ":")
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: idiom == .phone ? 115 : 190, alignment: .trailing)
|
||||
Text(log.category)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
} icon: {
|
||||
Image(systemName: "square.grid.2x2")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.listRowSeparator(.visible)
|
||||
/// Level
|
||||
Label {
|
||||
Text("log.level".localized + ":")
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: idiom == .phone ? 115 : 190, alignment: .trailing)
|
||||
Text(log.level.description)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
} icon: {
|
||||
Image(systemName: "stairs")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.listRowSeparator(.visible)
|
||||
/// message
|
||||
Label {
|
||||
Text("log.message".localized + ":")
|
||||
.font(idiom == .phone ? .caption : .title)
|
||||
.frame(width: idiom == .phone ? 115 : 190, alignment: .trailing)
|
||||
Text(log.composedMessage)
|
||||
.textSelection(.enabled)
|
||||
.font(idiom == .phone ? .body : .title)
|
||||
.padding(.bottom, 5)
|
||||
|
||||
} icon: {
|
||||
Image(systemName: "text.bubble")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(idiom == .phone ? .callout : .title)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
}
|
||||
.listStyle(.plain)
|
||||
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
Spacer()
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Label("close", systemImage: "xmark")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
.padding(.bottom)
|
||||
#endif
|
||||
}
|
||||
.monospaced()
|
||||
.presentationDetents([.fraction(0.75), .fraction(0.85), .fraction(1.0)])
|
||||
.presentationDragIndicator(.visible)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ struct MeshLog: View {
|
|||
// Stop adding logs when an error is thrown
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.fileExporter(
|
||||
isPresented: $isExporting,
|
||||
document: document,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ struct Settings: View {
|
|||
case meshLog
|
||||
case adminMessageLog
|
||||
case about
|
||||
case appLog
|
||||
}
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
|
|
@ -412,17 +413,18 @@ struct Settings: View {
|
|||
}
|
||||
}
|
||||
.tag(SettingsSidebar.meshLog)
|
||||
NavigationLink {
|
||||
let connectedNode = nodes.first(where: { $0.num == preferredNodeNum })
|
||||
AdminMessageList(user: connectedNode?.user)
|
||||
} label: {
|
||||
Label {
|
||||
Text("admin.log")
|
||||
} icon: {
|
||||
Image(systemName: "building.columns")
|
||||
if #available (iOS 17.4, *) {
|
||||
NavigationLink {
|
||||
AppLog()
|
||||
} label: {
|
||||
Label {
|
||||
Text("Debug Logs")
|
||||
} icon: {
|
||||
Image(systemName: "stethoscope")
|
||||
}
|
||||
}
|
||||
.tag(SettingsSidebar.appLog)
|
||||
}
|
||||
.tag(SettingsSidebar.adminMessageLog)
|
||||
}
|
||||
Section(header: Text("Firmware")) {
|
||||
NavigationLink {
|
||||
|
|
|
|||
|
|
@ -173,6 +173,12 @@
|
|||
"interval.seventytwo.hours"="Seventy Two Hours";
|
||||
"keyboard.type"="Keyboard Typ";
|
||||
"logging"="Logging";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa Einstellungen";
|
||||
"map"="Mesh Karte";
|
||||
|
|
|
|||
|
|
@ -177,6 +177,12 @@
|
|||
"interval.seventytwo.hours"="Seventy Two Hours";
|
||||
"keyboard.type"="Keyboard Type";
|
||||
"logging"="Logging";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa Config";
|
||||
"map"="Mesh Map";
|
||||
|
|
|
|||
|
|
@ -154,6 +154,12 @@
|
|||
"interval.seventytwo.hours"="Soixante douze heures";
|
||||
"keyboard.type"="Type de clavier";
|
||||
"logging"="Enregistrement";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="Configuration LoRa";
|
||||
"map"="Carte de maillage";
|
||||
|
|
|
|||
|
|
@ -177,6 +177,12 @@
|
|||
"interval.seventytwo.hours"="שבעים ושתיים שעות";
|
||||
"keyboard.type"="סוג מקלדת";
|
||||
"logging"="רישום";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="לורה";
|
||||
"lora.config"="הגדרות לורה";
|
||||
"map"="מפת מש";
|
||||
|
|
|
|||
|
|
@ -175,6 +175,12 @@
|
|||
"interval.seventytwo.hours"="Siedemdziesiąt Dwie Godziny";
|
||||
"keyboard.type"="Typ Klawiatury";
|
||||
"logging"="Rejestracja";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="Konfiguracja LoRa";
|
||||
"map"="Mapa Sieci";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 1c3029f2868e5fc49809fd378f6c0c66aee0eaf4
|
||||
Subproject commit 4da558d0f73c46ef91b74431facee73c09affbfc
|
||||
|
|
@ -177,6 +177,12 @@
|
|||
"interval.seventytwo.hours"="Seventy Two Hours";
|
||||
"keyboard.type"="Keyboard Type";
|
||||
"logging"="Logging";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa Config";
|
||||
"map"="Mesh Map";
|
||||
|
|
|
|||
|
|
@ -177,6 +177,12 @@
|
|||
"interval.seventytwo.hours"="Sjuttiotvå Timmar";
|
||||
"keyboard.type"="Tangentbordstyp";
|
||||
"logging"="Loggning";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa Konfiguration";
|
||||
"map"="Mesh Karta";
|
||||
|
|
|
|||
|
|
@ -173,6 +173,12 @@
|
|||
"interval.eventytwo.hours"="七十二小时";
|
||||
"keyboard.type"="键盘类型";
|
||||
"logging"="加载中";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa 配置";
|
||||
"map"="Mesh 地图";
|
||||
|
|
|
|||
|
|
@ -173,6 +173,12 @@
|
|||
"interval.eventytwo.hours"="七十二小時";
|
||||
"keyboard.type"="鍵盤類型";
|
||||
"logging"="加載中";
|
||||
"log.time"="Time";
|
||||
"log.subsystem"="Subsystem";
|
||||
"log.process"="Process";
|
||||
"log.category"="Category";
|
||||
"log.level"="Level";
|
||||
"log.message"="Message";
|
||||
"lora"="LoRa";
|
||||
"lora.config"="LoRa 設定";
|
||||
"map"="Mesh 地圖";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue