Merge pull request #355 from meshtastic/2.1.18_Working_Changes

2.1.18 Working Changes
This commit is contained in:
Garth Vander Houwen 2023-05-05 09:31:03 -07:00 committed by GitHub
commit 81138ade33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 1083 additions and 358 deletions

View file

@ -280,6 +280,7 @@
DDB6ABE128B13FB500384BA1 /* PositionConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfigEnums.swift; sourceTree = "<group>"; };
DDB6ABE328B13FFF00384BA1 /* DisplayEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayEnums.swift; sourceTree = "<group>"; };
DDB6ABE528B1406100384BA1 /* LoraConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoraConfigEnums.swift; sourceTree = "<group>"; };
DDB759E12A04B264006ED576 /* MeshtasticDataModelV12.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV12.xcdatamodel; sourceTree = "<group>"; };
DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV8.xcdatamodel; sourceTree = "<group>"; };
DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; };
DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = "<group>"; };
@ -1264,7 +1265,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.7;
MARKETING_VERSION = 2.1.8;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1298,7 +1299,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.7;
MARKETING_VERSION = 2.1.8;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTS_MACCATALYST = YES;
@ -1417,7 +1418,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 2.1.8;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1448,7 +1449,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 2.1.8;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1545,6 +1546,7 @@
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
DDB759E12A04B264006ED576 /* MeshtasticDataModelV12.xcdatamodel */,
DDDEE5E229DBE43E00A8E078 /* MeshtasticDataModelV11.xcdatamodel */,
DDC94FC329CED7280082EA6E /* MeshtasticDataModelV10.xcdatamodel */,
DDDD527729B5B83F0045BC3C /* MeshtasticDataModelV9.xcdatamodel */,
@ -1557,7 +1559,7 @@
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
);
currentVersion = DDDEE5E229DBE43E00A8E078 /* MeshtasticDataModelV11.xcdatamodel */;
currentVersion = DDB759E12A04B264006ED576 /* MeshtasticDataModelV12.xcdatamodel */;
name = Meshtastic.xcdatamodeld;
path = Meshtastic/Meshtastic.xcdatamodeld;
sourceTree = "<group>";

View file

@ -8,31 +8,6 @@
import Foundation
import MapKit
enum KeyboardType: Int, CaseIterable, Identifiable {
case defaultKeyboard = 0
case asciiCapable = 1
case twitter = 9
case emailAddress = 7
case numbersAndPunctuation = 2
var id: Int { self.rawValue }
var description: String {
switch self {
case .defaultKeyboard:
return NSLocalizedString("default", comment: "Default Keyboard")
case .asciiCapable:
return NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard")
case .twitter:
return NSLocalizedString("twitter", comment: "Twitter Keyboard")
case .emailAddress:
return NSLocalizedString("email.address", comment: "Email Address Keyboard")
case .numbersAndPunctuation:
return NSLocalizedString("numbers.punctuation", comment: "Numbers and Punctuation Keyboard")
}
}
}
enum MeshMapTypes: Int, CaseIterable, Identifiable {
case standard = 0
@ -47,17 +22,17 @@ enum MeshMapTypes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .standard:
return NSLocalizedString("standard", comment: "Standard")
return "standard".localized
case .mutedStandard:
return NSLocalizedString("standard.muted", comment: "Standard Muted")
return "standard.muted".localized
case .hybrid:
return NSLocalizedString("hybrid", comment: "Hybrid")
return "hybrid".localized
case .hybridFlyover:
return NSLocalizedString("hybrid.flyover", comment: "Hybrid Flyover")
return "hybrid.flyover".localized
case .satellite:
return NSLocalizedString("satellite", comment: "Satellite")
return "satellite".localized
case .satelliteFlyover:
return NSLocalizedString("satellite.flyover", comment: "Satellite Flyover")
return "satellite.flyover".localized
}
}
func MKMapTypeValue() -> MKMapType {
@ -90,11 +65,11 @@ enum UserTrackingModes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .none:
return NSLocalizedString("map.usertrackingmode.none", comment: "None")
return "map.usertrackingmode.none".localized
case .follow:
return NSLocalizedString("map.usertrackingmode.follow", comment: "Follow")
return "map.usertrackingmode.follow".localized
case .followWithHeading:
return NSLocalizedString("map.usertrackingmode.followwithheading", comment: "Follow with Heading")
return "map.usertrackingmode.followwithheading".localized
}
}
var icon: String {
@ -123,6 +98,7 @@ enum LocationUpdateInterval: Int, CaseIterable, Identifiable {
case tenSeconds = 10
case fifteenSeconds = 15
case thirtySeconds = 30
case fortyFiveSeconds = 45
case oneMinute = 60
case fiveMinutes = 300
case tenMinutes = 600
@ -132,21 +108,23 @@ enum LocationUpdateInterval: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .fiveSeconds:
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
return "interval.five.seconds".localized
case .tenSeconds:
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
return "interval.ten.seconds".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .fortyFiveSeconds:
return "interval.fortyfive.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
}
}
}

View file

@ -16,11 +16,11 @@ enum BluetoothModes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .randomPin:
return NSLocalizedString("bluetooth.mode.randompin", comment: "Random PIN")
return "bluetooth.mode.randompin".localized
case .fixedPin:
return NSLocalizedString("bluetooth.mode.fixedpin", comment: "Fixed PIN")
return "bluetooth.mode.fixedpin".localized
case .noPin:
return NSLocalizedString("bluetooth.mode.nopin", comment: "No PIN (Just Works)")
return "bluetooth.mode.nopin".localized
}
}
func protoEnumValue() -> Config.BluetoothConfig.PairingMode {

View file

@ -18,11 +18,11 @@ enum ConfigPresets: Int, CaseIterable, Identifiable {
switch self {
case .unset:
return NSLocalizedString("canned.messages.preset.manual", comment: "Manual Configuration")
return "canned.messages.preset.manual".localized
case .rakRotaryEncoder:
return NSLocalizedString("canned.messages.preset.rakrotary", comment: "RAK Rotary Encoder Module")
return "canned.messages.preset.rakrotary".localized
case .cardKB:
return NSLocalizedString("canned.messages.preset.cardkb", comment: "M5 Stack Card KB / RAK Keypad")
return "canned.messages.preset.cardkb".localized
}
}
}
@ -44,21 +44,21 @@ enum InputEventChars: Int, CaseIterable, Identifiable {
switch self {
case .none:
return NSLocalizedString("inputevent.none", comment: "None")
return "inputevent.none".localized
case .up:
return NSLocalizedString("inputevent.up", comment: "Up")
return "inputevent.up".localized
case .down:
return NSLocalizedString("inputevent.down", comment: "Down")
return "inputevent.down".localized
case .left:
return NSLocalizedString("inputevent.left", comment: "Left")
return "inputevent.left".localized
case .right:
return NSLocalizedString("inputevent.right", comment: "Right")
return "inputevent.right".localized
case .select:
return NSLocalizedString("inputevent.select", comment: "Select")
return "inputevent.select".localized
case .back:
return NSLocalizedString("inputevent.back", comment: "Back")
return "inputevent.back".localized
case .cancel:
return NSLocalizedString("inputevent.cancel", comment: "Cancel")
return "inputevent.cancel".localized
}
}
func protoEnumValue() -> ModuleConfig.CannedMessageConfig.InputEventChar {

View file

@ -18,11 +18,11 @@ enum ChannelRoles: Int, CaseIterable, Identifiable {
switch self {
case .disabled:
return NSLocalizedString("channel.role.disabled", comment: "Disabled")
return "channel.role.disabled".localized
case .primary:
return NSLocalizedString("channel.role.primary", comment: "Primary")
return "channel.role.primary".localized
case .secondary:
return NSLocalizedString("channel.role.secondary", comment: "Secondary")
return "channel.role.secondary".localized
}
}
func protoEnumValue() -> Channel.Role {

View file

@ -40,19 +40,19 @@ enum DeviceRoles: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .client:
return NSLocalizedString("device.role.client", comment: "Client (default) - App connected client.")
return "device.role.client".localized
case .clientMute:
return NSLocalizedString("device.role.clientmute", comment: "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh.")
return "device.role.clientmute".localized
case .router:
return NSLocalizedString("device.role.router", comment: "Router - Mesh packets will prefer to be routed over this node. Assumes device will operate in a standalone manner while placed in a location with a coverage advantage. WARNING: The BLE/Wi-Fi radios and the OLED screen will be put to sleep.")
return "device.role.router".localized
case .routerClient:
return NSLocalizedString("device.role.routerclient", comment: "Router Client - Hybrid of the Client and Router roles. Similar to Router, except the Router Client can be used as both a Router and an app connected Client. BLE/Wi-Fi and OLED screen will not be put to sleep.")
return "device.role.routerclient".localized
case .repeater:
return NSLocalizedString("device.role.repeater", comment: "Repeater - Mesh packets will prefer to be routed over this node. This role eliminates unnecessary overhead such as NodeInfo, DeviceTelemetry, and any other mesh packet, resulting in the device not appearing as part of the network. Please see Rebroadcast Mode for additional settings specific to this role.")
return "device.role.repeater".localized
case .tracker:
return NSLocalizedString("device.role.tracker", comment: "Tracker - For use with devices intended as a GPS tracker. Position packets sent from this device will be higher priority, with position broadcasting every two minutes. Smart Position Broadcast will default to off.")
return "device.role.tracker".localized
case .sensor:
return NSLocalizedString("device.role.sensor", comment: "Sensor - For use with remote telemetry sensors. Setting this role will turn on environment telemetry. Telemetry packets sent from this device will be higher priority, with telemetry broadcasting every 7 minutes")
return "device.role.sensor".localized
}
}
func protoEnumValue() -> Config.DeviceConfig.Role {

View file

@ -48,23 +48,23 @@ enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
case .thirtyMinutes:
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
return "interval.thirty.minutes".localized
case .oneHour:
return NSLocalizedString("interval.one.hour", comment: "One Hour")
return "interval.one.hour".localized
case .max:
return NSLocalizedString("always.on", comment: "Always On")
return "always.on".localized
}
}
}
@ -83,17 +83,17 @@ enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .off:
return NSLocalizedString("off", comment: "Off")
return "off".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
}
}
}
@ -109,7 +109,7 @@ enum OledTypes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .auto:
return NSLocalizedString("automatic.detection", comment: "Automatic Detection")
return "automatic.detection".localized
case .ssd1306:
return "SSD 1306"
case .sh1106:

