diff --git a/Localizable.xcstrings b/Localizable.xcstrings index a5e01356..6fdfdd7f 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -132,28 +132,6 @@ } } }, - "%@ - 1 Hop" : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ - 1 Скок" - } - } - } - }, - "%@ - Direct" : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ - Директно" - } - } - } - }, "%@ - No Response" : { "localizations" : { "de" : { @@ -296,17 +274,6 @@ } } }, - "%@ hPa" : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ hPa" - } - } - } - }, "%@, %@" : { "localizations" : { "en" : { @@ -575,6 +542,23 @@ } } }, + "2.4ghz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "2.4 GHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "2.4 GHz" + } + } + } + }, "7" : { "localizations" : { "sr" : { @@ -2231,6 +2215,23 @@ } } }, + "australia.new.zealand" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Australia / New Zealand" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Аустралија / Нови Зеланд" + } + } + } + }, "automatic.detection" : { "extractionState" : "migrated", "localizations" : { @@ -2390,17 +2391,6 @@ } } }, - "Bad" : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Лош" - } - } - } - }, "Bandwidth" : { "localizations" : { "de" : { @@ -4521,14 +4511,38 @@ } }, "Chart" : { - + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Графукон" + } + } + } }, "CHG" : { "localizations" : { "sr" : { "stringUnit" : { "state" : "translated", - "value" : "CHG" + "value" : "ПУЊ" + } + } + } + }, + "china" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "China" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кина" } } } @@ -4876,7 +4890,14 @@ } }, "Config" : { - + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Конфигурација" + } + } + } }, "config.module.paxcounter.enabled.description" : { "localizations" : { @@ -6811,71 +6832,6 @@ } } }, - "current" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aktuell" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Current" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actuel" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "נוכחי" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bieżący" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atual" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aktuell" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Тренутни" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "当前" - } - }, - "zh-Hant-TW" : { - "stringUnit" : { - "state" : "translated", - "value" : "目前" - } - } - } - }, "Current Firmware Version: %@" : { "localizations" : { "de" : { @@ -6930,17 +6886,6 @@ } } }, - "Currently the reccomended way to update ESP32 devices is using the web flasher on a desktop computer from a chrome based browser. It does not work on mobile devices or over BLE." : { - "extractionState" : "stale", - "localizations" : { - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Тренутно препоручени начин за ажурирање ЕСП32 уређаја је коришћење веб флешера на десктоп рачунару из прегледача заснованог на хрому. Не ради на мобилним уређајима или преко BLE-а." - } - } - } - }, "Currently the recommended way to update ESP32 devices is using the web flasher on a desktop computer from a chrome based browser. It does not work on mobile devices or over BLE." : { "localizations" : { "sr" : { @@ -8224,6 +8169,17 @@ } } }, + "device.role.name.routerlate" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Router Late" + } + } + } + }, "device.role.name.sensor" : { "extractionState" : "manual", "localizations" : { @@ -8487,6 +8443,17 @@ } } }, + "device.role.routerlate" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Infrastructure node that always rebroadcasts packets once but only after all other modes, ensuring additional coverage for local clusters. Visible in Nodes list." + } + } + } + }, "device.role.sensor" : { "extractionState" : "migrated", "localizations" : { @@ -9831,6 +9798,40 @@ } } }, + "european.union.433mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "European Union 433MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Европска унија 433MHz" + } + } + } + }, + "european.union.868mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "European Union 868MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Европска унија 868MHz" + } + } + } + }, "Exchange Positions" : { "localizations" : { "sr" : { @@ -12336,6 +12337,23 @@ } } }, + "india" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "India" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Индија" + } + } + } + }, "Indoor Air Quality" : { "localizations" : { "sr" : { @@ -14787,6 +14805,23 @@ } } }, + "japan" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Japan" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Јапан" + } + } + } + }, "JSON Enabled" : { "localizations" : { "sr" : { @@ -14812,7 +14847,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Taste" + "value" : "Schlüssel" } }, "sr" : { @@ -14838,7 +14873,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tastengröße" + "value" : "Schlüsselgröße" } }, "sr" : { @@ -14914,6 +14949,23 @@ } } }, + "korea" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Korea" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кореја" + } + } + } + }, "Last heard" : { "localizations" : { "de" : { @@ -15550,6 +15602,57 @@ } } }, + "long.range.fast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Long Range - Fast" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дугачки домет - Брзо" + } + } + } + }, + "long.range.moderate" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Long Range - Moderate" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дугачки домет - Умерено" + } + } + } + }, + "long.range.slow" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Long Range - Slow" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дугачки домет - Споро" + } + } + } + }, "Longitude" : { "localizations" : { "de" : { @@ -15772,6 +15875,40 @@ } } }, + "malaysia.433mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Malaysia 433MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Малезија 433MHz" + } + } + } + }, + "malaysia.919mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Malaysia 919MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Малезија 919MHz" + } + } + } + }, "Managed Device" : { "localizations" : { "sr" : { @@ -15951,65 +16088,6 @@ } } }, - "map.recentering" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Automatic Re-centering" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recentrage automatique" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מרכז מפה אוטומטית" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Automatyczne Wyśrodkowywanie" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Re-centralização Automática" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Automatisk Centrering" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Аутоматско поновно центрирање" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "自动重新居中" - } - }, - "zh-Hant-TW" : { - "stringUnit" : { - "state" : "translated", - "value" : "自動重新居中" - } - } - } - }, "map.tiles.delete" : { "extractionState" : "migrated", "localizations" : { @@ -16459,6 +16537,40 @@ } } }, + "medium.range.fast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medium Range - Fast" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Средњи домет - Брзо" + } + } + } + }, + "medium.range.slow" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medium Range - Slow" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Средњи домет - Споро" + } + } + } + }, "Mesh activity update" : { "localizations" : { "sr" : { @@ -19358,7 +19470,14 @@ } }, "Metric" : { - + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Метрика" + } + } + } }, "Minimum Distance" : { "localizations" : { @@ -20258,6 +20377,23 @@ } } }, + "new.zealand.865mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New Zealand 865MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нови зеланд 865MHz" + } + } + } + }, "Newer firmware is available" : { "localizations" : { "de" : { @@ -21734,6 +21870,57 @@ } } }, + "philippines.433mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Philippines 433MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Филипини 433MHz" + } + } + } + }, + "philippines.868mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Philippines 868MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Филипини 868MHz" + } + } + } + }, + "philippines.915mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Philippines 915MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Филипини 915MHz" + } + } + } + }, "phone.gps" : { "localizations" : { "de" : { @@ -21919,6 +22106,23 @@ } } }, + "please.set.a.region" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Please set a region" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Молимо изаберите регион" + } + } + } + }, "Points of Interest" : { "localizations" : { "sr" : { @@ -25127,6 +25331,23 @@ } } }, + "russia" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Russia" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Русија" + } + } + } + }, "RX Boosted Gain" : { "localizations" : { "sr" : { @@ -25695,71 +25916,6 @@ } } }, - "select.menu.item" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wähle einen Menüeintrag aus" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Select an item from the menu" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sélectioner un item du menu" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "בחר מהתפריט" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wybierz element z menu" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seleciona um opção do menu" - } - }, - "se" : { - "stringUnit" : { - "state" : "translated", - "value" : "Välj ett alternativ från menyn" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Изабери ставку из менија" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "从菜单选择一个选项" - } - }, - "zh-Hant-TW" : { - "stringUnit" : { - "state" : "translated", - "value" : "從菜單選擇項目" - } - } - } - }, "select.node" : { "localizations" : { "de" : { @@ -26516,7 +26672,14 @@ } }, "Series" : { - + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Серије" + } + } + } }, "Server" : { "localizations" : { @@ -26901,6 +27064,57 @@ } } }, + "short.range.fast" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Short Range - Fast" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кратки домет - Брзо" + } + } + } + }, + "short.range.slow" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Short Range - Slow" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кратки домет - Споро" + } + } + } + }, + "short.range.turbo" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Short Range - Turbo" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кратки домет - Турбо" + } + } + } + }, "Show alerts" : { "localizations" : { "de" : { @@ -27055,6 +27269,23 @@ } } }, + "singapore.923mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Singapore 923MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сингапур 923MHz" + } + } + } + }, "Smart Position" : { "localizations" : { "sr" : { @@ -27734,7 +27965,31 @@ } }, "Table" : { - + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Табела" + } + } + } + }, + "taiwan" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taiwan" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тајван" + } + } + } }, "tapback" : { "localizations" : { @@ -28625,6 +28880,23 @@ } } }, + "thailand" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thailand" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тајланд" + } + } + } + }, "The amount of time to wait before we consider your packet as done." : { "localizations" : { "sr" : { @@ -29833,23 +30105,6 @@ } } }, - "Trace route received directly by %@ with a SNR of %@ dB" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Trace route received directly by %1$@ with a SNR of %2$@ dB" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Захтев за праћење руте комуникације директно примљен од %1$@ са SNR од %2$@ dB." - } - } - } - }, "Trace Route Sent" : { "localizations" : { "sr" : { @@ -30047,6 +30302,40 @@ } } }, + "ukraine.433mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukraine 433MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Украјина 433MHz" + } + } + } + }, + "ukraine.868mhz" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukraine 868MHz" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Украјина 868MHz" + } + } + } + }, "Un-Favorite" : { "localizations" : { "sr" : { @@ -30057,6 +30346,23 @@ } } }, + "united.states" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "United States" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сједињене Америчке државе" + } + } + } + }, "Units displayed on the device screen" : { "localizations" : { "sr" : { @@ -30797,6 +31103,23 @@ } } }, + "very.long.range.slow" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Very Long Range - Slow" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Веома дугачки домет - Споро" + } + } + } + }, "Via Lora" : { "localizations" : { "de" : { @@ -31333,4 +31656,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 8877bfa9..238ed7d6 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -429,6 +429,7 @@ DD9A1A912BA2D2D3001E602E /* MeshtasticDataModelV 30.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 30.xcdatamodel"; sourceTree = ""; }; DDA0B6B1294CDC55001356EC /* Channels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channels.swift; sourceTree = ""; }; DDA1C48D28DB49D3009933EC /* ChannelRoles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelRoles.swift; sourceTree = ""; }; + DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 48.xcdatamodel"; sourceTree = ""; }; DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshPackets.swift; sourceTree = ""; }; DDA951592BC6624100CEA535 /* TelemetryWeather.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelemetryWeather.swift; sourceTree = ""; }; DDA9515B2BC6631200CEA535 /* TelemetryEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryEnums.swift; sourceTree = ""; }; @@ -1754,7 +1755,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.5.14; + MARKETING_VERSION = 2.5.17; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1788,7 +1789,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.5.14; + MARKETING_VERSION = 2.5.17; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1820,7 +1821,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.5.14; + MARKETING_VERSION = 2.5.17; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1853,7 +1854,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.5.14; + MARKETING_VERSION = 2.5.17; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1965,6 +1966,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */, DDDFE7402D0D4A070044463C /* MeshtasticDataModelV 47.xcdatamodel */, DD0BE30C2CB785D8000BA445 /* MeshtasticDataModelV 46.xcdatamodel */, DD6D5A342CA13BA600ED3032 /* MeshtasticDataModelV 45.xcdatamodel */, @@ -2013,7 +2015,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DDDFE7402D0D4A070044463C /* MeshtasticDataModelV 47.xcdatamodel */; + currentVersion = DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme b/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme index 19c6089f..e5a73d1e 100644 --- a/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme +++ b/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme @@ -77,6 +77,11 @@ value = "oslogToStdio" isEnabled = "YES"> + + 0 && routingMessage.snrBack.count > 0 { + traceRoute?.hopsBack = Int32(routingMessage.routeBack.count) var routeBackString = "\(traceRoute?.node?.user?.longName ?? "unknown".localized) \((traceRoute?.node?.num ?? 0).toHex()) --> " for (index, node) in routingMessage.routeBack.enumerated() { var hopNode = getNodeInfo(id: Int64(node), context: context) @@ -947,19 +957,19 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate let snrBackLast = Float(routingMessage.snrBack.last ?? -128) / 4 routeBackString += "\(connectedNode.user?.longName ?? String(connectedNode.num.toHex())) (\(snrBackLast != -32 ? String(snrBackLast) : "unknown ".localized)dB)" traceRoute?.routeBackText = routeBackString - traceRoute?.hops = NSOrderedSet(array: hopNodes) - traceRoute?.time = Date() - do { - try context.save() - Logger.data.info("💾 Saved Trace Route") - } catch { - context.rollback() - let nsError = error as NSError - Logger.data.error("Error Updating Core Data TraceRouteHop: \(nsError, privacy: .public)") - } - let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString) - MeshLogger.log("🪧 \(logString)") } + traceRoute?.hops = NSOrderedSet(array: hopNodes) + traceRoute?.time = Date() + do { + try context.save() + Logger.data.info("💾 Saved Trace Route") + } catch { + context.rollback() + let nsError = error as NSError + Logger.data.error("Error Updating Core Data TraceRouteHop: \(nsError, privacy: .public)") + } + let logString = String.localizedStringWithFormat("mesh.log.traceroute.received.route %@".localized, routeString) + MeshLogger.log("🪧 \(logString)") } case .neighborinfoApp: if let neighborInfo = try? NeighborInfo(serializedBytes: decodedInfo.packet.decoded.payload) { @@ -978,8 +988,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate MeshLogger.log("🕸️ MESH PACKET received for ATAK Plugin App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") case .powerstressApp: MeshLogger.log("🕸️ MESH PACKET received for Power Stress App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") - case .alertApp: - MeshLogger.log("🕸️ MESH PACKET received for Alert App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure")") } if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce { diff --git a/Meshtastic/Helpers/LocalNotificationManager.swift b/Meshtastic/Helpers/LocalNotificationManager.swift index 7fe17865..3cccaa9d 100644 --- a/Meshtastic/Helpers/LocalNotificationManager.swift +++ b/Meshtastic/Helpers/LocalNotificationManager.swift @@ -69,6 +69,9 @@ class LocalNotificationManager { if notification.userNum != nil { content.userInfo["userNum"] = notification.userNum } + if notification.critical { + content.sound = UNNotificationSound.defaultCritical + } let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: nil) @@ -103,4 +106,5 @@ struct Notification { var messageId: Int64? var channel: Int32? var userNum: Int64? + var critical: Bool = false } diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 157c6296..6d13a2d8 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -111,6 +111,7 @@ func myInfoPacket (myInfo: MyNodeInfo, peripheralId: String, context: NSManagedO myInfoEntity.peripheralId = peripheralId myInfoEntity.myNodeNum = Int64(myInfo.myNodeNum) myInfoEntity.rebootCount = Int32(myInfo.rebootCount) + myInfoEntity.deviceId = myInfo.deviceID do { try context.save() Logger.data.info("💾 Saved a new myInfo for node: \(myInfo.myNodeNum.toHex(), privacy: .public)") @@ -820,6 +821,7 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage func textMessageAppPacket( packet: MeshPacket, wantRangeTestPackets: Bool, + critical: Bool = false, connectedNode: Int64, storeForward: Bool = false, context: NSManagedObjectContext, @@ -952,7 +954,8 @@ func textMessageAppPacket( path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.messageId)", messageId: newMessage.messageId, channel: newMessage.channel, - userNum: Int64(packet.from) + userNum: Int64(packet.from), + critical: critical ) ] manager.schedule() @@ -960,59 +963,41 @@ func textMessageAppPacket( } } else if newMessage.toUser == nil { let fetchMyInfoRequest = MyInfoEntity.fetchRequest() - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode)) +fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(connectedNode)) - do { - let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) - if !fetchedMyInfo.isEmpty { - appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages +do { + let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) + if !fetchedMyInfo.isEmpty { + appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages - for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { - if channel.index == newMessage.channel { - context.refresh(channel, mergeChanges: true) - } - if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications { - // Create an iOS Notification for the received private channel message and schedule it immediately - let manager = LocalNotificationManager() - - if newMessage.fromUser != nil { - manager.notifications = [ - Notification( - id: ("notification.id.\(newMessage.messageId)"), - title: "\(newMessage.fromUser?.longName ?? "unknown".localized)", - subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")", - content: messageText!, - target: "messages", - path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)", - messageId: newMessage.messageId, - channel: newMessage.channel, - userNum: Int64(newMessage.fromUser?.userId ?? "0") - ) - ] - } else { - manager.notifications = [ - Notification( - id: ("notification.id.\(newMessage.messageId)"), - title: "unknown".localized, - content: messageText!, - target: "messages", - path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)", - messageId: newMessage.messageId, - channel: newMessage.channel, - userNum: 0 - ) - ] - } - - manager.schedule() - Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)") - } - } - } - } catch { - // Handle error - } - } + for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { + if channel.index == newMessage.channel { + context.refresh(channel, mergeChanges: true) + } + if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications { + // Create an iOS Notification for the received private channel message and schedule it immediately + let manager = LocalNotificationManager() + manager.notifications = [ + Notification( + id: ("notification.id.\(newMessage.messageId)"), + title: "\(newMessage.fromUser?.longName ?? \"unknown\".localized)", + subtitle: "AKA \(newMessage.fromUser?.shortName ?? \"?\")", + content: messageText!, + target: "messages", + path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.messageId)", + messageId: newMessage.messageId, + channel: newMessage.channel, + userNum: Int64(newMessage.fromUser?.userId ?? "0"), + critical: critical) + ] + manager.schedule() + Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? \"unknown\".localized)") + } + } + } +} catch { + // Handle error +} } } catch { context.rollback() diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 77cd1088..98860495 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -26,7 +26,7 @@ class MqttClientProxyManager { var debugLog = false func connectFromConfigSettings(node: NodeInfoEntity) { let defaultServerAddress = "mqtt.meshtastic.org" - let useSsl = node.mqttConfig?.tlsEnabled == true + var useSsl = node.mqttConfig?.tlsEnabled == true var defaultServerPort = useSsl ? 8883 : 1883 var host = node.mqttConfig?.address if host == nil || host!.isEmpty { @@ -43,8 +43,14 @@ class MqttClientProxyManager { if let host = host { let port = defaultServerPort - let username = node.mqttConfig?.username - let password = node.mqttConfig?.password + var username = node.mqttConfig?.username + var password = node.mqttConfig?.password + if host == defaultServerAddress { + + // username = ProcessInfo.processInfo.environment["publicMqttUsername"] + // password = ProcessInfo.processInfo.environment["publicMqttPsk"] + useSsl = false + } let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" let prefix = root! topic = prefix + (supportedVersion ? "/2/e" : "/2/c") + "/#" diff --git a/Meshtastic/Meshtastic.entitlements b/Meshtastic/Meshtastic.entitlements index 241de35a..abcef61d 100644 --- a/Meshtastic/Meshtastic.entitlements +++ b/Meshtastic/Meshtastic.entitlements @@ -2,6 +2,8 @@ + com.apple.developer.usernotifications.critical-alerts + com.apple.developer.associated-domains applinks:meshtastic.org/e/* diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 3581f63f..a702965e 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV 47.xcdatamodel + MeshtasticDataModelV 48.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents new file mode 100644 index 00000000..709d5943 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift b/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift index 0476b6b8..ccb0b758 100644 --- a/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift +++ b/Meshtastic/Model/Metrics Visualization/MetricsColumnList.swift @@ -79,18 +79,18 @@ class MetricsColumnList: ObservableObject, RandomAccessCollection, RangeReplacea columns.append(newElement) objectWillChange.send() } - + func remove(at index: Int) -> Element { objectWillChange.send() let removedElement = columns.remove(at: index) return removedElement } - + func removeAll() { objectWillChange.send() columns.removeAll() } - + func insert(_ newElement: Element, at index: Int) { objectWillChange.send() columns.insert(newElement, at: index) diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index edc21605..15c0d89b 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -833,7 +833,6 @@ func upsertSecurityConfigPacket(config: Config.SecurityConfig, nodeNum: Int64, s fetchedNode[0].securityConfig?.adminKey = config.adminKey[0] if config.adminKey.count > 1 { fetchedNode[0].securityConfig?.adminKey2 = config.adminKey[1] - } else if config.adminKey.count > 2 { fetchedNode[0].securityConfig?.adminKey3 = config.adminKey[2] } } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index a424cbf0..ed9728c6 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -30,7 +30,7 @@ struct Connect: View { let notificationCenter = UNUserNotificationCenter.current() notificationCenter.getNotificationSettings(completionHandler: { (settings) in if settings.authorizationStatus == .notDetermined { - UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound, .criticalAlert]) { success, error in if success { Logger.services.info("Notifications are all set!") } else if let error = error { diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 6f031018..3bf88f7a 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -93,12 +93,14 @@ struct NodeList: View { ) /// Don't show message, trace route, position exchange or delete context menu items for the connected node if connectedNode.num != node.num { - Button(action: { - if let url = URL(string: "meshtastic:///messages?userNum=\(node.num)") { - UIApplication.shared.open(url) + if (!node.viaMqtt || node.viaMqtt && node.hopsAway == 0) { + Button(action: { + if let url = URL(string: "meshtastic:///messages?userNum=\(node.num)") { + UIApplication.shared.open(url) + } + }) { + Label("Message", systemImage: "message") } - }) { - Label("Message", systemImage: "message") } Button { let traceRouteSent = bleManager.sendTraceRouteRequest( diff --git a/Meshtastic/Views/Nodes/TraceRouteLog.swift b/Meshtastic/Views/Nodes/TraceRouteLog.swift index 1bdccf46..f10dad58 100644 --- a/Meshtastic/Views/Nodes/TraceRouteLog.swift +++ b/Meshtastic/Views/Nodes/TraceRouteLog.swift @@ -44,7 +44,7 @@ struct TraceRouteLog: View { .font(.caption) } else if route.response { let hopTowardsString = String(localized: "\(route.hopsTowards) Hops") - let hopBackString = String(localized: "\(route.hopsBack) Hops") + let hopBackString = route.hopsBack >= 0 ? String(localized: "\(route.hopsBack) Hops") : String(localized: "unknown") Text("\(routeTime) - \(hopTowardsString) Towards \(hopBackString) Back") .font(.caption) } else if route.sent { diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 51461cf0..81cfa1ba 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -168,7 +168,7 @@ struct LoRaConfig: View { .focused($focusedField, equals: .channelNum) .disabled(overrideFrequency > 0.0) } - Text("This determines the actual frequency you are transmitting on in the band. If set to 0 this value will be calculated automatically based on the primary channel name.") + Text("Your node’s operating frequency is calculated based on the region, modem preset, and this field. When 0, the slot is automatically calculated based on the primary channel name.") .foregroundColor(.gray) .font(.callout) } diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index fc93e6f9..f616f8bf 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -174,51 +174,52 @@ struct MQTTConfig: View { .keyboardType(.default) } .autocorrectionDisabled() - - HStack { - Label("mqtt.username", systemImage: "person.text.rectangle") - TextField("mqtt.username", text: $username) - .foregroundColor(.gray) - .autocapitalization(.none) - .disableAutocorrection(true) - .onChange(of: username) { - var totalBytes = username.utf8.count - // Only mess with the value if it is too big - while totalBytes > 62 { - username = String(username.dropLast()) - totalBytes = username.utf8.count + if address != "mqtt.meshtastic.org" { + HStack { + Label("mqtt.username", systemImage: "person.text.rectangle") + TextField("mqtt.username", text: $username) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: username) { + var totalBytes = username.utf8.count + // Only mess with the value if it is too big + while totalBytes > 62 { + username = String(username.dropLast()) + totalBytes = username.utf8.count + } + hasChanges = true } - hasChanges = true - } - .foregroundColor(.gray) - } - .keyboardType(.default) - .scrollDismissesKeyboard(.interactively) - HStack { - Label("password", systemImage: "wallet.pass") - TextField("password", text: $password) - .foregroundColor(.gray) - .autocapitalization(.none) - .disableAutocorrection(true) - .onChange(of: password) { - var totalBytes = password.utf8.count - // Only mess with the value if it is too big - while totalBytes > 62 { - password = String(password.dropLast()) - totalBytes = password.utf8.count + .foregroundColor(.gray) + } + .keyboardType(.default) + .scrollDismissesKeyboard(.interactively) + HStack { + Label("password", systemImage: "wallet.pass") + TextField("password", text: $password) + .foregroundColor(.gray) + .autocapitalization(.none) + .disableAutocorrection(true) + .onChange(of: password) { + var totalBytes = password.utf8.count + // Only mess with the value if it is too big + while totalBytes > 62 { + password = String(password.dropLast()) + totalBytes = password.utf8.count + } + hasChanges = true } - hasChanges = true - } - .foregroundColor(.gray) + .foregroundColor(.gray) + } + .keyboardType(.default) + .scrollDismissesKeyboard(.interactively) + .listRowSeparator(/*@START_MENU_TOKEN@*/.visible/*@END_MENU_TOKEN@*/) + Toggle(isOn: $tlsEnabled) { + Label("TLS Enabled", systemImage: "checkmark.shield.fill") + Text("Your MQTT Server must support TLS.") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } - .keyboardType(.default) - .scrollDismissesKeyboard(.interactively) - .listRowSeparator(/*@START_MENU_TOKEN@*/.visible/*@END_MENU_TOKEN@*/) - Toggle(isOn: $tlsEnabled) { - Label("TLS Enabled", systemImage: "checkmark.shield.fill") - Text("Your MQTT Server must support TLS. Not available via the public mqtt server.") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } Text("For all Mqtt functionality other than the map report you must also set uplink and downlink for each channel you want to bridge over Mqtt.") .font(.callout) diff --git a/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift index c8c90be7..566b3ef1 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/config.pb.swift @@ -263,6 +263,14 @@ public struct Config: Sendable { /// and automatic TAK PLI (position location information) broadcasts. /// Uses position module configuration to determine TAK PLI broadcast interval. case takTracker // = 10 + + /// + /// Description: Will always rebroadcast packets, but will do so after all other modes. + /// Technical Details: Used for router nodes that are intended to provide additional coverage + /// in areas not already covered by other routers, or to bridge around problematic terrain, + /// but should not be given priority over other routers in order to avoid unnecessaraily + /// consuming hops. + case routerLate // = 11 case UNRECOGNIZED(Int) public init() { @@ -282,6 +290,7 @@ public struct Config: Sendable { case 8: self = .clientHidden case 9: self = .lostAndFound case 10: self = .takTracker + case 11: self = .routerLate default: self = .UNRECOGNIZED(rawValue) } } @@ -299,6 +308,7 @@ public struct Config: Sendable { case .clientHidden: return 8 case .lostAndFound: return 9 case .takTracker: return 10 + case .routerLate: return 11 case .UNRECOGNIZED(let i): return i } } @@ -316,6 +326,7 @@ public struct Config: Sendable { .clientHidden, .lostAndFound, .takTracker, + .routerLate, ] } @@ -741,6 +752,10 @@ public struct Config: Sendable { /// rsyslog Server and Port public var rsyslogServer: String = String() + /// + /// Flags for enabling/disabling network protocols + public var enabledProtocols: UInt32 = 0 + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum AddressMode: SwiftProtobuf.Enum, Swift.CaseIterable { @@ -783,6 +798,48 @@ public struct Config: Sendable { } + /// + /// Available flags auxiliary network protocols + public enum ProtocolFlags: SwiftProtobuf.Enum, Swift.CaseIterable { + public typealias RawValue = Int + + /// + /// Do not broadcast packets over any network protocol + case noBroadcast // = 0 + + /// + /// Enable broadcasting packets via UDP over the local network + case udpBroadcast // = 1 + case UNRECOGNIZED(Int) + + public init() { + self = .noBroadcast + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .noBroadcast + case 1: self = .udpBroadcast + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .noBroadcast: return 0 + case .udpBroadcast: return 1 + case .UNRECOGNIZED(let i): return i + } + } + + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [ + .noBroadcast, + .udpBroadcast, + ] + + } + public struct IpV4Config: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -2081,6 +2138,7 @@ extension Config.DeviceConfig.Role: SwiftProtobuf._ProtoNameProviding { 8: .same(proto: "CLIENT_HIDDEN"), 9: .same(proto: "LOST_AND_FOUND"), 10: .same(proto: "TAK_TRACKER"), + 11: .same(proto: "ROUTER_LATE"), ] } @@ -2314,6 +2372,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp 7: .standard(proto: "address_mode"), 8: .standard(proto: "ipv4_config"), 9: .standard(proto: "rsyslog_server"), + 10: .standard(proto: "enabled_protocols"), ] public mutating func decodeMessage(decoder: inout D) throws { @@ -2330,6 +2389,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp case 7: try { try decoder.decodeSingularEnumField(value: &self.addressMode) }() case 8: try { try decoder.decodeSingularMessageField(value: &self._ipv4Config) }() case 9: try { try decoder.decodeSingularStringField(value: &self.rsyslogServer) }() + case 10: try { try decoder.decodeSingularUInt32Field(value: &self.enabledProtocols) }() default: break } } @@ -2364,6 +2424,9 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp if !self.rsyslogServer.isEmpty { try visitor.visitSingularStringField(value: self.rsyslogServer, fieldNumber: 9) } + if self.enabledProtocols != 0 { + try visitor.visitSingularUInt32Field(value: self.enabledProtocols, fieldNumber: 10) + } try unknownFields.traverse(visitor: &visitor) } @@ -2376,6 +2439,7 @@ extension Config.NetworkConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp if lhs.addressMode != rhs.addressMode {return false} if lhs._ipv4Config != rhs._ipv4Config {return false} if lhs.rsyslogServer != rhs.rsyslogServer {return false} + if lhs.enabledProtocols != rhs.enabledProtocols {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -2388,6 +2452,13 @@ extension Config.NetworkConfig.AddressMode: SwiftProtobuf._ProtoNameProviding { ] } +extension Config.NetworkConfig.ProtocolFlags: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 0: .same(proto: "NO_BROADCAST"), + 1: .same(proto: "UDP_BROADCAST"), + ] +} + extension Config.NetworkConfig.IpV4Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = Config.NetworkConfig.protoMessageName + ".IpV4Config" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ diff --git a/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift index 82c6e834..eaf3951c 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/device_ui.pb.swift @@ -133,6 +133,10 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable { /// Norwegian case norwegian // = 14 + /// + /// Slovenian + case slovenian // = 15 + /// /// Simplified Chinese (experimental) case simplifiedChinese // = 30 @@ -163,6 +167,7 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable { case 12: self = .dutch case 13: self = .greek case 14: self = .norwegian + case 15: self = .slovenian case 30: self = .simplifiedChinese case 31: self = .traditionalChinese default: self = .UNRECOGNIZED(rawValue) @@ -186,6 +191,7 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable { case .dutch: return 12 case .greek: return 13 case .norwegian: return 14 + case .slovenian: return 15 case .simplifiedChinese: return 30 case .traditionalChinese: return 31 case .UNRECOGNIZED(let i): return i @@ -209,6 +215,7 @@ public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable { .dutch, .greek, .norwegian, + .slovenian, .simplifiedChinese, .traditionalChinese, ] @@ -354,6 +361,10 @@ public struct NodeFilter: Sendable { /// Filter nodes by matching name string public var nodeName: String = String() + /// + /// Filter based on channel + public var channel: Int32 = 0 + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -418,6 +429,7 @@ extension Language: SwiftProtobuf._ProtoNameProviding { 12: .same(proto: "DUTCH"), 13: .same(proto: "GREEK"), 14: .same(proto: "NORWEGIAN"), + 15: .same(proto: "SLOVENIAN"), 30: .same(proto: "SIMPLIFIED_CHINESE"), 31: .same(proto: "TRADITIONAL_CHINESE"), ] @@ -612,6 +624,7 @@ extension NodeFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio 4: .standard(proto: "hops_away"), 5: .standard(proto: "position_switch"), 6: .standard(proto: "node_name"), + 7: .same(proto: "channel"), ] public mutating func decodeMessage(decoder: inout D) throws { @@ -626,6 +639,7 @@ extension NodeFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio case 4: try { try decoder.decodeSingularInt32Field(value: &self.hopsAway) }() case 5: try { try decoder.decodeSingularBoolField(value: &self.positionSwitch) }() case 6: try { try decoder.decodeSingularStringField(value: &self.nodeName) }() + case 7: try { try decoder.decodeSingularInt32Field(value: &self.channel) }() default: break } } @@ -650,6 +664,9 @@ extension NodeFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if !self.nodeName.isEmpty { try visitor.visitSingularStringField(value: self.nodeName, fieldNumber: 6) } + if self.channel != 0 { + try visitor.visitSingularInt32Field(value: self.channel, fieldNumber: 7) + } try unknownFields.traverse(visitor: &visitor) } @@ -660,6 +677,7 @@ extension NodeFilter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if lhs.hopsAway != rhs.hopsAway {return false} if lhs.positionSwitch != rhs.positionSwitch {return false} if lhs.nodeName != rhs.nodeName {return false} + if lhs.channel != rhs.channel {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift b/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift index 72ba0edc..cf3cb4ee 100644 --- a/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift +++ b/MeshtasticProtobufs/Sources/meshtastic/mesh.pb.swift @@ -2024,6 +2024,15 @@ public struct MeshPacket: @unchecked Sendable { set {_uniqueStorage()._relayNode = newValue} } + /// + /// *Never* sent over the radio links. + /// Timestamp after which this packet may be sent. + /// Set by the firmware internally, clients are not supposed to set this. + public var txAfter: UInt32 { + get {return _storage._txAfter} + set {_uniqueStorage()._txAfter = newValue} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable { @@ -4111,6 +4120,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio 17: .standard(proto: "pki_encrypted"), 18: .standard(proto: "next_hop"), 19: .standard(proto: "relay_node"), + 20: .standard(proto: "tx_after"), ] fileprivate class _StorageClass { @@ -4132,6 +4142,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio var _pkiEncrypted: Bool = false var _nextHop: UInt32 = 0 var _relayNode: UInt32 = 0 + var _txAfter: UInt32 = 0 #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. @@ -4164,6 +4175,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio _pkiEncrypted = source._pkiEncrypted _nextHop = source._nextHop _relayNode = source._relayNode + _txAfter = source._txAfter } } @@ -4220,6 +4232,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio case 17: try { try decoder.decodeSingularBoolField(value: &_storage._pkiEncrypted) }() case 18: try { try decoder.decodeSingularUInt32Field(value: &_storage._nextHop) }() case 19: try { try decoder.decodeSingularUInt32Field(value: &_storage._relayNode) }() + case 20: try { try decoder.decodeSingularUInt32Field(value: &_storage._txAfter) }() default: break } } @@ -4294,6 +4307,9 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if _storage._relayNode != 0 { try visitor.visitSingularUInt32Field(value: _storage._relayNode, fieldNumber: 19) } + if _storage._txAfter != 0 { + try visitor.visitSingularUInt32Field(value: _storage._txAfter, fieldNumber: 20) + } } try unknownFields.traverse(visitor: &visitor) } @@ -4321,6 +4337,7 @@ extension MeshPacket: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio if _storage._pkiEncrypted != rhs_storage._pkiEncrypted {return false} if _storage._nextHop != rhs_storage._nextHop {return false} if _storage._relayNode != rhs_storage._relayNode {return false} + if _storage._txAfter != rhs_storage._txAfter {return false} return true } if !storagesAreEqual {return false} diff --git a/protobufs b/protobufs index 2cffaf53..76f806e1 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 2cffaf53e3faf1b6e41a8b8f05312f2f893be413 +Subproject commit 76f806e1bb1e2a7b157a14fadd095775f63db5e4