View file

@ -38,7 +38,7 @@ enum HardwareModels: String, CaseIterable, Identifiable {
switch self {
case .UNSET:
return NSLocalizedString("unset", comment: "UNSET")
return "unset".localized
case .TLORAV2:
return "TLoRa V2"
case .TLORAV1:

View file

@ -25,25 +25,25 @@ enum OutputIntervals: Int, CaseIterable, Identifiable {
switch self {
case .unset:
return NSLocalizedString("unset", comment: "Unset")
return "unset".localized
case .oneSecond:
return NSLocalizedString("interval.one.second", comment: "One Second")
return "interval.one.second".localized
case .twoSeconds:
return NSLocalizedString("interval.two.seconds", comment: "Two Seconds")
return "interval.two.seconds".localized
case .threeSeconds:
return NSLocalizedString("interval.three.seconds", comment: "Three Seconds")
return "interval.three.seconds".localized
case .fourSeconds:
return NSLocalizedString("interval.four.seconds", comment: "Four Seconds")
return "interval.four.seconds".localized
case .fiveSeconds:
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
return "interval.five.seconds".localized
case .tenSeconds:
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
return "interval.ten.seconds".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
}
}
}
@ -65,23 +65,23 @@ enum SenderIntervals: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .off:
return NSLocalizedString("off", comment: "Off")
return "off".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
case .thirtyMinutes:
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
return "interval.thirty.minutes".localized
case .oneHour:
return NSLocalizedString("interval.one.hour", comment: "One Hour")
return "interval.one.hour".localized
}
}
}
@ -115,47 +115,47 @@ enum UpdateIntervals: Int, CaseIterable, Identifiable {
switch self {
case .tenSeconds:
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
return "interval.ten.seconds".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .twoMinutes:
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
return "interval.two.minutes".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
case .thirtyMinutes:
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
return "interval.thirty.minutes".localized
case .oneHour:
return NSLocalizedString("interval.one.hour", comment: "One Hour")
return "interval.one.hour".localized
case .twoHours:
return NSLocalizedString("interval.two.hours", comment: "Two Hours")
return "interval.two.hours".localized
case .threeHours:
return NSLocalizedString("interval.three.hours", comment: "Three Hours")
return "interval.three.hours".localized
case .fourHours:
return NSLocalizedString("interval.four.hours", comment: "Four Hours")
return "interval.four.hours".localized
case .fiveHours:
return NSLocalizedString("interval.five.hours", comment: "Five Hours")
return "interval.five.hours".localized
case .sixHours:
return NSLocalizedString("interval.six.hours", comment: "Six Hours")
return "interval.six.hours".localized
case .twelveHours:
return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours")
return "interval.twelve.hours".localized
case .eighteenHours:
return NSLocalizedString("interval.eighteen.hours", comment: "Eighteen Hours")
return "interval.eighteen.hours".localized
case .twentyFourHours:
return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours")
return "interval.twentyfour.hours".localized
case .thirtySixHours:
return NSLocalizedString("interval.thirtysix.hours", comment: "Thirty Six Hours")
return "interval.thirtysix.hours".localized
case .fortyeightHours:
return NSLocalizedString("interval.fortyeight.hours", comment: "Forty Eight Hours")
return "interval.fortyeight.hours".localized
case .seventyTwoHours:
return NSLocalizedString("interval.seventytwo.hours", comment: "Seventy Two Hours")
return "interval.seventytwo.hours".localized
}
}
}

View file

@ -43,19 +43,19 @@ enum Tapbacks: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .heart:
return NSLocalizedString("tapback.heart", comment: "Heart")
return "tapback.heart".localized
case .thumbsUp:
return NSLocalizedString("tapback.thumbsup", comment: "Thumbs Up")
return "tapback.thumbsup".localized
case .thumbsDown:
return NSLocalizedString("tapback.thumbsdown", comment: "Thumbs Down")
return "tapback.thumbsdown".localized
case .haHa:
return NSLocalizedString("tapback.haha", comment: "HaHa")
return "tapback.haha".localized
case .exclamation:
return NSLocalizedString("tapback.exclamation", comment: "Exclamation Mark")
return "tapback.exclamation".localized
case .question:
return NSLocalizedString("tapback.question", comment: "Question Mark")
return "tapback.question".localized
case .poop:
return NSLocalizedString("tapback.poop", comment: "Poop")
return "tapback.poop".localized
}
}
}

View file

@ -20,17 +20,17 @@ enum GpsFormats: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .gpsFormatDec:
return NSLocalizedString("gpsformat.dec", comment: "Decimal Degrees Format")
return "gpsformat.dec".localized
case .gpsFormatDms:
return NSLocalizedString("gpsformat.dms", comment: "Degrees Minutes Seconds")
return "gpsformat.dms".localized
case .gpsFormatUtm:
return NSLocalizedString("gpsformat.utm", comment: "Universal Transverse Mercator")
return "gpsformat.utm".localized
case .gpsFormatMgrs:
return NSLocalizedString("gpsformat.mgrs", comment: "Military Grid Reference System")
return "gpsformat.mgrs".localized
case .gpsFormatOlc:
return NSLocalizedString("gpsformat.olc", comment: "Open Location Code (aka Plus Codes)")
return "gpsformat.olc".localized
case .gpsFormatOsgr:
return NSLocalizedString("gpsformat.osgr", comment: "Ordnance Survey Grid Reference")
return "gpsformat.osgr".localized
}
}
func protoEnumValue() -> Config.DisplayConfig.GpsCoordinateFormat {
@ -61,6 +61,7 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
case twentySeconds = 20
case twentyFiveSeconds = 25
case thirtySeconds = 30
case fortyFiveSeconds = 45
case oneMinute = 60
case twoMinutes = 120
case fiveMinutes = 300
@ -77,39 +78,41 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .fiveSeconds:
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
return "interval.five.seconds".localized
case .tenSeconds:
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
return "interval.ten.seconds".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .twentySeconds:
return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds")
return "interval.twenty.seconds".localized
case .twentyFiveSeconds:
return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds")
return "interval.twentyfive.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .fortyFiveSeconds:
return "interval.fortyfive.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .twoMinutes:
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
return "interval.two.minutes".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
case .thirtyMinutes:
return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes")
return "interval.thirty.minutes".localized
case .oneHour:
return NSLocalizedString("interval.one.hour", comment: "One Hour")
return "interval.one.hour".localized
case .sixHours:
return NSLocalizedString("interval.six.hours", comment: "Six Hours")
return "interval.six.hours".localized
case .twelveHours:
return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours")
return "interval.twelve.hours".localized
case .twentyFourHours:
return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours")
return "interval.twentyfour.hours".localized
case .maxInt32:
return NSLocalizedString("on.boot", comment: "On Boot Only")
return "on.boot"
}
}
}
@ -123,6 +126,7 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable {
case twentySeconds = 20
case twentyFiveSeconds = 25
case thirtySeconds = 30
case fortyFiveSeconds = 45
case oneMinute = 60
case twoMinutes = 120
case fiveMinutes = 300
@ -133,29 +137,31 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .twoSeconds:
return NSLocalizedString("interval.two.seconds", comment: "Two Seconds")
return "interval.two.seconds".localized
case .fiveSeconds:
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
return "interval.five.seconds".localized
case .tenSeconds:
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
return "interval.ten.seconds".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .twentySeconds:
return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds")
return "interval.twenty.seconds".localized
case .twentyFiveSeconds:
return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds")
return "interval.twentyfive.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .fortyFiveSeconds:
return "interval.fortyfive.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .twoMinutes:
return NSLocalizedString("interval.two.minutes", comment: "Two Minutes")
return "interval.two.minutes".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
case .tenMinutes:
return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes")
return "interval.ten.minutes".localized
case .fifteenMinutes:
return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes")
return "interval.fifteen.minutes".localized
}
}
}

View file

@ -26,29 +26,29 @@ enum RoutingError: Int, CaseIterable, Identifiable {
switch self {
case .none:
return NSLocalizedString("routing.acknowledged", comment: "Acknowledged")
return "routing.acknowledged".localized
case .noRoute:
return NSLocalizedString("routing.noroute", comment: "No Route")
return "routing.noroute".localized
case .gotNak:
return NSLocalizedString("routing.gotnak", comment: "Received a negative acknowledgment")
return "routing.gotnak".localized
case .timeout:
return NSLocalizedString("routing.timeout", comment: "Timeout")
return "routing.timeout".localized
case .noInterface:
return NSLocalizedString("routing.nointerface", comment: "No Interface")
return "routing.nointerface".localized
case .maxRetransmit:
return NSLocalizedString("routing.maxretransmit", comment: "Max Retransmission Reached")
return "routing.maxretransmit".localized
case .noChannel:
return NSLocalizedString("routing.nochannel", comment: "No Channel")
return "routing.nochannel".localized
case .tooLarge:
return NSLocalizedString("routing.toolarge", comment: "The packet is too large")
return "routing.toolarge".localized
case .noResponse:
return NSLocalizedString("routing.noresponse", comment: "No Response")
return "routing.noresponse".localized
case .dutyCycleLimit:
return NSLocalizedString("routing.dutycyclelimit", comment: "Regional Duty Cycle Limit Reached")
return "routing.dutycyclelimit".localized
case .badRequest:
return NSLocalizedString("routing.badRequest", comment: "Bad Request")
return "routing.badRequest".localized
case .notAuthorized:
return NSLocalizedString("routing.notauthorized", comment: "Not Authorized")
return "routing.notauthorized".localized
}
}
func protoEnumValue() -> Routing.Error {

View file

@ -30,7 +30,7 @@ enum SerialBaudRates: Int, CaseIterable, Identifiable {
switch self {
case .baudDefault:
return NSLocalizedString("default", comment: "Default")
return "default".localized
case .baud110:
return "110 Baud"
case .baud300:
@ -116,15 +116,15 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .default:
return NSLocalizedString("serial.mode.default", comment: "Default")
return "serial.mode.default".localized
case .simple:
return NSLocalizedString("serial.mode.simple", comment: "Simple")
return "serial.mode.simple".localized
case .proto:
return NSLocalizedString("serial.mode.proto", comment: "Protobufs")
return "serial.mode.proto".localized
case .txtmsg:
return NSLocalizedString("serial.mode.txtmsg", comment: "Text Message")
return "serial.mode.txtmsg".localized
case .nmea:
return NSLocalizedString("serial.mode.nmea", comment: "NMEA Positions")
return "serial.mode.nmea".localized
}
}
func protoEnumValue() -> ModuleConfig.SerialConfig.Serial_Mode {
@ -160,21 +160,21 @@ enum SerialTimeoutIntervals: Int, CaseIterable, Identifiable {
var description: String {
switch self {
case .unset:
return NSLocalizedString("unset", comment: "Unset")
return "unset".localized
case .oneSecond:
return NSLocalizedString("interval.one.second", comment: "One Second")
return "interval.one.second".localized
case .fiveSeconds:
return NSLocalizedString("interval.five.seconds", comment: "Five Seconds")
return "interval.five.seconds".localized
case .tenSeconds:
return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds")
return "interval.ten.seconds".localized
case .fifteenSeconds:
return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds")
return "interval.fifteen.seconds".localized
case .thirtySeconds:
return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds")
return "interval.thirty.seconds".localized
case .oneMinute:
return NSLocalizedString("interval.one.minute", comment: "One Minute")
return "interval.one.minute".localized
case .fiveMinutes:
return NSLocalizedString("interval.five.minutes", comment: "Five Minutes")
return "interval.five.minutes".localized
}
}
}

View file

@ -13,7 +13,7 @@ func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
if metricsType == 0 {
// Create Device Metrics Header
csvString = "\(NSLocalizedString("battery.level", comment: "")), \(NSLocalizedString("voltage", comment: "")), \(NSLocalizedString("channel.utilization", comment: "")), \(NSLocalizedString("airtime", comment: "")), \(NSLocalizedString("timestamp", comment: ""))"
csvString = "\("battery.level".localized), \("voltage".localized), \("channel.utilization".localized), \("airtime".localized), \("timestamp".localized)"
for dm in telemetry {
if dm.metricsType == 0 {
csvString += "\n"
@ -25,12 +25,12 @@ func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
csvString += ", "
csvString += String(dm.airUtilTx)
csvString += ", "
csvString += dm.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: "")
csvString += dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized
}
}
} else if metricsType == 1 {
// Create Environment Telemetry Header
csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, \(NSLocalizedString("voltage", comment: "")), \(NSLocalizedString("current", comment: "")), \(NSLocalizedString("timestamp", comment: ""))"
csvString = "Temperature, Relative Humidity, Barometric Pressure, Gas Resistance, \("voltage".localized), \("current".localized), \("timestamp".localized)"
for dm in telemetry {
if dm.metricsType == 1 {
csvString += "\n"
@ -46,7 +46,7 @@ func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin
csvString += ", "
csvString += String(dm.current)
csvString += ", "
csvString += dm.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: "")
csvString += dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized
}
}
}
@ -58,7 +58,7 @@ func positionToCsvFile(positions: [PositionEntity]) -> String {
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "")
// Create Position Header
csvString = "SeqNo, Latitude, Longitude, Altitude, Sats, Speed, Heading, SNR, \(NSLocalizedString("timestamp", comment: ""))"
csvString = "SeqNo, Latitude, Longitude, Altitude, Sats, Speed, Heading, SNR, \("timestamp".localized)"
for pos in positions {
csvString += "\n"
csvString += String(pos.seqNo)
@ -77,7 +77,7 @@ func positionToCsvFile(positions: [PositionEntity]) -> String {
csvString += ", "
csvString += String(pos.snr)
csvString += ", "
csvString += pos.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: "")
csvString += pos.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized
}
return csvString
}

View file

@ -28,6 +28,8 @@ extension String {
return base64url
}
var localized: String { NSLocalizedString(self, comment: self) }
func onlyEmojis() -> Bool {
return count > 0 && !contains { !$0.isEmoji }
}

View file

@ -34,7 +34,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
private var configNonce: UInt32 = 1
var timeoutTimer: Timer?
var timeoutTimerCount = 0
var timeoutTimerRuns = 0
var positionTimer: Timer?
var lastPosition: CLLocationCoordinate2D?
let emptyNodeNum: UInt32 = 4294967295
@ -100,13 +99,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
}
self.isConnected = false
self.isConnecting = false
self.lastConnectionError = "🚨 " + String.localizedStringWithFormat(NSLocalizedString("ble.connection.timeout %d %@",
comment: "Connection failed after %d attempts to connect to %@. You may need to forget your device under Settings > Bluetooth."),
timeoutTimerCount, name)
self.lastConnectionError = "🚨 " + String.localizedStringWithFormat("ble.connection.timeout %d %@".localized, timeoutTimerCount, name)
MeshLogger.log(lastConnectionError)
self.timeoutTimerCount = 0
self.timeoutTimerRuns += 1
self.startScanning()
} else {
print("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)")
@ -200,9 +195,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
let errorCode = (e as NSError).code
if errorCode == 6 { // CBError.Code.connectionTimeout The connection has timed out unexpectedly.
// Happens when device is manually reset / powered off
lastConnectionError = "🚨" + String.localizedStringWithFormat(NSLocalizedString("ble.errorcode.6 %@",
comment: "The app will automatically reconnect to the preferred radio if it come back in range."),
e.localizedDescription)
lastConnectionError = "🚨" + String.localizedStringWithFormat("ble.errorcode.6 %@".localized, e.localizedDescription)
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
} else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us.
// Seems to be what is received when a tbeam sleeps, immediately recconnecting does not work.
@ -210,9 +203,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)")
} else if errorCode == 14 { // Peer removed pairing information
// Forgetting and reconnecting seems to be necessary so we need to show the user an error telling them to do that
lastConnectionError = "🚨 " + String.localizedStringWithFormat(NSLocalizedString("ble.errorcode.14 %@",
comment: "This error usually cannot be fixed without forgetting the device unders Settings > Bluetooth and re-connecting to the radio."),
e.localizedDescription)
lastConnectionError = "🚨 " + String.localizedStringWithFormat("ble.errorcode.14 %@".localized, e.localizedDescription)
print("🚨 BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(lastConnectionError)")
} else {
lastConnectionError = "🚨 \(e.localizedDescription)"
@ -297,7 +288,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛎️ Requested Device Metadata for node \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown")) by \(fromUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
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) {
return Int64(meshPacket.id)
}
@ -330,8 +321,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.traceroute.sent %@",
comment: "Sent a Trace Route Request to node: %@"), String(destNum))
let logString = String.localizedStringWithFormat("mesh.log.traceroute.sent %@".localized, String(destNum))
MeshLogger.log("🪧 \(logString)")
}
return success
@ -341,13 +331,13 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
guard connectedPeripheral!.peripheral.state == CBPeripheralState.connected else { return }
if FROMRADIO_characteristic == nil {
MeshLogger.log("🚨 \(NSLocalizedString("firmware.version.unsupported", comment: "Unsupported Firmware Version Detected, unable to connect to device."))")
MeshLogger.log("🚨 \("firmware.version.unsupported".localized)")
invalidVersion = true
return
} else {
let nodeName = connectedPeripheral!.peripheral.name ?? NSLocalizedString("unknown", comment: NSLocalizedString("unknown", comment: "Unknown"))
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.wantconfig %@", comment: "Issuing Want Config to %@"), nodeName)
let nodeName = connectedPeripheral?.peripheral.name ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.wantconfig %@".localized, nodeName)
MeshLogger.log("🛎️ \(logString)")
// BLE Characteristics discovered, issue wantConfig
var toRadio: ToRadio = ToRadio()
@ -379,9 +369,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
// BLE PIN connection errors
// 5 CBATTErrorDomain Code=5 "Authentication is insufficient."
// 15 CBATTErrorDomain Code=15 "Encryption is insufficient."
lastConnectionError = "🚨" + String.localizedStringWithFormat(NSLocalizedString("ble.errorcode.pin %@",
comment: "Please try connecting again and check the PIN carefully."),
e.localizedDescription)
lastConnectionError = "🚨" + String.localizedStringWithFormat("ble.errorcode.pin %@".localized, e.localizedDescription)
print("🚨 \(e.localizedDescription) Please try connecting again and check the PIN carefully.")
self.disconnectPeripheral(reconnect: false)
}
@ -426,7 +414,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame
if !supportedVersion {
invalidVersion = true
lastConnectionError = "🚨" + NSLocalizedString("update.firmware", comment: "Update Your Firmware")
lastConnectionError = "🚨" + "update.firmware".localized
return
} else {
@ -435,9 +423,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if myInfo != nil {
connectedPeripheral.num = myInfo!.myNodeNum
connectedPeripheral.firmwareVersion = myInfo?.firmwareVersion ?? NSLocalizedString("unknown", comment: "Unknown")
connectedPeripheral.name = myInfo?.bleName ?? NSLocalizedString("unknown", comment: "Unknown")
connectedPeripheral.longName = myInfo?.bleName ?? NSLocalizedString("unknown", comment: "Unknown")
connectedPeripheral.firmwareVersion = myInfo?.firmwareVersion ?? "unknown".localized
connectedPeripheral.name = myInfo?.bleName ?? "unknown".localized
connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized
}
}
tryClearExistingChannels()
@ -451,7 +439,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num {
if nodeInfo!.user != nil {
connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "????"
connectedPeripheral.longName = nodeInfo?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
connectedPeripheral.longName = nodeInfo?.user?.longName ?? "unknown".localized
}
}
}
@ -530,8 +518,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if let routingMessage = try? RouteDiscovery(serializedData: decodedInfo.packet.decoded.payload) {
if routingMessage.route.count == 0 {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.traceroute.received.direct %@",
comment: "Trace Route request sent to node: %@ was recieived directly."), String(decodedInfo.packet.from))
let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.direct %@".localized, String(decodedInfo.packet.from))
MeshLogger.log("🪧 \(logString)")
} else {
@ -540,11 +527,12 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
routeString += "\(node) --> "
}
routeString += "\(decodedInfo.packet.from)"
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.traceroute.received.route %@",
comment: "Trace Route request returned: %@"), routeString)
let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString)
MeshLogger.log("🪧 \(logString)")
}
}
case .neighborinfoApp:
MeshLogger.log("🕸️ MESH PACKET received for Neighbor Info App App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")")
case .UNRECOGNIZED:
MeshLogger.log("🕸️ MESH PACKET received for Other App UNHANDLED \(try! decodedInfo.packet.jsonString())")
case .max:
@ -570,7 +558,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce {
invalidVersion = false
lastConnectionError = ""
timeoutTimerRuns = 0
isSubscribed = true
print("🤜 Want Config Complete. ID:\(decodedInfo.configCompleteID)")
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
@ -578,11 +565,11 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
// MARK: Share Location Position Update Timer
// Use context to pass the radio name with the timer
// Use a RunLoop to prevent the timer from running on the main UI thread
if UserDefaults.provideLocation ?? false {
if UserDefaults.provideLocation {
if positionTimer != nil {
positionTimer!.invalidate()
}
positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval((UserDefaults.provideLocationInterval ?? 900)), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true)
positionTimer = Timer.scheduledTimer(timeInterval: TimeInterval((UserDefaults.provideLocationInterval )), target: self, selector: #selector(positionTimerFired), userInfo: context, repeats: true)
if positionTimer != nil {
RunLoop.current.add(positionTimer!, forMode: .common)
}
@ -615,9 +602,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if preferredPeripheral != nil && preferredPeripheral?.peripheral != nil {
connectTo(peripheral: preferredPeripheral!.peripheral)
}
let nodeName = connectedPeripheral?.peripheral.name ?? NSLocalizedString("unknown", comment: NSLocalizedString("unknown", comment: "Unknown"))
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.textmessage.send.failed %@",
comment: "Message Send Failed, not properly connected to %@"), nodeName)
let nodeName = connectedPeripheral?.peripheral.name ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.textmessage.send.failed %@".localized, nodeName)
MeshLogger.log("🚫 \(logString)")
success = false
@ -693,7 +679,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.textmessage.sent %@ %@ %@", comment: "Sent message %@ from %@ to %@"), String(newMessage.messageId), String(fromUserNum), String(toUserNum))
let logString = String.localizedStringWithFormat("mesh.log.textmessage.sent %@ %@ %@".localized, String(fromUserNum), String(toUserNum))
MeshLogger.log("💬 \(logString)")
do {
try context!.save()
@ -733,7 +719,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.sent %@", comment: "Sent a Waypoint Packet from: %@"), String(fromNodeNum))
let logString = String.localizedStringWithFormat("mesh.log.waypoint.sent %@".localized, String(fromNodeNum))
MeshLogger.log("📍 \(logString)")
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
@ -819,7 +805,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
success = true
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.sharelocation %@", comment: "Sent a Position Packet from the Apple device GPS to node: %@"), String(fromNodeNum))
let logString = String.localizedStringWithFormat("mesh.log.sharelocation %@".localized, String(fromNodeNum))
MeshLogger.log("📍 \(logString)")
}
return success
@ -848,7 +834,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Shutdown Admin Message to: \(toUser.longName ?? NSLocalizedString("unknown", comment: "")) from: \(fromUser.longName ?? NSLocalizedString("unknown", comment: ""))"
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) {
return true
}
@ -869,7 +855,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Reboot Admin Message to: \(toUser.longName ?? NSLocalizedString("unknown", comment: "")) from: \(fromUser.longName ?? NSLocalizedString("unknown", comment: ""))"
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) {
return true
}
@ -890,7 +876,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Reboot OTA Admin Message to: \(toUser.longName ?? NSLocalizedString("unknown", comment: "")) from: \(fromUser.longName ?? NSLocalizedString("unknown", comment: ""))"
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) {
return true
}
@ -911,7 +897,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent Factory Reset Admin Message to: \(toUser.longName ?? NSLocalizedString("unknown", comment: "")) from: \(fromUser.longName ?? NSLocalizedString("unknown", comment: ""))"
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) {
return true
}
@ -932,7 +918,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🚀 Sent NodeDB Reset Admin Message to: \(toUser.longName ?? NSLocalizedString("unknown", comment: "")) from: \(fromUser.longName ?? NSLocalizedString("unknown", comment: ""))"
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) {
return true
}
@ -972,7 +958,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🎛️ Requested Channel \(channel.index) for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🎛️ Requested Channel \(channel.index) for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
@ -993,7 +979,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Channel \(channel.index) for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Channel \(channel.index) for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
@ -1041,7 +1027,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.channel.sent %@ %d", comment: "Sent a Channel for: %@ Channel Index %d"), String(connectedPeripheral.num), chan.index)
let logString = String.localizedStringWithFormat("mesh.log.channel.sent %@ %d".localized, String(connectedPeripheral.num), chan.index)
MeshLogger.log("🎛️ \(logString)")
}
}
@ -1065,7 +1051,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.lora.config.sent %@", comment: "Sent a LoRaConfig for: %@"), String(connectedPeripheral.num))
let logString = String.localizedStringWithFormat("mesh.log.lora.config.sent %@".localized, String(connectedPeripheral.num))
MeshLogger.log("📻 \(logString)")
}
return true
@ -1091,7 +1077,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved User Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved User Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
@ -1112,7 +1098,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Ham Parameters for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Ham Parameters for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
return Int64(meshPacket.id)
}
@ -1132,7 +1118,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Bluetooth Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Bluetooth Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertBluetoothConfigPacket(config: config, nodeNum: toUser.num, context: context!)
@ -1158,7 +1144,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Device Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Device Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertDeviceConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
@ -1182,7 +1168,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Display Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Display Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertDisplayConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
@ -1205,7 +1191,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved LoRa Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved LoRa Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertLoRaConfigPacket(config: config, nodeNum: toUser.num, context: context!)
@ -1233,7 +1219,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Position Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Position Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertPositionConfigPacket(config: config, nodeNum: toUser.num, context: context!)
@ -1261,7 +1247,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Network Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Network Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertNetworkConfigPacket(config: config, nodeNum: toUser.num, context: context!)
@ -1288,7 +1274,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Canned Message Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Canned Message Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
@ -1316,7 +1302,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
let messageDescription = "🛟 Saved Canned Message Module Messages for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "🛟 Saved Canned Message Module Messages for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
@ -1344,7 +1330,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved External Notification Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "Saved External Notification Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
@ -1369,7 +1355,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved RTTTL Ringtone Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "Saved RTTTL Ringtone Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: toUser.num, context: context!)
@ -1398,7 +1384,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
meshPacket.decoded = dataMessage
let messageDescription = "Saved WiFi Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "Saved WiFi Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertMqttModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
@ -1423,7 +1409,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Range Test Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "Saved Range Test Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertRangeTestModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
@ -1451,7 +1437,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Serial Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "Saved Serial Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertSerialModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
@ -1477,7 +1463,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? NSLocalizedString("unknown", comment: "Unknown"))"
let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? "unknown".localized)"
if sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription, fromUser: fromUser, toUser: toUser) {
upsertTelemetryModuleConfigPacket(config: config, nodeNum: toUser.num, context: context!)
return Int64(meshPacket.id)
@ -1542,7 +1528,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessages.messages.get %@", comment: "Requested Canned Messages Module Messages for node: %@"), String(connectedPeripheral.num))
let logString = String.localizedStringWithFormat("mesh.log.cannedmessages.messages.get %@".localized, String(connectedPeripheral.num))
MeshLogger.log("🥫 \(logString)")
return true
}
@ -1999,7 +1985,7 @@ extension BLEManager: CBCentralManagerDelegate {
// Called each time a peripheral is discovered
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
if self.automaticallyReconnect && timeoutTimerRuns < 2 && peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
if self.automaticallyReconnect && peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
self.connectTo(peripheral: peripheral)
print(" BLE Reconnecting to prefered peripheral: \(peripheral.name ?? "Unknown")")
}

View file

@ -73,7 +73,7 @@ func moduleConfig (config: ModuleConfig, context: NSManagedObjectContext, nodeNu
func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedObjectContext) -> MyInfoEntity? {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.myinfo %@", comment: "MyInfo received: %@"), String(myInfo.myNodeNum))
let logString = String.localizedStringWithFormat("mesh.log.myinfo %@".localized, String(myInfo.myNodeNum))
MeshLogger.log(" \(logString)")
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
@ -143,7 +143,7 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.channel.received %d %@", comment: "Channel %d received from: %@"), channel.index, String(fromNum))
let logString = String.localizedStringWithFormat("mesh.log.channel.received %d %@", channel.index, String(fromNum))
MeshLogger.log("🎛️ \(logString)")
let fetchedMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
@ -194,7 +194,7 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo
func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NSManagedObjectContext) {
if metadata.isInitialized {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.metadata.received %@", comment: "Device Metadata admin message received from: %@"), String(fromNum))
let logString = String.localizedStringWithFormat("mesh.log.device.metadata.received %@".localized, String(fromNum))
MeshLogger.log("🏷️ \(logString)")
let fetchedNodeRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -233,7 +233,7 @@ func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, context: NS
func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObjectContext) -> NodeInfoEntity? {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(nodeInfo.num))
let logString = String.localizedStringWithFormat("mesh.log.nodeinfo.received %@".localized, String(nodeInfo.num))
MeshLogger.log("📟 \(logString)")
guard nodeInfo.num > 0 else { return nil }
@ -404,7 +404,7 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) {
if !cmmc.messages.isEmpty {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessages.messages.received %@", comment: "Canned Messages Messages Received For: %@"), String(packet.from))
let logString = String.localizedStringWithFormat("mesh.log.cannedmessages.messages.received %@".localized, String(packet.from))
MeshLogger.log("🥫 \(logString)")
let fetchNodeRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -533,8 +533,8 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
let routingError = RoutingError(rawValue: routingMessage.errorReason.rawValue)
let routingErrorString = routingError?.display ?? NSLocalizedString("unknown", comment: "")
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.routing.message %@ %@", comment: "Routing received for RequestID: %@ Ack Status: %@"), String(packet.decoded.requestID), routingErrorString)
let routingErrorString = routingError?.display ?? "unknown".localized
let logString = String.localizedStringWithFormat("mesh.log.routing.message %@ %@".localized, String(packet.decoded.requestID), routingErrorString)
MeshLogger.log("🕸️ \(logString)")
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
@ -599,7 +599,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
// Only log telemetry from the mesh not the connected device
if connectedNode != Int64(packet.from) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.received %@", comment: "Telemetry received for: %@"), String(packet.from))
let logString = String.localizedStringWithFormat("mesh.log.telemetry.received %@".localized, String(packet.from))
MeshLogger.log("📈 \(logString)")
} else {
// If it is the connected node
@ -635,6 +635,8 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage
telemetry.voltage = telemetryMessage.environmentMetrics.voltage
telemetry.metricsType = 1
}
telemetry.snr = packet.rxSnr
telemetry.rssi = packet.rxRssi
telemetry.time = Date(timeIntervalSince1970: TimeInterval(Int64(telemetryMessage.time)))
guard let mutableTelemetries = fetchedNode[0].telemetries!.mutableCopy() as? NSMutableOrderedSet else {
return
@ -703,7 +705,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
if let messageText = String(bytes: packet.decoded.payload, encoding: .utf8) {
MeshLogger.log("💬 \(NSLocalizedString("mesh.log.textmessage.received", comment: "Message received from the text message app"))")
MeshLogger.log("💬 \("mesh.log.textmessage.received".localized)")
let messageUsers: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
messageUsers.predicate = NSPredicate(format: "num IN %@", [packet.to, packet.from])
@ -716,6 +718,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
newMessage.messageTimestamp = Int32(bitPattern: packet.rxTime)
newMessage.receivedACK = false
newMessage.snr = packet.rxSnr
newMessage.rssi = packet.rxRssi
newMessage.isEmoji = packet.decoded.emoji == 1
newMessage.channel = Int32(packet.channel)
@ -751,12 +754,12 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))",
title: "\(newMessage.fromUser?.longName ?? "unknown".localized)",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
@ -778,12 +781,12 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
manager.notifications = [
Notification(
id: ("notification.id.\(newMessage.messageId)"),
title: "\(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))",
title: "\(newMessage.fromUser?.longName ?? "unknown".localized)",
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
content: messageText)
]
manager.schedule()
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
}
}
}
@ -805,7 +808,7 @@ func textMessageAppPacket(packet: MeshPacket, connectedNode: Int64, context: NSM
func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.waypoint.received %@", comment: "Waypoint Packet received from node: %@"), String(packet.from))
let logString = String.localizedStringWithFormat("mesh.log.waypoint.received %@".localized, String(packet.from))
MeshLogger.log("📍 \(logString)")
let fetchWaypointRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "WaypointEntity")

View file

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>MeshtasticDataModelV11.xcdatamodel</string>
<string>MeshtasticDataModelV12.xcdatamodel</string>
</dict>
</plist>

View file

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22E261" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
<attribute name="downlinkEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<attribute name="psk" optional="YES" attributeType="Binary"/>
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="uplinkEnabled" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
<fetchedProperty name="allPrivateMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="channel == $FETCH_SOURCE.index &amp;&amp; toUser == nil AND isEmoji == false"/>
</fetchedProperty>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="index"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hwModel" optional="YES" attributeType="String"/>
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<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="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 32" usesScalarValueType="YES"/>
<attribute name="latitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="longitude" optional="YES" 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="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="adminDescription" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="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="password" optional="YES" attributeType="String" maxValueString="30"/>
<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="bitrate" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="errorCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" attributeType="String"/>
<attribute name="hasGps" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="maxChannels" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="messageTimeoutMsec" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="peripheralId" optional="YES" attributeType="String"/>
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="myNodeNum"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="ntpServer" optional="YES" attributeType="String"/>
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
<attribute name="bleName" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="peripheralId" optional="YES" attributeType="String"/>
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
<relationship name="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="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="num"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="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="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="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="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 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="date" 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" maxCount="1" deletionRule="Nullify" 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="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES" codeGenerationType="class">
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="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="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
</entity>
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
<attribute name="hwModel" attributeType="String"/>
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longName" attributeType="String"/>
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shortName" attributeType="String"/>
<attribute name="userId" attributeType="String"/>
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
<fetchedProperty name="adminMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="(fromUser.num == $FETCH_SOURCE.num) AND isEmoji == false AND admin = true"/>
</fetchedProperty>
<fetchedProperty name="allMessages" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="((toUser.num == $FETCH_SOURCE.num) OR (fromUser.num == $FETCH_SOURCE.num)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false"/>
</fetchedProperty>
</entity>
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
</entity>
</model>

View file

@ -59,6 +59,6 @@ extension PositionEntity {
extension PositionEntity: MKAnnotation {
public var coordinate: CLLocationCoordinate2D { nodeCoordinate ?? LocationHelper.DefaultLocation }
public var title: String? { nodePosition?.user?.shortName ?? NSLocalizedString("unknown", comment: "Unknown") }
public var title: String? { nodePosition?.user?.shortName ?? "unknown".localized }
public var subtitle: String? { time?.formatted() }
}

View file

@ -99,7 +99,7 @@ public func clearCoreDataDatabase(context: NSManagedObjectContext) {
func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.nodeinfo.received %@", comment: "Node info received for: %@"), String(packet.from))
let logString = String.localizedStringWithFormat("mesh.log.nodeinfo.received %@".localized, String(packet.from))
MeshLogger.log("📟 \(logString)")
guard packet.from > 0 else { return }
@ -117,6 +117,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
newNode.num = Int64(packet.from)
newNode.lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
newNode.snr = packet.rxSnr
newNode.rssi = packet.rxRssi
if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
newNode.channel = Int32(nodeInfoMessage.channel)
@ -138,6 +139,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
fetchedNode[0].num = Int64(packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(packet.rxTime)))
fetchedNode[0].snr = packet.rxSnr
fetchedNode[0].rssi = packet.rxRssi
if let nodeInfoMessage = try? NodeInfo(serializedData: packet.decoded.payload) {
@ -177,7 +179,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.received %@", comment: "Position Packet received from node: %@"), String(packet.from))
let logString = String.localizedStringWithFormat("mesh.log.position.received %@".localized, String(packet.from))
MeshLogger.log("📍 \(logString)")
let fetchNodePositionRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -210,6 +212,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
let position = PositionEntity(context: context)
position.latest = true
position.snr = packet.rxSnr
position.rssi = packet.rxRssi
position.seqNo = Int32(positionMessage.seqNumber)
position.latitudeI = positionMessage.latitudeI
position.longitudeI = positionMessage.longitudeI
@ -230,6 +233,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
fetchedNode[0].num = Int64(packet.from)
fetchedNode[0].lastHeard = Date(timeIntervalSince1970: TimeInterval(Int64(positionMessage.time)))
fetchedNode[0].snr = packet.rxSnr
fetchedNode[0].rssi = packet.rxRssi
fetchedNode[0].positions = mutablePositions.copy() as? NSOrderedSet
do {
@ -258,7 +262,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext)
func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.bluetooth.config %@", comment: "Bluetooth config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.bluetooth.config %@".localized, String(nodeNum))
MeshLogger.log("📶 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -300,7 +304,7 @@ func upsertBluetoothConfigPacket(config: Meshtastic.Config.BluetoothConfig, node
func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.device.config %@", comment: "Device config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.device.config %@".localized, String(nodeNum))
MeshLogger.log("📟 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
@ -349,7 +353,7 @@ func upsertDeviceConfigPacket(config: Meshtastic.Config.DeviceConfig, nodeNum: I
func upsertDisplayConfigPacket(config: Meshtastic.Config.DisplayConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.display.config %@", comment: "Display config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.display.config %@".localized, String(nodeNum))
MeshLogger.log("🖥️ \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -413,7 +417,7 @@ func upsertDisplayConfigPacket(config: Meshtastic.Config.DisplayConfig, nodeNum:
func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.lora.config %@", comment: "LoRa config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.lora.config %@".localized, String(nodeNum))
MeshLogger.log("📻 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -478,7 +482,7 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64
func upsertNetworkConfigPacket(config: Meshtastic.Config.NetworkConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.network.config %@", comment: "Network config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.network.config %@".localized, String(nodeNum))
MeshLogger.log("🌐 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -525,7 +529,7 @@ func upsertNetworkConfigPacket(config: Meshtastic.Config.NetworkConfig, nodeNum:
func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.position.config %@", comment: "Positon config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.position.config %@".localized, String(nodeNum))
MeshLogger.log("🗺️ \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -584,7 +588,7 @@ func upsertPositionConfigPacket(config: Meshtastic.Config.PositionConfig, nodeNu
func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.CannedMessageConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.cannedmessage.config %@", comment: "Canned Message module config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.cannedmessage.config %@".localized, String(nodeNum))
MeshLogger.log("🥫 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -648,7 +652,7 @@ func upsertCannedMessagesModuleConfigPacket(config: Meshtastic.ModuleConfig.Cann
func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfig.ExternalNotificationConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.externalnotification.config %@", comment: "External Notifiation module config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.externalnotification.config %@".localized, String(nodeNum))
MeshLogger.log("📣 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -716,7 +720,7 @@ func upsertExternalNotificationModuleConfigPacket(config: Meshtastic.ModuleConfi
func upsertRtttlConfigPacket(ringtone: String, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.ringtone.config %@", comment: "RTTTL Ringtone config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.ringtone.config %@".localized, String(nodeNum))
MeshLogger.log("⛰️ \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -755,7 +759,7 @@ func upsertRtttlConfigPacket(ringtone: String, nodeNum: Int64, context: NSManage
func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.mqtt.config %@", comment: "MQTT module config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.mqtt.config %@".localized, String(nodeNum))
MeshLogger.log("🌉 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -775,16 +779,20 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no
newMQTTConfig.address = config.address
newMQTTConfig.username = config.username
newMQTTConfig.password = config.password
newMQTTConfig.root = config.root
newMQTTConfig.encryptionEnabled = config.encryptionEnabled
newMQTTConfig.jsonEnabled = config.jsonEnabled
newMQTTConfig.tlsEnabled = config.tlsEnabled
fetchedNode[0].mqttConfig = newMQTTConfig
} else {
fetchedNode[0].mqttConfig?.enabled = config.enabled
fetchedNode[0].mqttConfig?.address = config.address
fetchedNode[0].mqttConfig?.username = config.username
fetchedNode[0].mqttConfig?.password = config.password
fetchedNode[0].mqttConfig?.root = config.root
fetchedNode[0].mqttConfig?.encryptionEnabled = config.encryptionEnabled
fetchedNode[0].mqttConfig?.jsonEnabled = config.jsonEnabled
fetchedNode[0].mqttConfig?.tlsEnabled = config.tlsEnabled
}
do {
try context.save()
@ -805,7 +813,7 @@ func upsertMqttModuleConfigPacket(config: Meshtastic.ModuleConfig.MQTTConfig, no
func upsertRangeTestModuleConfigPacket(config: Meshtastic.ModuleConfig.RangeTestConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.rangetest.config %@", comment: "Range Test module config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.rangetest.config %@".localized, String(nodeNum))
MeshLogger.log("⛰️ \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -848,7 +856,7 @@ func upsertRangeTestModuleConfigPacket(config: Meshtastic.ModuleConfig.RangeTest
func upsertSerialModuleConfigPacket(config: Meshtastic.ModuleConfig.SerialConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.serial.config %@", comment: "Serial module config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.serial.config %@".localized, String(nodeNum))
MeshLogger.log("🤖 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -911,7 +919,7 @@ func upsertSerialModuleConfigPacket(config: Meshtastic.ModuleConfig.SerialConfig
func upsertTelemetryModuleConfigPacket(config: Meshtastic.ModuleConfig.TelemetryConfig, nodeNum: Int64, context: NSManagedObjectContext) {
let logString = String.localizedStringWithFormat(NSLocalizedString("mesh.log.telemetry.config %@", comment: "Telemetry module config received: %@"), String(nodeNum))
let logString = String.localizedStringWithFormat("mesh.log.telemetry.config %@".localized, String(nodeNum))
MeshLogger.log("📈 \(logString)")
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
@ -928,17 +936,14 @@ func upsertTelemetryModuleConfigPacket(config: Meshtastic.ModuleConfig.Telemetry
if fetchedNode[0].telemetryConfig == nil {
let newTelemetryConfig = TelemetryConfigEntity(context: context)
newTelemetryConfig.deviceUpdateInterval = Int32(config.deviceUpdateInterval)
newTelemetryConfig.environmentUpdateInterval = Int32(config.environmentUpdateInterval)
newTelemetryConfig.environmentMeasurementEnabled = config.environmentMeasurementEnabled
newTelemetryConfig.environmentScreenEnabled = config.environmentScreenEnabled
newTelemetryConfig.environmentDisplayFahrenheit = config.environmentDisplayFahrenheit
fetchedNode[0].telemetryConfig = newTelemetryConfig
} else {
fetchedNode[0].telemetryConfig?.deviceUpdateInterval = Int32(config.deviceUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentUpdateInterval = Int32(config.environmentUpdateInterval)
fetchedNode[0].telemetryConfig?.environmentMeasurementEnabled = config.environmentMeasurementEnabled

View file

@ -0,0 +1,163 @@
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: meshtastic/clientonly.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
}
///
/// This abstraction is used to contain any configuration for provisioning a node on any client.
/// It is useful for importing and exporting configurations.
struct DeviceProfile {
// 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.
///
/// Long name for the node
var longName: String {
get {return _longName ?? String()}
set {_longName = newValue}
}
/// Returns true if `longName` has been explicitly set.
var hasLongName: Bool {return self._longName != nil}
/// Clears the value of `longName`. Subsequent reads from it will return its default value.
mutating func clearLongName() {self._longName = nil}
///
/// Short name of the node
var shortName: String {
get {return _shortName ?? String()}
set {_shortName = newValue}
}
/// Returns true if `shortName` has been explicitly set.
var hasShortName: Bool {return self._shortName != nil}
/// Clears the value of `shortName`. Subsequent reads from it will return its default value.
mutating func clearShortName() {self._shortName = nil}
///
/// The url of the channels from our node
var channelURL: String {
get {return _channelURL ?? String()}
set {_channelURL = newValue}
}
/// Returns true if `channelURL` has been explicitly set.
var hasChannelURL: Bool {return self._channelURL != nil}
/// Clears the value of `channelURL`. Subsequent reads from it will return its default value.
mutating func clearChannelURL() {self._channelURL = nil}
///
/// The Config of the node
var config: LocalConfig {
get {return _config ?? LocalConfig()}
set {_config = newValue}
}
/// Returns true if `config` has been explicitly set.
var hasConfig: Bool {return self._config != nil}
/// Clears the value of `config`. Subsequent reads from it will return its default value.
mutating func clearConfig() {self._config = nil}
///
/// The ModuleConfig of the node
var moduleConfig: LocalModuleConfig {
get {return _moduleConfig ?? LocalModuleConfig()}
set {_moduleConfig = newValue}
}
/// Returns true if `moduleConfig` has been explicitly set.
var hasModuleConfig: Bool {return self._moduleConfig != nil}
/// Clears the value of `moduleConfig`. Subsequent reads from it will return its default value.
mutating func clearModuleConfig() {self._moduleConfig = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _longName: String? = nil
fileprivate var _shortName: String? = nil
fileprivate var _channelURL: String? = nil
fileprivate var _config: LocalConfig? = nil
fileprivate var _moduleConfig: LocalModuleConfig? = nil
}
#if swift(>=5.5) && canImport(_Concurrency)
extension DeviceProfile: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "meshtastic"
extension DeviceProfile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".DeviceProfile"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "long_name"),
2: .standard(proto: "short_name"),
3: .standard(proto: "channel_url"),
4: .same(proto: "config"),
5: .standard(proto: "module_config"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self._longName) }()
case 2: try { try decoder.decodeSingularStringField(value: &self._shortName) }()
case 3: try { try decoder.decodeSingularStringField(value: &self._channelURL) }()
case 4: try { try decoder.decodeSingularMessageField(value: &self._config) }()
case 5: try { try decoder.decodeSingularMessageField(value: &self._moduleConfig) }()
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
try { if let v = self._longName {
try visitor.visitSingularStringField(value: v, fieldNumber: 1)
} }()
try { if let v = self._shortName {
try visitor.visitSingularStringField(value: v, fieldNumber: 2)
} }()
try { if let v = self._channelURL {
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
} }()
try { if let v = self._config {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
} }()
try { if let v = self._moduleConfig {
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
} }()
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: DeviceProfile, rhs: DeviceProfile) -> Bool {
if lhs._longName != rhs._longName {return false}
if lhs._shortName != rhs._shortName {return false}
if lhs._channelURL != rhs._channelURL {return false}
if lhs._config != rhs._config {return false}
if lhs._moduleConfig != rhs._moduleConfig {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

View file

@ -159,6 +159,19 @@ struct DeviceState {
set {_uniqueStorage()._didGpsReset = newValue}
}
///
/// We keep the last received waypoint stored in the device flash,
/// so we can show it on the screen.
/// Might be null
var rxWaypoint: MeshPacket {
get {return _storage._rxWaypoint ?? MeshPacket()}
set {_uniqueStorage()._rxWaypoint = newValue}
}
/// Returns true if `rxWaypoint` has been explicitly set.
var hasRxWaypoint: Bool {return _storage._rxWaypoint != nil}
/// Clears the value of `rxWaypoint`. Subsequent reads from it will return its default value.
mutating func clearRxWaypoint() {_uniqueStorage()._rxWaypoint = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -280,6 +293,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
7: .standard(proto: "rx_text_message"),
9: .standard(proto: "no_save"),
11: .standard(proto: "did_gps_reset"),
12: .standard(proto: "rx_waypoint"),
]
fileprivate class _StorageClass {
@ -291,6 +305,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
var _rxTextMessage: MeshPacket? = nil
var _noSave: Bool = false
var _didGpsReset: Bool = false
var _rxWaypoint: MeshPacket? = nil
static let defaultInstance = _StorageClass()
@ -305,6 +320,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
_rxTextMessage = source._rxTextMessage
_noSave = source._noSave
_didGpsReset = source._didGpsReset
_rxWaypoint = source._rxWaypoint
}
}
@ -331,6 +347,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }()
case 9: try { try decoder.decodeSingularBoolField(value: &_storage._noSave) }()
case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }()
case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }()
default: break
}
}
@ -367,6 +384,9 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if _storage._didGpsReset != false {
try visitor.visitSingularBoolField(value: _storage._didGpsReset, fieldNumber: 11)
}
try { if let v = _storage._rxWaypoint {
try visitor.visitSingularMessageField(value: v, fieldNumber: 12)
} }()
}
try unknownFields.traverse(visitor: &visitor)
}
@ -384,6 +404,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati
if _storage._rxTextMessage != rhs_storage._rxTextMessage {return false}
if _storage._noSave != rhs_storage._noSave {return false}
if _storage._didGpsReset != rhs_storage._didGpsReset {return false}
if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false}
return true
}
if !storagesAreEqual {return false}

View file

@ -2231,6 +2231,50 @@ struct Compressed {
init() {}
}
///
/// Full info on edges for a single node
struct NeighborInfo {
// 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 node ID of the node sending info on its neighbors
var nodeID: UInt32 = 0
///
/// Field to pass neighbor info for the next sending cycle
var lastSentByID: UInt32 = 0
///
/// The list of out edges from this node
var neighbors: [Neighbor] = []
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
///
/// A single edge in the mesh
struct Neighbor {
// 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.
///
/// Node ID of neighbor
var nodeID: UInt32 = 0
///
/// SNR of last heard message
var snr: Float = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
///
/// Device metadata response
struct DeviceMetadata {
@ -2307,6 +2351,8 @@ extension FromRadio.OneOf_PayloadVariant: @unchecked Sendable {}
extension ToRadio: @unchecked Sendable {}
extension ToRadio.OneOf_PayloadVariant: @unchecked Sendable {}
extension Compressed: @unchecked Sendable {}
extension NeighborInfo: @unchecked Sendable {}
extension Neighbor: @unchecked Sendable {}
extension DeviceMetadata: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
@ -3896,6 +3942,88 @@ extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
}
}
extension NeighborInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".NeighborInfo"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "node_id"),
2: .standard(proto: "last_sent_by_id"),
3: .same(proto: "neighbors"),
]
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.nodeID) }()
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.lastSentByID) }()
case 3: try { try decoder.decodeRepeatedMessageField(value: &self.neighbors) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.nodeID != 0 {
try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1)
}
if self.lastSentByID != 0 {
try visitor.visitSingularUInt32Field(value: self.lastSentByID, fieldNumber: 2)
}
if !self.neighbors.isEmpty {
try visitor.visitRepeatedMessageField(value: self.neighbors, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: NeighborInfo, rhs: NeighborInfo) -> Bool {
if lhs.nodeID != rhs.nodeID {return false}
if lhs.lastSentByID != rhs.lastSentByID {return false}
if lhs.neighbors != rhs.neighbors {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Neighbor"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "node_id"),
2: .same(proto: "snr"),
]
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.nodeID) }()
case 2: try { try decoder.decodeSingularFloatField(value: &self.snr) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.nodeID != 0 {
try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1)
}
if self.snr != 0 {
try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 2)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: Neighbor, rhs: Neighbor) -> Bool {
if lhs.nodeID != rhs.nodeID {return false}
if lhs.snr != rhs.snr {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".DeviceMetadata"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [

View file

@ -246,6 +246,11 @@ struct ModuleConfig {
/// If true, we attempt to establish a secure connection using TLS
var tlsEnabled: Bool = false
///
/// The root topic to use for MQTT messages. Default is "msh".
/// This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs
var root: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
@ -1114,6 +1119,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
5: .standard(proto: "encryption_enabled"),
6: .standard(proto: "json_enabled"),
7: .standard(proto: "tls_enabled"),
8: .same(proto: "root"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
@ -1129,6 +1135,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
case 5: try { try decoder.decodeSingularBoolField(value: &self.encryptionEnabled) }()
case 6: try { try decoder.decodeSingularBoolField(value: &self.jsonEnabled) }()
case 7: try { try decoder.decodeSingularBoolField(value: &self.tlsEnabled) }()
case 8: try { try decoder.decodeSingularStringField(value: &self.root) }()
default: break
}
}
@ -1156,6 +1163,9 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
if self.tlsEnabled != false {
try visitor.visitSingularBoolField(value: self.tlsEnabled, fieldNumber: 7)
}
if !self.root.isEmpty {
try visitor.visitSingularStringField(value: self.root, fieldNumber: 8)
}
try unknownFields.traverse(visitor: &visitor)
}
@ -1167,6 +1177,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
if lhs.encryptionEnabled != rhs.encryptionEnabled {return false}
if lhs.jsonEnabled != rhs.jsonEnabled {return false}
if lhs.tlsEnabled != rhs.tlsEnabled {return false}
if lhs.root != rhs.root {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}

View file

@ -135,6 +135,10 @@ enum PortNum: SwiftProtobuf.Enum {
/// a certain destination would take on the mesh.
case tracerouteApp // = 70
///
/// Aggregates edge info for the network by sending out a list of each node's neighbors
case neighborinfoApp // = 71
///
/// Private applications should use portnums >= 256.
/// To simplify initial development and testing you can use "PRIVATE_APP"
@ -175,6 +179,7 @@ enum PortNum: SwiftProtobuf.Enum {
case 68: self = .zpsApp
case 69: self = .simulatorApp
case 70: self = .tracerouteApp
case 71: self = .neighborinfoApp
case 256: self = .privateApp
case 257: self = .atakForwarder
case 511: self = .max
@ -203,6 +208,7 @@ enum PortNum: SwiftProtobuf.Enum {
case .zpsApp: return 68
case .simulatorApp: return 69
case .tracerouteApp: return 70
case .neighborinfoApp: return 71
case .privateApp: return 256
case .atakForwarder: return 257
case .max: return 511
@ -236,6 +242,7 @@ extension PortNum: CaseIterable {
.zpsApp,
.simulatorApp,
.tracerouteApp,
.neighborinfoApp,
.privateApp,
.atakForwarder,
.max,
@ -271,6 +278,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding {
68: .same(proto: "ZPS_APP"),
69: .same(proto: "SIMULATOR_APP"),
70: .same(proto: "TRACEROUTE_APP"),
71: .same(proto: "NEIGHBORINFO_APP"),
256: .same(proto: "PRIVATE_APP"),
257: .same(proto: "ATAK_FORWARDER"),
511: .same(proto: "MAX"),

View file

@ -59,10 +59,10 @@ struct Connect: View {
if node != nil {
Text(bleManager.connectedPeripheral.longName).font(.title2)
}
Text("ble.name").font(.callout)+Text(": \(bleManager.connectedPeripheral.peripheral.name ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("ble.name").font(.callout)+Text(": \(bleManager.connectedPeripheral.peripheral.name ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
if node != nil {
Text("firmware.version").font(.callout)+Text(": \(node?.myInfo?.firmwareVersion ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("firmware.version").font(.callout)+Text(": \(node?.myInfo?.firmwareVersion ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
}
if bleManager.isSubscribed {
@ -110,7 +110,7 @@ struct Connect: View {
#endif
Text("Num: \(String(node!.num))")
Text("Short Name: \(node?.user?.shortName ?? "????")")
Text("Long Name: \(node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("Long Name: \(node?.user?.longName ?? "unknown".localized)")
Text("Max Channels: \(String(node?.myInfo?.maxChannels ?? 0))")
Text("Bitrate: \(String(format: "%.2f", node?.myInfo?.bitrate ?? 0.00))")
Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)")

View file

@ -147,11 +147,11 @@ struct NodeInfoView: View {
Divider()
VStack {
if node.user != nil {
Image(node.user!.hwModel ?? NSLocalizedString("unset", comment: "Unset"))
Image(node.user!.hwModel ?? "unset".localized)
.resizable()
.frame(width: 75, height: 75)
.cornerRadius(5)
Text(String(node.user!.hwModel ?? NSLocalizedString("unset", comment: "Unset")))
Text(String(node.user!.hwModel ?? "unset".localized))
.font(.callout).fixedSize()
}
}

View file

@ -309,13 +309,13 @@ struct MapViewSwiftUI: UIViewRepresentable {
}
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
let metersAway = positionAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
subtitle.text! += NSLocalizedString("distance", comment: "") + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
subtitle.text! += "distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
}
subtitle.text! += positionAnnotation.time?.formatted() ?? "Unknown \n"
subtitle.numberOfLines = 0
annotationView.detailCalloutAccessoryView = subtitle
let detailsIcon = UIButton(type: .detailDisclosure)
detailsIcon.setImage(UIImage(systemName: "info.square"), for: .normal)
detailsIcon.setImage(UIImage(systemName: "trash"), for: .normal)
annotationView.rightCalloutAccessoryView = detailsIcon
return annotationView
case let waypointAnnotation as WaypointEntity:
@ -343,7 +343,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
let metersAway = waypointAnnotation.coordinate.distance(from: LocationHelper.currentLocation)
let distanceFormatter = MKDistanceFormatter()
subtitle.text! += NSLocalizedString("distance", comment: "") + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
subtitle.text! += "distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway))) \n"
}
if waypointAnnotation.created != nil {
subtitle.text! += "Created: \(waypointAnnotation.created?.formatted() ?? "Unknown") \n"
@ -365,9 +365,18 @@ struct MapViewSwiftUI: UIViewRepresentable {
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// Only Allow Edit for waypoint annotations with a id
if view.tag > 0 {
parent.onWaypointEdit(view.tag)
switch view.annotation {
case let positionAnnotation as PositionEntity:
print(positionAnnotation)
case let waypointAnnotation as WaypointEntity:
// Only Allow Edit for waypoint annotations with a id
if view.tag > 0 {
parent.onWaypointEdit(view.tag)
}
print(waypointAnnotation)
default: break
}
}

View file

@ -263,7 +263,7 @@ struct ChannelMessageList: View {
.symbolRenderingMode(.hierarchical)
.imageScale(.large).foregroundColor(.accentColor)
}
ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
ProgressView("\("bytes".localized): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
.frame(width: 130)
.padding(5)
.font(.subheadline)
@ -325,7 +325,7 @@ struct ChannelMessageList: View {
.imageScale(.large).foregroundColor(.accentColor)
}
ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
ProgressView("\("bytes".localized): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
.frame(width: 130)
.padding(5)
.font(.subheadline)
@ -377,7 +377,7 @@ struct ChannelMessageList: View {
ToolbarItem(placement: .principal) {
HStack {
CircleText(text: String(channel.index), color: .accentColor, circleSize: 44, fontSize: 30).fixedSize()
Text(String(channel.name ?? NSLocalizedString("unknown", comment: "Unknown")).camelCaseToWords()).font(.headline)
Text(String(channel.name ?? "unknown".localized).camelCaseToWords()).font(.headline)
}
}
ToolbarItem(placement: .navigationBarTrailing) {

View file

@ -152,7 +152,7 @@ struct Contacts: View {
.padding(.trailing, 5)
VStack {
HStack {
Text(user.longName ?? NSLocalizedString("unknown", comment: "Unknown")).font(.headline)
Text(user.longName ?? "unknown".localized).font(.headline)
Spacer()
if user.messageList.count > 0 {
VStack(alignment: .trailing) {

View file

@ -254,7 +254,7 @@ struct UserMessageList: View {
.symbolRenderingMode(.hierarchical)
.imageScale(.large).foregroundColor(.accentColor)
}
ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
ProgressView("\("bytes".localized): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
.frame(width: 130)
.padding(5)
.font(.subheadline)
@ -302,7 +302,7 @@ struct UserMessageList: View {
.symbolRenderingMode(.hierarchical)
.imageScale(.large).foregroundColor(.accentColor)
}
ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
ProgressView("\("bytes".localized): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
.frame(width: 130)
.padding(5)
.font(.subheadline)
@ -354,7 +354,7 @@ struct UserMessageList: View {
ToolbarItem(placement: .principal) {
HStack {
CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44, fontSize: 14, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white ).fixedSize()
Text(user.longName ?? NSLocalizedString("unknown", comment: "Unknown")).font(.headline)
Text(user.longName ?? "unknown".localized).font(.headline)
}
}
ToolbarItem(placement: .navigationBarTrailing) {

View file

@ -112,7 +112,7 @@ struct DeviceMetricsLog: View {
Text("\(String(format: "%.2f", dm.airUtilTx))%")
}
TableColumn("timestamp") { dm in
Text(dm.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
Text(dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
}
.width(min: 180)
}
@ -215,7 +215,7 @@ struct DeviceMetricsLog: View {
isPresented: $isExporting,
document: CsvDocument(emptyCsv: exportString),
contentType: .commaSeparatedText,
defaultFilename: String("\(node.user?.longName ?? "Node") \(NSLocalizedString("device.metrics.log", comment: "Device Metrics Log"))"),
defaultFilename: String("\(node.user?.longName ?? "Node") \("device.metrics.log".localized)"),
onCompletion: { result in
if case .success = result {
print("Device metrics log download succeeded.")

View file

@ -101,7 +101,7 @@ struct EnvironmentMetricsLog: View {
Text("\(String(format: "%.2f", em.voltage))")
}
TableColumn("timestamp") { em in
Text(em.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
Text(em.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
}
.width(min: 180)
}
@ -145,7 +145,7 @@ struct EnvironmentMetricsLog: View {
.font(.caption)
Text("\(String(format: "%.2f", em.gasResistance))")
.font(.caption)
Text(em.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
Text(em.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
.font(.caption2)
}
}

View file

@ -215,7 +215,7 @@ struct NodeDetail: View {
.presentationDetents([.medium, .large])
.presentationDragIndicator(.automatic)
})
.navigationBarTitle(String(node.user?.longName ?? NSLocalizedString("unknown", comment: "")), displayMode: .inline)
.navigationBarTitle(String(node.user?.longName ?? "unknown".localized), displayMode: .inline)
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(

View file

@ -38,7 +38,7 @@ struct NodeList: View {
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: 20, brightness: 0.0, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white)
.padding(.trailing, 5)
VStack(alignment: .leading) {
Text(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")).font(.headline)
Text(node.user?.longName ?? "unknown".localized).font(.headline)
if connected {
HStack(alignment: .bottom) {
Image(systemName: "repeat.circle.fill")

View file

@ -61,7 +61,7 @@ struct PositionLog: View {
Text("\(String(format: "%.2f", position.snr)) dB")
}
TableColumn("Time Stamp") { position in
Text(position.time?.formattedDate(format: dateFormatString) ?? NSLocalizedString("unknown.age", comment: ""))
Text(position.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
}
.width(min: 180)
}

View file

@ -30,7 +30,7 @@ struct AdminMessageList: View {
VStack(alignment: .leading) {
Text("\(am.adminDescription ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("\(am.adminDescription ?? "unknown".localized)")
.font(.caption)
Text("Sent \(Date(timeIntervalSince1970: TimeInterval(am.messageTimestamp)).formattedDate(format: dateFormatString))")

View file

@ -110,8 +110,8 @@ struct BluetoothConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
if connectedNode != nil {

View file

@ -204,8 +204,8 @@ struct DeviceConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
if connectedNode != nil {
@ -309,7 +309,7 @@ struct DeviceConfig: View {
self.buttonGPIO = Int(node?.deviceConfig?.buttonGpio ?? 0)
self.buzzerGPIO = Int(node?.deviceConfig?.buzzerGpio ?? 0)
self.rebroadcastMode = Int(node?.deviceConfig?.rebroadcastMode ?? 0)
self.doubleTapAsButtonPress = node!.deviceConfig?.doubleTapAsButtonPress ?? false
self.doubleTapAsButtonPress = node?.deviceConfig?.doubleTapAsButtonPress ?? false
self.hasChanges = false
}
}

View file

@ -149,8 +149,8 @@ struct DisplayConfig: View {
"are.you.sure",
isPresented: $isPresentingSaveConfirm
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
if connectedNode != nil {

View file

@ -189,8 +189,8 @@ struct LoRaConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context)
if connectedNode != nil {

View file

@ -207,8 +207,8 @@ struct CannedMessagesConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if hasChanges {

View file

@ -180,8 +180,8 @@ struct ExternalNotificationConfig: View {
) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if connectedNode != nil {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
var enc = ModuleConfig.ExternalNotificationConfig()
enc.enabled = enabled

View file

@ -20,6 +20,9 @@ struct MQTTConfig: View {
@State var password = ""
@State var encryptionEnabled = false
@State var jsonEnabled = false
@State var tlsEnabled = true
@State var root = "msh"
var body: some View {
@ -63,11 +66,18 @@ struct MQTTConfig: View {
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Toggle(isOn: $tlsEnabled) {
Label("TLS Enabled", systemImage: "checkmark.shield.fill")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Toggle(isOn: $jsonEnabled) {
Label("JSON Enabled", systemImage: "ellipsis.curlybraces")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
Text("JSON mode is not reccomended it is incomplete and unstable.")
.font(.caption2)
}
Section(header: Text("Custom Server")) {
HStack {
@ -147,6 +157,29 @@ struct MQTTConfig: View {
}
.keyboardType(.default)
.scrollDismissesKeyboard(.interactively)
HStack {
Label("Root Topic", systemImage: "tree")
TextField("Root Topic", text: $root)
.foregroundColor(.gray)
.onChange(of: root, perform: { _ in
let totalBytes = root.utf8.count
// Only mess with the value if it is too big
if totalBytes > 14 {
let firstNBytes = Data(root.utf8.prefix(14))
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
// Set the shortName back to the last place where it was the right size
root = maxBytesString
}
}
})
.foregroundColor(.gray)
}
.keyboardType(.asciiCapable)
.scrollDismissesKeyboard(.interactively)
.disableAutocorrection(true)
Text("The root topic to use for MQTT messages. Default is \"msh\". This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs")
.font(.caption2)
}
Text("WiFi or Ethernet must also be enabled for MQTT to work. You can set uplink and downlink for each channel.")
.font(.callout)
@ -171,16 +204,18 @@ struct MQTTConfig: View {
) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if connectedNode != nil {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
var mqtt = ModuleConfig.MQTTConfig()
mqtt.enabled = self.enabled
mqtt.address = self.address
mqtt.username = self.username
mqtt.password = self.password
mqtt.root = self.root
mqtt.encryptionEnabled = self.encryptionEnabled
mqtt.jsonEnabled = self.jsonEnabled
mqtt.tlsEnabled = self.tlsEnabled
let adminMessageId = bleManager.saveMQTTConfig(config: mqtt, 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
@ -212,6 +247,26 @@ struct MQTTConfig: View {
}
}
}
.onChange(of: address) { newAddress in
if node != nil && node?.mqttConfig != nil {
if newAddress != node!.mqttConfig!.address { hasChanges = true }
}
}
.onChange(of: username) { newUsername in
if node != nil && node?.mqttConfig != nil {
if newUsername != node!.mqttConfig!.username { hasChanges = true }
}
}
.onChange(of: password) { newPassword in
if node != nil && node?.mqttConfig != nil {
if newPassword != node!.mqttConfig!.password { hasChanges = true }
}
}
.onChange(of: root) { newRoot in
if node != nil && node?.mqttConfig != nil {
if newRoot != node!.mqttConfig!.root { hasChanges = true }
}
}
.onChange(of: enabled) { newEnabled in
if node != nil && node?.mqttConfig != nil {
if newEnabled != node!.mqttConfig!.enabled { hasChanges = true }
@ -227,6 +282,11 @@ struct MQTTConfig: View {
if newJsonEnabled != node!.mqttConfig!.jsonEnabled { hasChanges = true }
}
}
.onChange(of: tlsEnabled) { newTlsEnabled in
if node != nil && node?.mqttConfig != nil {
if newTlsEnabled != node!.mqttConfig!.tlsEnabled { hasChanges = true }
}
}
}
func setMqttValues() {
@ -234,8 +294,10 @@ struct MQTTConfig: View {
self.address = node?.mqttConfig?.address ?? ""
self.username = node?.mqttConfig?.username ?? ""
self.password = node?.mqttConfig?.password ?? ""
self.root = node?.mqttConfig?.root ?? "msh"
self.encryptionEnabled = (node?.mqttConfig?.encryptionEnabled ?? false)
self.jsonEnabled = (node?.mqttConfig?.jsonEnabled ?? false)
self.tlsEnabled = (node?.mqttConfig?.tlsEnabled ?? false)
self.hasChanges = false
}
}

View file

@ -88,8 +88,8 @@ struct RangeTestConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)

View file

@ -92,8 +92,8 @@ struct RtttlConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)

View file

@ -143,8 +143,8 @@ struct SerialConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
if connectedNode != nil {

View file

@ -105,8 +105,8 @@ struct TelemetryConfig: View {
) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
if connectedNode != nil {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
var tc = ModuleConfig.TelemetryConfig()
tc.deviceUpdateInterval = UInt32(deviceUpdateInterval)

View file

@ -136,8 +136,8 @@ struct NetworkConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral.num, context: context)
if connectedNode != nil {

View file

@ -283,8 +283,8 @@ struct PositionConfig: View {
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
let nodeName = node?.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown")
let buttonText = String.localizedStringWithFormat(NSLocalizedString("save.config %@", comment: "Save Config for %@"), nodeName)
let nodeName = node?.user?.longName ?? "unknown".localized
let buttonText = String.localizedStringWithFormat("save.config %@".localized, nodeName)
Button(buttonText) {
if fixedPosition {

View file

@ -70,13 +70,13 @@ struct Settings: View {
}
ForEach(nodes) { node in
if node.num == bleManager.connectedPeripheral?.num ?? 0 {
Text("BLE Config: \(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("BLE Config: \(node.user?.longName ?? "unknown".localized)")
.tag(Int(node.num))
} else if node.metadata != nil {
Text("Remote Config: \(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("Remote Config: \(node.user?.longName ?? "unknown".localized)")
.tag(Int(node.num))
} else {
Text("Request Admin: \(node.user?.longName ?? NSLocalizedString("unknown", comment: "Unknown"))")
Text("Request Admin: \(node.user?.longName ?? "unknown".localized)")
.tag(Int(node.num))
}
}

View file

@ -113,6 +113,7 @@
"interval.twenty.seconds"="Zwanzig Sekunden";
"interval.twentyfive.seconds"="Fünfundzwanzig Sekunden";
"interval.thirty.seconds"="Dreißig Sekunden";
"interval.fortyfive.seconds"="Forty Five Seconds";
"interval.one.minute"="Eine Minute";
"interval.two.minutes"="Zwei Minutes";
"interval.five.minutes"="Fünf Minutes";

View file

@ -113,6 +113,7 @@
"interval.twenty.seconds"="Twenty Seconds";
"interval.twentyfive.seconds"="Twenty Five Seconds";
"interval.thirty.seconds"="Thirty Seconds";
"interval.fortyfive.seconds"="Forty Five Seconds";
"interval.one.minute"="One Minute";
"interval.two.minutes"="Two Minutes";
"interval.five.minutes"="Five Minutes";

View file

@ -113,6 +113,7 @@
"interval.twenty.seconds"="二十秒";
"interval.twentyfive.seconds"="二十五秒";
"interval.thirty.seconds"="三十秒";
"interval.fortyfive.seconds"="Forty Five Seconds";
"interval.one.minute"="一分钟";
"interval.two.minutes"="两分钟";
"interval.five.minutes"="五分钟";